Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org
 ========================================================================
                         LINUX ASSEMBLER TUTORIAL
                                    by
                               Robin Miyagi
 ========================================================================
   При программировании под линукс нужно помнить , что это
 операционная система с защищенным режимом . Это означает ,
 что пользовательским процессам недоступны например DMA или
 доступ к IO портам . С другой стороны , написание модулей ядра
 позволяет получить доступ к железу напрямую . Пользовательские
 процессы могут иметь доступ к железу с помощью файлов устройств.
 
 * Системные вызовы
 ------------------------------------------------------------------------
  В программировании под ДОС вы возможно ипользовали 21-е прерывание.
 В линуксе аналогичную роль играет прерывание 0x80.
 Основным параметром прерывания является его номер - system call number -
 это параметр передавается в регистр EAX. Другие регистры используются
 также для получения других параметров прерывания.Мы будем рассматривать
 только такие прерывания, в которых не более 5 параметров.
 В противном случае параметры прерывания надо размещать где-то в памяти. 
  Список системных вызовов можно найти в хидере /usr/include/asm/unistd.h.
 Для получения хэлпа можно набрать что-то типа `man 2 write' .   
 В файле /usr/include/asm/unistd.h в заголовке можно увидеть следующее :
     #define __NR_write          4   
 Это говорит о том , что регистр EAX должен быть установлен в 4  перед вызовом.
 
 * GNU Assembler
 ------------------------------------------------------------------------
 
 GNU  C compiler (gcc) использует ассемблер , называемый как `as' .
 `As'  использует AT&T  синтаксис.   
 Для получения информации можно набрать
           `info as' 
 
     
 ** GNU assembler / Intel Syntax
 ------------------------------------------------------------------------
  Сравним интеловский и айтишный синтаксисы :
       - В `as' источник находится перед приемником
       
       - перед операндами ставится суффикс , указывающий на его размер
         ( `l' - dword, `w' - word, `b' - byte).
 
       - операнды идут с префиксом `$', регистры с префиксом  `%'.
 
       - эффективный адрес имеет синтаксис  DISP(BASE,INDEX,SCALE).  
       Пример :
             movl mem_location(%ebx,%ecx,4), %eax
         Что эквивалентно интеловскому :
             mov eax, [eax + ecx*4 + mem_location]
 
 Пример :
 ------------------------------------------------------------------------   
         ## hello-world.s
 
         ## by Robin Miyagi
         ## http://www.geocities.com/SiliconValley/Ridge/2544/
 
         ## Компиляция :
         ## -------------------------------------------------------------
         ## as -o hello-world.o hello-world.s
         ## ld -o hello-world -O0 hello-world.o
 
         ## Программа выводит строку на экран
         .section .data
 hello:  
         .ascii  "Hello, world!\n"
 hello_len:
         .long   . - hello
         .section .text
         .globl _start
         
 _start:
         ## используется системный вызов write () 
         xorl %ebx, %ebx         # %ebx = 0
         movl $4, %eax           # write () system call
         xorl %ebx, %ebx         # %ebx = 0
         incl %ebx               # %ebx = 1, fd = stdout
         leal hello, %ecx        # %ecx ---> hello
         movl hello_len, %edx    # %edx = count
         int $0x80               # execute write () system call
         
         ## terminate program via _exit () system call 
         xorl %eax, %eax         # %eax = 0
         incl %eax               # %eax = 1 system call _exit ()
         xorl %ebx, %ebx         # %ebx = 0 normal program return code
         int $0x80               # execute system call _exit ()
 
 
 ------------------------------------------------------------------------
      
     `#' - строка с комментариями .
 Комментарии можно также писать с помощью си-шного синтаксиса /* */
 Сегменты начинаются с ключевых слов .text и  .data. 
 Есть еще одна секция -   .bss  - для хранения неинициализированных 
 данных .
 
 
 ------------------------------------------------------------------------
 * Доступ к параметрам командной строки и переменным окружения
  При старте приложения параметры командной строки и переменные
 окружения доступны в стеке . Доступ к ним можно получить с
 помощью указателя , который хранится в ESP.
  Число аргументов командной строки плюс имя программы хранится
 как целое число в  [esp].  Далее , по адресу стека  [esp+4] 
 хранится указатель на первый аргумент . Последующие соответственно
 в [esp+8], [esp+12], и т.д. В конце идет NULL-указатель . После него
 идут указатели на переменные окружения и после опять 
 NULL-указатель .
     (%esp)       argc, count of arguments (integer)
     4(%esp)      char *argv (pointer to first command line argument)
        ...       pointers to the rest of the command line arguments
     ?(%esp) NULL pointer
        ...       pointers to environment variables
     ??(%esp)     NULL pointer
 
     Еще одна программа :
         ## Показан доступ к параметрам командной строки
         ## Compile Instructions:
         ## -------------------------------------------------------------
         ## as -o stack-param.o stack-param.s
         ## ld -O0 -o stack-param stack-param.o
         .section .data
 new_line_char:
         .byte 0x0a
         .section .text
 
         .globl _start
 
         .align 4
 _start:
         movl %esp, %ebp         # store %esp in %ebp
 again:
         addl $4, %esp           # %esp ---> next parameter on stack
         movl (%esp), %eax       # move next parameter into %eax
         testl %eax, %eax        # %eax (parameter) == NULL pointer?
         jz end_again            # get out of loop if yes
         call putstring          # output parameter to stdout.
         jmp again               # repeat loop
 end_again:
         xorl %eax, %eax         # %eax = 0
         incl %eax               # %eax = 1, system call _exit ()
         xorl %ebx, %ebx         # %ebx = 0, normal program exit.
         int $0x80               # execute _exit () system call
 
         ## prints string to stdout
 putstring:      .type @function
         pushl %ebp
         movl %esp, %ebp
         movl 8(%ebp), %ecx
         xorl %edx, %edx
 count_chars:
         movb (%ecx,%edx,$1), %al
         testb %al, %al
         jz done_count_chars
         incl %edx
         jmp count_chars
 done_count_chars:
         movl $4, %eax
         xorl %ebx, %ebx
         incl %ebx
         int $0x80
         movl $4, %eax
         leal new_line_char, %ecx
         xorl %edx, %edx
         incl %edx
         int $0x80 
         movl %ebp, %esp
         popl %ebp
         ret
 
 ------------------------------------------------------------------------
 
 * Пакет Binutils
 
  Этот пакет включает полезные тулзы , включая дебаг .
 
 
 ** Objdump
 ------------------------------------------------------------------------
     Выводит информацию об обьектных файлах .
 Например :     
         objdump -x param-stack | less
 
 Вывод :
         ----------------------------------------------------------------
         stack-param:     file format elf32-i386
         stack-param
         architecture: i386, flags 0x00000112:
         EXEC_P, HAS_SYMS, D_PAGED
         start address 0x08048074
         
         Program Header:
             LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
                  filesz 0x000000be memsz 0x000000be flags r-x
             LOAD off    0x000000c0 vaddr 0x080490c0 paddr 0x080490c0 align 2**12
                  filesz 0x00000001 memsz 0x00000004 flags rw-
         
         Sections:
         Idx Name          Size      VMA       LMA       File off  Algn
           0 .text         0000004a  08048074  08048074  00000074  2**2
                           CONTENTS, ALLOC, LOAD, READONLY, CODE
           1 .data         00000001  080490c0  080490c0  000000c0  2**2
                           CONTENTS, ALLOC, LOAD, DATA
           2 .bss          00000000  080490c4  080490c4  000000c4  2**2
                           ALLOC
         SYMBOL TABLE:
         08048074 l    d  .text  00000000 
         080490c0 l    d  .data  00000000 
         080490c4 l    d  .bss   00000000 
         00000000 l    d  *ABS*  00000000 
         00000000 l    d  *ABS*  00000000 
         00000000 l    d  *ABS*  00000000 
         080490c0 l       .data  00000000 new_line_char
         08048076 l       .text  00000000 again
         08048087 l       .text  00000000 end_again
         0804808e l       .text  00000000 putstring
         08048096 l       .text  00000000 count_chars
         080480a0 l       .text  00000000 done_count_chars
         00000000       F *UND*  00000000 
         080480be g     O *ABS*  00000000 _etext
         08048074 g       .text  00000000 _start
         080490c1 g     O *ABS*  00000000 __bss_start
         080490c1 g     O *ABS*  00000000 _edata
         080490c4 g     O *ABS*  00000000 _end
 
         ----------------------------------------------------------------
  Информация извлекается из хидера обьектного ELF-файла .
 Включается информация о секциях 
 
 Objdump можно использовать для дизассемблирования бинарных файлов :
         objdump -d stack-param | less
 Вывод этой команды :
         ----------------------------------------------------------------
         stack-param:     file format elf32-i386
 
         Disassembly of section .text:
         
         08048074 <_start>:
          8048074:       89 e5                   movl   %esp,%ebp
         
         08048076 :
          8048076:       83 c4 04                addl   $0x4,%esp
          8048079:       8b 04 24                movl   (%esp,1),%eax
          804807c:       85 c0                   testl  %eax,%eax
          804807e:       74 07                   je     8048087 
          8048080:       e8 09 00 00 00          call   804808e 
          8048085:       eb ef                   jmp    8048076 
         
         08048087 :
          8048087:       31 c0                   xorl   %eax,%eax
          8048089:       40                      incl   %eax
          804808a:       31 db                   xorl   %ebx,%ebx
          804808c:       cd 80                   int    $0x80
         
         0804808e :
          804808e:       55                      pushl  %ebp
          804808f:       89 e5                   movl   %esp,%ebp
          8048091:       8b 4d 08                movl   0x8(%ebp),%ecx
          8048094:       31 d2                   xorl   %edx,%edx
         
         08048096 :
          8048096:       8a 04 11                movb   (%ecx,%edx,1),%al
          8048099:       84 c0                   testb  %al,%al
          804809b:       74 03                   je     80480a0 
          804809d:       42                      incl   %edx
          804809e:       eb f6                   jmp    8048096 
         
         080480a0 :
          80480a0:       b8 04 00 00 00          movl   $0x4,%eax
          80480a5:       31 db                   xorl   %ebx,%ebx
          80480a7:       43                      incl   %ebx
          80480a8:       cd 80                   int    $0x80
          80480aa:       b8 04 00 00 00          movl   $0x4,%eax
          80480af:       8d 0d c0 90 04 08       leal   0x80490c0,%ecx
          80480b5:       31 d2                   xorl   %edx,%edx
          80480b7:       42                      incl   %edx
          80480b8:       cd 80                   int    $0x80
          80480ba:       89 ec                   movl   %ebp,%esp
          80480bc:       5d                      popl   %ebp
          80480bd:       c3                      ret    
         ----------------------------------------------------------------
 
     Параметр `-d' указывает objdump делать дизассемблинг кодового сегмента .
 Опция `-D' указывает на все сегменты .
 Objdump может выводить имена меток .
 Первый столбик в последнем выводе - виртуальные адреса.
 Второй выводит машинные коды .
 Третий выводит собственно ассемблерный код .
 
 ------------------------------------------------------------------------
 ** Определение размера используемой памяти
 
   Команда
      `ls -l stack-param' 
 выдаст 
         -rwxrwxr-x    1 robin    robin         932 Sep 15 18:21 stack-param  
 Файл имеет размер 932 байта.  
 Для определения памяти , которую может использовать эта программа при выполнении :
         size stack-param
 Получим результат :
         text       data     bss     dec     hex filename
           74          1       0      75      4b stack-param
 Кодовый сегмент использует 74 байта , данные - 1 байт , итого - 75 байт .    
 
 ------------------------------------------------------------------------
 * Дебаг и gdb
 
   Дебаг - один из самых сложных аспектов программирования .
     
     Пример программы , которая выходит с сигналом SIG_SEGV
         ## stack-param-error.s 
 
         ## В примере описывается доступ  командным параметрам
         ## Compile Instructions:
         ## -------------------------------------------------------------
         ## as --gstabs -o stack-param-error.o stack-param-error.s
         ## ld -O0 -o stack-param-error stack-param-error.o
         .section .data
 new_line_char:
         .byte 0x0a
         .section .text
         .globl _start
         .align 4
 _start:
         movl %esp, %ebp         # store %esp in %ebp
 again:
         addl $4, %esp           # %esp ---> next parameter on stack
         leal (%esp), %eax       # move next parameter into %eax
         testl %eax, %eax        # %eax (parameter) == NULL pointer?
         jz end_again            # get out of loop if yes
         call putstring          # output parameter to stdout.
         jmp again               # repeat loop
 end_again:
         xorl %eax, %eax         # %eax = 0
         incl %eax               # %eax = 1, system call _exit ()
         xorl %ebx, %ebx         # %ebx = 0, normal program exit.
         int $0x80               # execute _exit () system call
 
         ## prints string to stdout
 putstring:      .type @function
         pushl %ebp
         movl %esp, %ebp
         movl 8(%ebp), %ecx
         xorl %edx, %edx
 count_chars:
         movb (%ecx,%edx,$1), %al
         testb %al, %al
         jz done_count_chars
         incl %edx
         jmp count_chars
 done_count_chars:
         movl $4, %eax
         xorl %ebx, %ebx
         incl %ebx
         int $0x80
         movl $4, %eax
         leal new_line_char, %ecx
         xorl %edx, %edx
         incl %edx
         int $0x80 
         movl %ebp, %esp
         popl %ebp
         ret
 
 
 
 Программа откомпилирована с опцией  `--gstabs'.
 Она выводит отладочную информацию в бинарник .  
 Теперь найдем ошибку с помощтю команды :
         gdb stack-param-error
 
 После чего появится приглашение для дебага :
         (gdb) run eat my shorts
         /home/robin/programming/asm-tut/stack-param-error
         eat
         my
         shorts
         Program recieved SIGSEGV, segmentation fault
         count_chars () at stack-param-error.s:47
 
         47      movb (%ecx,%edx,$1), %al
         Current language: auto; currently asm
         (gdb) q
         [~]$ _
 Здесь приведен не весь вывод .
 Мы видим , что ошибка произошла 47-й строке .
 А сама проблема живет в 29-й строке .        
             reads `movl (%esp), %eax'.
 Мы загружаем в eax нулевой несуществующий указатель .            
 Цикл в функции _start () никогда не закончится .
 
  Для получения общей информации о gdb наберите 
      `info  gdb'
 
 --------------------------------------------------------------------
 Comments and suggestions 
 
 You are free to make verbatim copies of this file, providing that this
 notice is preserved.
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье