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

    +2

    1. 1
    [code]main[]__attribute__((section(".text"))) = {0xC0FFC031,0x7401FF83,0xFFE7F706,0xC3F5EBCF};[/code]

    Печатает факториал, от числа аргументов.
    капча: t4ar

    Запостил: 3.14159265, 18 Июля 2020

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

    • $ gcc -w -o hex ./bomb.c  && ./hex 
      ➤ 1 
      $ gcc -w -o hex ./bomb.c  && ./hex 1
      ➤ 2 
      $ gcc -w -o hex ./bomb.c  && ./hex 1 1
      ➤ 6 
      $ gcc -w -o hex ./bomb.c  && ./hex 1 1 1
      ➤ 24
      $ gcc -w -o hex ./bomb.c  && ./hex 1 1 1 1
      ➤ 120
      $ gcc -w -o hex ./bomb.c  && ./hex 1 1 1 1
      Ответить
    • Какой скилл )))
      Ответить
    • 1 00000000 31C0                    xor eax,eax
      2 00000002 FFC0                    inc eax
      3                                  loop:
      4 00000004 83FF01                  cmp    edi,1
      5 00000007 7406                    je     r
      6 00000009 F7E7                    mul   edi
      7 0000000B FFCF                    dec    edi
      8 0000000D EBF5                    jmp    loop
      9 0000000F C3                      r: retq

      Кстати компиляторы — говно.
      Пишу gcc -Os /clang -Os. Подразумевается компактный код.

      Они генерят
      B8 01 00 00 00              mov eax,1
      ; можно
      31 c0                   xor eax,eax
      ff c0                   inc eax
      Или умножение
      0F AF C7                  imul  eax,edi
      ;хотя можно
      F7 E7                    mul   edi
      Ответить
      • Ну кстати imul они юзают скорее всего чтобы регистр сэкономить. mul edi выдает 64 битный результат, а imul eax, edi - только 32, но сишке больше и не надо.
        Ответить
    • 32-битный вариант:
      main[]__attribute__((section(".text"))) = {0xC031C189,0xE2E1F740,0xC3FC};

      Листинг:
      2                                  start:
           3 00000000 89C1                    mov ecx,eax
           4 00000002 31C0                    xor eax,eax
           5 00000004 40                      inc eax
           6                                  loopa:
           7 00000005 F7E1                    mul   ecx
           8 00000007 E2FC                    loop loopa
           9 00000009 C3                      ret

      Пояснение: в 32-битном режиме Кол Конвеншин «cdecl». Аргументы пушатся справа налево, поэтому argc лежит в [esp+4] (т. к. в [esp+0] лежит адрес разврата). Однако, CRT перед тем, как занести argc в стек, временно вносит его в eax. Этим можно воспользоваться (правда, можно обломаться в некоторых версиях компилятора).

      Инструкция loop уменьшает ecx и проверяет ecx на ноль.

      А ещё в 32-битном режиме есть однобайтовые инкременты и декременты.
      Ответить
      • >Однако, CRT перед тем, как занести argc в стек, временно вносит его в eax.
        Вот здесь я удивился. Т.к. нет привычных esp.

        Сам хотел написать в x32, т.к. верно подмечено что incи на байт короче.
        И к тому же баш из кода разврата выводит только младшие 8 бит.

        ./hex 1 1 1 1 1
        ➤ 208

        720 & 255 = 208
        Ответить
        • Надёжнее будет mov ecx,[esp+4]. Тогда не придётся дрожать, что с новой библиотекой что-то сломается.

          Написав mov ecx, eax, я положился на то, что код инициализации копирует в стек значение argc в последнюю очередь (argv и указатель на окружение копирует раньше), а в качестве промежуточного регистра использует eax (он не пушит, а использует мув, а поскольку мув из памяти в память нельзя, он использует регистр как временное хранилище).
          Ответить
    • main[]__attribute__((section(".text"))) = {0xC031F989, 0xE1F7C0FF, 0xC3FCE2};

      x64, 11 бат, кто меньше?
      use64;
      
      mov ecx, edi
      xor eax, eax
      inc eax
      
      zaloopa:
      mul ecx
      loop zaloopa
      
      endloopa:
      ret
      Ответить
      • Колдовал с eax.

        Пробовал mov al, 1; cbw; cwde — получается длиннее.
        stc; setc al; cbw; cwde — будет ещё длиннее.

        Ещё вариант div eax — всего два байта для получения единицы в eax. Но если в eax изначально ноль, будет плохо (division by zero). Нужно смотреть код CRT, что там вносится в eax перед call _main.
        Ответить
        • Я сначала хотел сделать проверку на простые числа.

          Чтобы как в прошлом топике N единичек передать и проверить N.
          Ответить
          • Попробовал.
            Ну удивление код получился очень «ровный», двухбайтных.
            Как в «RISC».
            1                                  PRIME:
             2 00000000 89F9                            mov     ecx, edi
             3 00000002 FFC9                            dec     ecx
             4 00000004 FFC9                            dec     ecx ; N-2
             5                                  LOOP:
             6 00000006 89F8                            mov     eax, edi
             7 00000008 89CB                            mov     ebx, ecx
             8 0000000A FFC3                            inc     ebx
             9 0000000C F7FB                            idiv    ebx
            10 0000000E 89D0                            mov     eax, edx  ; остаток
            11 00000010 85D2                            test    edx, edx
            12 00000012 7406                            je      EXIT
            13 00000014 FFC9                            dec     ecx
            14 00000016 E2EE                            loop    LOOP
            15 00000018 FFC0                            inc     eax
            16 0000001A C3                      EXIT:   ret
            Ответить
            • 13 00000014 FFC9 dec ecx
              Лишний багор )))
              Ответить
              • Ошибся. Забыл cdq перед делением и выход для 1 и 2.
                Заодно поменял порядок итерации чтобы начинало проверять с 2, а не N-1.
                global main
                main:
                        mov     ecx, edi
                        dec     ecx
                        jz      NOPRIME
                        dec     ecx
                        jz      PRIME
                LOOP:
                        mov     ebx, edi
                        sub     ebx, ecx ;проверяем младшие делители
                        mov     eax, edi        
                        cdq
                        idiv     ebx
                        xor     eax, eax 
                        test    edx, edx
                        je      NOPRIME
                        loop    LOOP
                PRIME:  inc     eax
                NOPRIME:ret


                main[]__attribute__((section(".text")))={ 0xC9FFF989,0xC9FF1774, 0xFB891174, 0xF889CB29,0x31FBF799 ,0x74D285C0,0xFFEFE204,0xC3C0 };
                Ответить
                • Если забить на 1 и 2 и считать 0 признаком простого числа, то можно уложиться в 22 бата зожатия (x64):
                  main[]__attribute__((section(".text"))) = {0xFEBBF989, 0x89FFFFFF, 0xF1F799F8, 0x0275D285, 0xF3E2C3FF, 0xC393};

                  use64;
                  
                  mov ecx, edi
                  mov ebx, -2
                  
                  loopa:
                  mov eax, edi
                  cdq
                  div ecx
                  test edx, edx
                  jne skip
                  inc ebx
                  skip:
                  loop loopa
                  
                  xchg eax, ebx
                  ret

                  Тестовый полигон:
                  #!/usr/bin/env bash
                  gcc -w -o test ./test.c
                  ./test 1 1
                  echo $?                    # 0
                  ./test 1 1 1
                  echo $?                    # x
                  ./test 1 1 1 1
                  echo $?                    # 0
                  ./test 1 1 1 1 1
                  echo $?                    # x
                  ./test 1 1 1 1 1 1
                  echo $?                    # 0
                  ./test 1 1 1 1 1 1 1
                  echo $?                    # x
                  ./test 1 1 1 1 1 1 1 1
                  echo $?                    # x
                  ./test 1 1 1 1 1 1 1 1 1
                  echo $?                    # x
                  ./test 1 1 1 1 1 1 1 1 1 1
                  echo $?                    # 0
                  Ответить
                  • А зачем тебе cdq если число не знаковое?

                    А тьфу, для деления же. Понаделают говноинструкций с неявными аргументами.
                    Ответить
                  • Годно! Сходу и не понял зачем в ebx -2

                    Проверил эффективность унарного кодирования.
                    Где-то за секунду оно факторизует числа около миллиона.

                    Таким образом код вполне даже практичен.
                    $ ulimit -s 65534
                    $ time ./prime `perl -e 'print("1 " x 625064)'`
                    
                    real	0m1.173s
                    user	0m0.867s
                    sys	0m0.304s

                    Дальше баш упирается в длину списка: bash: ./prime: Argument list too long
                    Ответить
                  • >Если забить на 1 и 2 и считать 0 признаком простого числа
                    Так оно для 1 и 2 тоже правильно работает!
                    ./prime 1
                    ➤ 0
                    ./prime
                    ➤ 255

                    Заодно считает количество нетривиальных делителей числа. Круто.
                    Ответить
                    • >считает количество нетривиальных делителей числа
                      0 - простое
                      1 - квадрат простого
                      2 - произведение простых
                      Нечетное - квадрат числа.
                      Ответить
                  • Убрал ненужный бранчинг.
                    Можно использовать трёхбайтовую sete.
                    global main
                    main:
                            mov ecx, edi
                            mov ebx, -2
                            xor ebx, ebx
                    
                    LOOPA:  mov eax, edi
                            cdq
                            div ecx
                            test edx, edx
                            sete dl
                            add bl,dl
                            loop LOOPA
                    
                            xchg eax, ebx
                    ret

                    А если заменить 5-байтовую загрузку -2 на xor, то код будет считать хорошо известную в теории чисел «функцию делителей».

                    https://en.wikipedia.org/wiki/Divisor_function
                    Ответить
                    • Сриниваса Рамануджан, не имея специального математического образования, получил замечательные результаты в области теории чисел.

                      Какой математик )))
                      Ответить
                    • Красиво.

                      «sete» три бата занимает, поэтому баты по сравнению с примитивным «jne» не экономятся. Багор (((
                      Ответить
                      • Можно попробовать взять bsf вместо test.

                        Код будет короче на джва бата. Но тогда потеряется полезность функции делителей.

                        Впрочем нет. bsf в x64 тоже трёхбатная.
                        Ответить
                      • Шило на мыло.
                        F30FBDD2 LZCNT edx,edx   ;CF flag is set to 1 if input was zero and cleared otherwise
                        10F3           adc bl,dh
                        Ответить
                        • 18 бат
                          global main
                          main:
                                  mov ecx, edi
                                  xor ebx, ebx
                          LOOPA:  mov eax, edi
                                  cdq
                                  div ecx
                                  cmp bh,dl ; bh == 0
                                  adc bl,bh
                                  loop LOOPA
                                  sub eax,ebx
                          ret
                          Ответить
                          • Браво!
                            Ответить
                            • Можно выгадать ещё 1 бат, отнимая от ebx.
                              xor ebx, ebx
                              mov ebx, edi

                              adc bl,bh
                              sbb bl,0

                              И заменив обратно джвухбатный sub eax,ebx на xchg.

                              Но тогда я не знаю где взять 8-битный регистр с нулём.

                              bh работает, т.к. число делителей даже на больших числах меньше 255.
                              И результат всегда влазит в bl.
                              ./prime1 `perl -e 'print("1 " x ((144*5*7*9*11)-1))'`
                              ➤ 201
                              Ответить
                              • Мама, мама, ЫЫЫЫЫ!
                                Что я буду делать? ЫЫЫЫЫ!
                                Ы, ку, Ы, ку, Ы, ку, Ы!
                                Ответить
        • > div eax
          Спасибо за идею! Только для «div eax» надо ещё edx очищать, иначе бо-бо.

          main[]__attribute__((section(".text"))) = {0xF6975957, 0xE2E1F7F0, 0xC3FC};

          x64, 10 бат!

          use64;
          
          push rdi
          pop rcx
          xchg eax, edi
          div al
          
          zaloopa:
          mul ecx
          loop zaloopa
          
          ret
          Ответить
      • main[]__attribute__((section(".text"))) = {0xCFFFF889, 0xE7F70474, 0xC3F8EB};

        x64, 11 бат, другой подход.

        use64; 
        
        mov eax, edi
        
        zaloopa:
        dec edi
        jz endloopa
        mul edi
        jmp zaloopa
        
        endloopa:
        ret
        Ответить
    • Ну и раз уж гольфить…
      main(c){return c?c*main(c-1):1;}
      Ответить

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