Для холивара.

Апрель 13th, 2009 по SadKo Оставить ответ »

Надо сказать, что в предыдущей заметке про inline-ассемблер было написано не всё, а также допущена масса грубых ошибок, которые были выявлены и исправлены в процессе компиляции исходников при помощи GNU C++.

Об этом я расскажу чуть позже. Сейчас же, после очень большой проделанной работы (кстати, ещё не до конца) по созданию универсального способа разделять один и тот же ассемблерный код между разными компиляторами, позволю себе похоливарить.

Я не доверяю коду, который сгенерировал компилятор, до тех пор, пока не посмотрю его листинг. В Open Watcom листинг объектного файла можно получить при помощи утилиты wdis:

wdis <object_file> -l=<listing_file>

При этом, будет сгенерирован, например, такой вполне читабельный и понятный листинг:

Module: /home/sadko/eclipse/xsystem/xskernel/include/arch/i386/ipc/initial.cpp
GROUP: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT PARA USE32 00000044 bytes
0000    8D 40 00                  lea         eax,[eax]
0003    8B C9                     mov         ecx,ecx
0005    49 6E 69 74 69 61 6C 69 7A 65 0A                Initialize.

Routine Size: 16 bytes,    Routine Base: _TEXT + 0000

0010                          void near initial_thread_t::Initialize( task_tag_t near * ):
0010    53                        push        ebx
0011    51                        push        ecx
0012    89 C3                     mov         ebx,eax
0014    B8 80 00 00 00            mov         eax,0x00000080
0019    E8 00 00 00 00            call        void near * near TTSS::operator new( int unsigned )
001E    89 C2                     mov         edx,eax
0020    89 D8                     mov         eax,ebx
0022    E8 00 00 00 00            call        int unsigned near common_thread_t::AllocSelector( TTSS near * )
0027    50                        push        eax
0028    68 00 00 00 00            push        offset L$2
002D    89 C3                     mov         ebx,eax
002F    E8 00 00 00 00            call        int near k_printf( char const near *, ... )
0034    83 C4 08                  add         esp,0x00000008
0037    89 D8                     mov         eax,ebx
0039    0F 00 D8                  ltr         ax
003C    E9 00 00 00 00            jmp         L$1
0041                          L$1:
0041    59                        pop         ecx
0042    5B                        pop         ebx
0043    C3                        ret

Routine Size: 52 bytes,    Routine Base: _TEXT + 0010

No disassembly errors

Segment: CONST BYTE USE32 00000019 bytes
0000                          L$2:
0000    5B 74 73 73 5D 3A 20 4C 6F 61 64 69 6E 67 20 54 [tss]: Loading T
0010    52 3A 20 25 30 38 78 0A 00                      R: %08x..

Segment: CONST2 PARA USE32 00000000 bytes

Segment: _DATA BYTE USE32 00000000 bytes

Segment: _BSS BYTE USE32 00000000 bytes

BSS Size: 0 bytes

Здесь я знаю, какая функция что вызывает, и какие данные что значат.

Теперь, ярым любителям GNU GCC представляю аж целых два (!) листинга. Первый был получен при помощи команды:

g++ <source_file> -O3 -S -march=i386 -mtune=pentiumpro -nodefaultlibs <object_file>

И получаем следующий плохо читаемый код:

        .file   "initial.cpp"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "[tss]: Loading TR: %08x\n"
        .text
        .align 2
        .p2align 4,,15
.globl _ZN16initial_thread_t10InitializeEP10task_tag_t
        .type   _ZN16initial_thread_t10InitializeEP10task_tag_t, @function
_ZN16initial_thread_t10InitializeEP10task_tag_t:
.LFB332:
        pushl   %ebp
.LCFI0:
        movl    %esp, %ebp
.LCFI1:
        pushl   %ebx
.LCFI2:
        subl    $20, %esp
.LCFI3:
        movl    $128, (%esp)
        call    _ZN4TTSSnwEj
        movl    %eax, 4(%esp)
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    _ZN15common_thread_t13AllocSelectorEP4TTSS
        movl    $.LC0, (%esp)
        movl    %eax, %ebx
        movl    %eax, 4(%esp)
        call    _Z8k_printfPKcz
        movl    %ebx, %eax
#APP
        .intel_syntax noprefix
ltr ax
jmp 1f
1:
.att_syntax noprefix

#NO_APP
        addl    $20, %esp
        popl    %ebx
        popl    %ebp
        ret
.LFE332:
        .size   _ZN16initial_thread_t10InitializeEP10task_tag_t, .-_ZN16initial_thread_t10InitializeEP10task_tag_t
.globl __gxx_personality_v0
        .section        .eh_frame,"a",@progbits
.Lframe1:
        .long   .LECIE1-.LSCIE1
.LSCIE1:
        .long   0x0
        .byte   0x1
        .string "zP"
        .uleb128 0x1
        .sleb128 -4
        .byte   0x8
        .uleb128 0x5
        .byte   0x0
        .long   __gxx_personality_v0
        .byte   0xc
        .uleb128 0x4
        .uleb128 0x4
        .byte   0x88
        .uleb128 0x1
        .align 4
.LECIE1:
.LSFDE1:
        .long   .LEFDE1-.LASFDE1
.LASFDE1:
        .long   .LASFDE1-.Lframe1
        .long   .LFB332
        .long   .LFE332-.LFB332
        .uleb128 0x0
        .byte   0x4
        .long   .LCFI0-.LFB332
        .byte   0xe
        .uleb128 0x8
        .byte   0x85
        .uleb128 0x2
        .byte   0x4
        .long   .LCFI1-.LCFI0
        .byte   0xd
        .uleb128 0x5
        .byte   0x4
        .long   .LCFI3-.LCFI1
        .byte   0x83
        .uleb128 0x3
        .align 4
.LEFDE1:
        .ident  "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
        .section        .note.GNU-stack,"",@progbits

Нет, конечно, assemblable output не претендует быть суперчитабельным. Аналогично поступает и утилита wdis, если ей задать ключ ‘-a’:

.387
.386p
.model flat
                PUBLIC  `W?Initialize$:initial_thread_t$n(pn$task_tag_t$$)v`
                EXTRN   `W?$nw:TTSS$n(ui)pnv`:BYTE
                EXTRN   `W?AllocSelector$:common_thread_t$n(pn$TTSS$$)ui`:BYTE
                EXTRN   `W?k_printf$n(pnxae)i`:BYTE
DGROUP          GROUP   CONST,CONST2,_DATA,_BSS
_TEXT           SEGMENT PARA PUBLIC USE32 'CODE'
                ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
    lea         eax,[eax]
    mov         ecx,ecx
    DB  49H, 6eH, 69H, 74H, 69H, 61H, 6cH, 69H
    DB  7aH, 65H, 0aH
`W?Initialize$:initial_thread_t$n(pn$task_tag_t$$)v`:
    push        ebx
    push        ecx
    mov         ebx,eax
    mov         eax,80H
    call        near ptr FLAT:`W?$nw:TTSS$n(ui)pnv`
    mov         edx,eax
    mov         eax,ebx
    call        near ptr FLAT:`W?AllocSelector$:common_thread_t$n(pn$TTSS$$)ui`
    push        eax
    push        offset FLAT:L$2
    mov         ebx,eax
    call        near ptr FLAT:`W?k_printf$n(pnxae)i`
    add         esp,8
    mov         eax,ebx
    ltr         ax
    jmp         near ptr L$1
L$1:
    pop         ecx
    pop         ebx
    ret
_TEXT           ENDS
CONST           SEGMENT BYTE PUBLIC USE32 'DATA'
L$2:
    DB  5bH, 74H, 73H, 73H, 5dH, 3aH, 20H, 4cH
    DB  6fH, 61H, 64H, 69H, 6eH, 67H, 20H, 54H
    DB  52H, 3aH, 20H, 25H, 30H, 38H, 78H, 0aH
    DB  0

CONST           ENDS
CONST2          SEGMENT PARA PUBLIC USE32 'DATA'
CONST2          ENDS
_DATA           SEGMENT BYTE PUBLIC USE32 'DATA'
_DATA           ENDS
_BSS            SEGMENT BYTE PUBLIC USE32 'BSS'
_BSS            ENDS

                END

Единственное, чего я не понимаю — что G++ там в конце понагенерил. Ну да ладно, спишем пока на мои незнания.

Ок, но всё же хочется получить листинг, поэтому я прибегаю к утилите objdump:

objdump -D -Mintel <object_file> > <listing_file>

И получаю ещё менее читабельную и понятную фигню:

.bin/xskernel/include/arch/i386/ipc/initial.cpp.o:     file format elf32-i386

Disassembly of section .text:

