Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3057 
 Solaris...2933 
 LD...2904 
 Linux Kernel 2.6...2470 
 William Gropp...2180 
 Rodriguez 6...2011 
 C++ Templates 3...1945 
 Trees...1937 
 Kamran Husain...1865 
 Secure Programming for Li...1792 
 Максвелл 5...1710 
 DevFS...1693 
 Part 3...1682 
 Stein-MacEachern-> Час...1632 
 Go Web ...1624 
 Ethreal 4...1618 
 Arrays...1606 
 Стивенс 9...1603 
 Максвелл 1...1592 
 FAQ...1538 
 
  01.01.2024 : 3621733 посещений 

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.
 
Оставьте свой комментарий !

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

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