Няшная / Говнокод #26811 Ссылка на оригинал

0

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

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

Запостил: 3.14159265 3.14159265, (Updated )

Комментарии (39) 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
          Ответить
            • Ошибся. Забыл 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;}
    Ответить
  • mittorn@localhost ~ $ cat > code.c
    main[]__attribute__((section(".text"))) = {0xC0FFC031,0x7401FF83,0xFFE7F706,0xC3F5EBCF};
    mittorn@localhost ~ $ gcc code.c -o code
    code.c:1:1: warning: data definition has no type or storage class
        1 | main[]__attribute__((section(".text"))) = {0xC0FFC031,0x7401FF83,0xFFE7F706,0xC3F5EBCF};
          | ^~~~
    code.c:1:1: warning: type defaults to ‘int’ in declaration of ‘main’ [-Wimplicit-int]
    /tmp/ccz3SEm2.s: Assembler messages:
    /tmp/ccz3SEm2.s:5: Warning: ignoring changed section attributes for .text
    mittorn@localhost ~ $ ./code
    Illegal instruction
    Ответить

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

Я, guest, находясь в здравом уме и твердой памяти, торжественно заявляю:

    А не использовать ли нам bbcode?


    8