00000000 <_ZN16initial_thread_t10InitializeEP10task_tag_t>:
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   53                      push   ebx
   4:   83 ec 14                sub    esp,0x14
   7:   c7 04 24 80 00 00 00    mov    DWORD PTR [esp],0x80
   e:   e8 fc ff ff ff          call   f <_ZN16initial_thread_t10InitializeEP10task_tag_t+0xf>
  13:   89 44 24 04             mov    DWORD PTR [esp+4],eax
  17:   8b 45 08                mov    eax,DWORD PTR [ebp+8]
  1a:   89 04 24                mov    DWORD PTR [esp],eax
  1d:   e8 fc ff ff ff          call   1e <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x1e>
  22:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  29:   89 c3                   mov    ebx,eax
  2b:   89 44 24 04             mov    DWORD PTR [esp+4],eax
  2f:   e8 fc ff ff ff          call   30 <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x30>
  34:   89 d8                   mov    eax,ebx
  36:   0f 00 d8                ltr    ax
  39:   eb 00                   jmp    3b <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x3b>
  3b:   83 c4 14                add    esp,0x14
  3e:   5b                      pop    ebx
  3f:   5d                      pop    ebp
  40:   c3                      ret
Disassembly of section .rodata.str1.1:

00000000 <.rodata.str1.1>:
   0:   5b                      pop    ebx
   1:   74 73                   je     76 <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x76>
   3:   73 5d                   jae    62 <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x62>
   5:   3a 20                   cmp    ah,BYTE PTR [eax]
   7:   4c                      dec    esp
   8:   6f                      outs   [dx],DWORD PTR ds:[esi]
   9:   61                      popa
   a:   64 69 6e 67 20 54 52    imul   ebp,DWORD PTR fs:[esi+103],0x3a525420
  11:   3a
  12:   20 25 30 38 78 0a       and    BYTE PTR ds:0xa783830,ah
        ...
Disassembly of section .eh_frame:

00000000 <.eh_frame>:
   0:   18 00                   sbb    BYTE PTR [eax],al
   2:   00 00                   add    BYTE PTR [eax],al
   4:   00 00                   add    BYTE PTR [eax],al
   6:   00 00                   add    BYTE PTR [eax],al
   8:   01 7a 50                add    DWORD PTR [edx+80],edi
   b:   00 01                   add    BYTE PTR [ecx],al
   d:   7c 08                   jl     17 <.eh_frame+0x17>
   f:   05 00 00 00 00          add    eax,0x0
  14:   00 0c 04                add    BYTE PTR [esp+eax],cl
  17:   04 88                   add    al,0x88
  19:   01 00                   add    DWORD PTR [eax],eax
  1b:   00 18                   add    BYTE PTR [eax],bl
  1d:   00 00                   add    BYTE PTR [eax],al
  1f:   00 20                   add    BYTE PTR [eax],ah
  21:   00 00                   add    BYTE PTR [eax],al
  23:   00 00                   add    BYTE PTR [eax],al
  25:   00 00                   add    BYTE PTR [eax],al
  27:   00 41 00                add    BYTE PTR [ecx],al
  2a:   00 00                   add    BYTE PTR [eax],al
  2c:   00 41 0e                add    BYTE PTR [ecx+14],al
  2f:   08 85 02 42 0d 05       or     BYTE PTR [ebp+0x50d4202],al
  35:   44                      inc    esp
  36:   83                      .byte 0x83
  37:   03                      .byte 0x3
Disassembly of section .comment:

00000000 <.comment>:
   0:   00 47 43                add    BYTE PTR [edi+67],al
   3:   43                      inc    ebx
   4:   3a 20                   cmp    ah,BYTE PTR [eax]
   6:   28 47 4e                sub    BYTE PTR [edi+78],al
   9:   55                      push   ebp
   a:   29 20                   sub    DWORD PTR [eax],esp
   c:   34 2e                   xor    al,0x2e
   e:   31 2e                   xor    DWORD PTR [esi],ebp
  10:   32 20                   xor    ah,BYTE PTR [eax]
  12:   32 30                   xor    dh,BYTE PTR [eax]
  14:   30 36                   xor    BYTE PTR [esi],dh
  16:   31 31                   xor    DWORD PTR [ecx],esi
  18:   31 35 20 28 70 72       xor    DWORD PTR ds:0x72702820,esi
  1e:   65                      gs
  1f:   72 65                   jb     86 <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x86>
  21:   6c                      ins    BYTE PTR es:[edi],[dx]
  22:   65                      gs
  23:   61                      popa
  24:   73 65                   jae    8b <_ZN16initial_thread_t10InitializeEP10task_tag_t+0x8b>
  26:   29 20                   sub    DWORD PTR [eax],esp
  28:   28 53 55                sub    BYTE PTR [ebx+85],dl
  2b:   53                      push   ebx
  2c:   45                      inc    ebp
  2d:   20 4c 69 6e             and    BYTE PTR [ecx+ebp*2+110],cl
  31:   75 78                   jne    ab <_ZN16initial_thread_t10InitializeEP10task_tag_t+0xab>
  33:   29 00                   sub    DWORD PTR [eax],eax

Такой распорядок вещей мне не нравится.

Вопрос знатокам: есть возможность получить нормальный (или, хотя бы, более понятный) ассемблерный листинг, чтобы увидеть хотя бы, какие функции вызываются функцией, и какие данные где расположены?

А так, имхо, Watcom пока побеждает по читабельности дизассемблерного кода.

Реклама

13 комментариев

  1. Анонимно:

    Блин, Владимир… ну это не серьезно… :)

    В принципе мыслю, что компилятору нафиг не нужно генерировать юзерфрендли формат. Это промежуточное представление при компиляции и только.

    Что касается имен C++, то это называется Mangled name… чтобы увидеть их в человеческом виде — используй demangle. :) который в objdump вызывается через -C.

  2. Анонимно:

    А зачем jmp после ltr?

  3. На всякий пожарный, для i486, у которого может не сброситься очередь предвыборки. Чтобы сбросить очередь предвыборки, делается jump. Кстати, если мне не изменяет память, сама intel рекомендовала в своих мануалах.

  4. Re: Блин, Владимир… ну это не серьезно… :)

    О, спасибо! Заюзал опцию, названия функций стали более читабельными.
    Остаётся вопрос за:
    - вызовом функции k_printf, который ну никак не показывается в листинге;
    - изображением секции данных как секции данных, а не как кода.

  5. Анонимно:

    Если мне не изменяет память, то я сам тебе когда-то это рекомендовал :) . Только, я думаю, что при загрузке tr сбрасывать очередь не обязательно.

  6. Анонимно:

    Re: Блин, Владимир… ну это не серьезно… :)

    Насчет printk пока не знаю, а для отмены дизассемблирования секций данных нужна опция -d вместо опции -D. -D==disassemble-all, т.е. все, что только можно дизассемблировать :) . А -d — только секции, где предполагается наличие кода.

  7. Дрон, это ты, что ли, анонимайзишься?

    Возможно. Только это делается один раз на один процессор, дальше — тупо переключение через jump, поэтому, в принципе, можно и оставить.

  8. Re: Блин, Владимир… ну это не серьезно… :)

    Насчёт опции -d я знаю, вот mangled names проглядел.

  9. Анонимно:

    >Дрон, это ты, что ли, анонимайзишься?

    Мимо :) .

    >Возможно. Только это делается один раз на один процессор, дальше — тупо
    >переключение через jump, поэтому, в принципе, можно и оставить.

    Ну, хуже от нее, конечно, не будет, просто непонятно было, зачем.

  10. Анонимно:

    Re: Блин, Владимир… ну это не серьезно… :)

    А с какой оптимизацией компилировал? Без оптимизации gcc вставляет странную феерию со стеком (в прологе что-то вроде:
    mov eax, 0xF
    add eax, 0xF
    shr eax, 0×4
    shl eax, 0×4
    sub esp, eax
    но могу и ошибаться, на память написал). Для чего это надо, остается догадываться.

  11. Анонимно:

    Нет, это не я…

    Насчет стека… у gcc есть опция для типов — align че-то типа того…
    Если такую переменную расположить в стеке — gcc по идее должен выровнять ее смещение. Не могу точно сказать какое выравнивание по умолчанию… Во всяком случае вышеуказанные манипуляции очень похожи на выравнивание.

    А я почти параллельно с ltr делаю переход на адрес первой задачи, поэтому у меня так и так получается переход. :)

  12. Мммм. Кто ж такой читает мой ЖЖ, но не признаётся :)

    SII, ты, что ль?

  13. Re: Блин, Владимир… ну это не серьезно… :)

    Компилил с -O3 (мега-злая оптимизация :) ). Кстати, припаял ещё опции -mregparm=3 -mtune=pentiumpro, код стал покрасивше (да-да, я очень люблю передачу параметров в регистрах).

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

Blue Captcha Image
Refresh

*