Переносимость приложения

Тема в разделе "WASM.UNIX", создана пользователем K10, 19 дек 2008.

  1. K10

    K10 New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2008
    Сообщения:
    1.590
    В никсах не шарю совершенно, так что сильно не пинайте...

    Возможно ли создать переносимое приложение на ассемблере под nix? Имееются ввиду i386 FreeBSD и Linux.

    Вот например
    http://www.wasm.ru/article.php?article=asmunixlot
    Там есть пример клиент-серверного приложения:
    Код (Text):
    1. # *******************************************************
    2. # Server (daemon)
    3. # by Broken Sword [HI-TECH]
    4. # (for Linux based on Intel x86 only)
    5.  
    6. # brokensword@mail.ru
    7. # www.wasm.ru
    8.  
    9. # Compile Instructions:
    10. # -------------------------------------------------------
    11. # as server.s
    12. # ld --strip-all -o server a.out
    13. # *******************************************************
    14.  
    15. # *******************************************************
    16.  
    17. # этот файлик содержит выдранные из какого-то файла
    18. # определения системных вызовов (см. FUCK #3)
    19. .include        "syscalls.inc"     
    20. # а этот – все остальные, которые только могут
    21. # встретиться
    22.  
    23. .include    "def.inc"              
    24.         .text                       # начало сегмента кода
    25. # метка, с которой все начинается (нужно чтоб она была
    26. # глобальной)
    27.         .globl      _start         
    28.  
    29. _start:                        
    30. # итак, начинаем лепить нашего демона
    31. # процесс создания демона в *.nix и создание резидента в DOS в корне различаются
    32. # начинается любой демон с того, что нужно создать дочерний процесс.
    33. # Создать дочерний процесс в линуксе проще
    34. # пареной репы – достаточно поместить номер сис.
    35. # вызова в EAX и сделать «а-ля int 21h», т.е. int 80h
    36.         movl        $SYS_fork,%EAX
    37.         int     $0x80      
    38. # все.
    39. # Теперь у нас параллельно сосуществуют ДВА процесса:
    40. # родительский (в котором исполнялись все предыдущие
    41. # команды) и дочерний. Что же содержит дочерний код?
    42. # А все то же самое, что и родительский.
    43. # Т.е. важно понять, что # весь нижеследующий (и выше тоже)
    44. # код находиться в памяти в ДВУХ разных местах.
    45. # Как процессор переключается между
    46. # ними (и всеми остальными живыми процессами)
    47. # – читайте «Переключение задач» в интеловском мануале.
    48. test        %EAX,%EAX      
    49. # вот эту команду необходимо осознать.
    50. # Прежде всего, важно понять, что данная команда
    51. # существует и в родительском
    52. # и в дочернем процессах (об этом выше).
    53. # Следовательно выполниться она и там и там.
    54. # Все дело в том, что после
    55. # int 80h родительскому процессу вернется PID сына
    56. # (в EAX ессесно, вообще все возвращается в EAX, как и в винде)
    57. # а что же вернется сыне? Правильно, нолик.
    58. # Именно поэтому следующий jmp будет выполнен
    59. # в дочернем процессе и
    60. # не будет выполнен в родительском.
    61.  
    62. # ребенок улетает на метку _cont1
    63.         jz      _cont1         
    64. # ...а в это время, в родительском процессе:
    65.         xorl        %EBX,%EBX       # EBX=status code
    66.         xorl        %EAX,%EAX       #
    67.         incl        %EAX            # SYS_exit
    68. # завершаем родительский процесс.
    69.         int     $0x80          
    70.  
    71. # Теперь все дети
    72. # управляются процессом INIT
    73.  
    74.  
    75. _cont1:
    76.         movl        $SYS_setsid,%EAX
    77. # сделаем нашего ребенка главным в группе
    78.         int     $0x80          
    79.        
    80.         movl        $1,%EBX         # SIGHUP
    81.         movl        $1,%ECX         # SIG_IGN
    82.         movl        $SYS_signal,%EAX
    83. # далее сигнал SIGHUP будет игнорироваться
    84.         int     $0x80          
    85.                            
    86.         movl        $SYS_fork,%EAX
    87. # наш ребенок уже подрос и теперь сам может родить сына
    88.         int     $0x80          
    89. # (по сути – это уже внук нашему изначальному
    90. # родительскому процессу)
    91.  
    92. # EAX=0 в дочернем и EAX=PIDдочернего в родительском
    93.  
    94.         test        %EAX,%EAX      
    95.  
    96. jz      _cont2         
    97.  
    98. # внук нашего родительского (которого уже давно нет в
    99. # живых) улетает на метку _cont2, однако отец все еще
    100. # жив!!! (все как в мексиканском сериале)
    101.        
    102.         xorl        %EBX,%EBX       # EBX=status code
    103.         xorl        %EAX,%EAX       #
    104.         incl        %EAX            # SYS_exit
    105.         int         $0x80          
    106.  
    107. # вот уже и отец отправлен к деду на небеса (да,
    108. # злостная программка, недаром демоном зовется)
    109.  
    110. # ..а в это время внучок получает все наследство и
    111.  
    112. _cont2:
    113.  
    114. # продолжает жить
    115. # далее, после того,
    116. # как все кровавые разборки и отцеубийства благополучно завершены,
    117. # внучок,продавший душу демону,
    118. # преспокойно создает сокет.
    119. # Дело в том, что в линуксе есть такие системные вызовы,
    120. # для вызова которых их номер не
    121. # помещается в EAX.
    122. # Вместо этого в EAX помещается номер функции-мультиплексора,
    123. # реализующий вызов конкретной
    124. # функции номер которой помещается в EBX.
    125. # Так, например, происходит при вызове IPC и SOCKET-функций.
    126. # Кроме того,
    127. # при вызове SOCKET-функций параметры располагаются не в регистрах,
    128. # а в стеке. Смотри как все просто:
    129.  
    130.         pushl       $0          # протокол
    131.         pushl       $SOCK_STREAM        # тип
    132.  
    133.         pushl       $AF_INET            # домен
    134. # ECX должен указывать на кадр в стеке, содержащий
    135.         movl        %ESP,%ECX      
    136. # параметры, такая уж у него судьба...
    137.         movl        $SYS_SOCKET,%EBX       
    138. # а вот это уже номер той самой конкретной функции
    139. # SOCKET – создать сокет
    140.                            
    141.         movl        $SYS_socketcall,%EAX
    142.    
    143. # в EAX - номер функции мультиплексора (по сути он
    144. # просто перенаправит вызов в функцию, указанную в EBX
    145.        
    146.        int         $0x80
    147.  
    148. # сокет создан! Ура товарищи. В EAX возвратиться его дескриптор.
    149.  
    150. # «очистим» стек (по сути это выражение придумано специально
    151. # для HL-программистов, на самом деле ничего не
    152. # очищается, данную операцию необходимо производить только
    153. # для того чтобы в дальнейшем не произошло переполнение
    154. # стека, но в таких маленьких программках это делать вовсе
    155. # не обязательно):
    156.  
    157.         addl        $0xC,%ESP
    158.  
    159.         movl        %EAX,(sockfd)      
    160.  
    161. # сохраним дескриптор созданного сокета в переменной
    162. # sockfd
    163.  
    164. # далее необходимо осуществить операцию BIND,
    165. # которая называется «привязка имени сокету»,
    166. # хотя суть этого названия
    167. # слабо отражает смысл происходящего на самом деле.
    168. # На самом деле BIND просто назначает конкретному сокету IP-
    169. # адрес и порт, через который с ним можно взаимодействовать:
    170.  
    171. # размер передаваемой структуры (вообще подобран
    172.         pushl       $0x10
    173. # методом тыка, потому что логически непонятно почему
    174. # именно 16)
    175.  
    176. # указатель на структуру sockaddr_in
    177.  
    178.        pushl        $sockaddr_in
    179. # дескриптор нашего сокета
    180.         pushl       %EAX           
    181. # ECX указывает на параметры в стеке
    182.         movl        %ESP,%ECX      
    183. # номер функции BIND – в EBX
    184.         movl        $SYS_BIND,%EBX
    185. # функция-мультиплексор
    186.         movl        $SYS_socketcall,%EAX
    187.         int     $0x80
    188. # теперь сокет «привязан» к конкретному IP-шнику и порту
    189. # поднимем ESP на место
    190.         addl        $0xC,%ESP      
    191. # далее что-либо подробно описывать я не вижу смысла,
    192. # любой желающий сам без труда разберется, опираясь на
    193. # полученные выше знания.
    194.         pushl       $0          # backlog
    195.         movl        (sockfd),%EAX
    196.         pushl       %EAX
    197.         movl        %ESP,%ECX
    198.         movl        $SYS_LISTEN,%EBX
    199.         movl        $SYS_socketcall,%EAX
    200.         int         $0x80
    201.         addl        $0x8,%ESP
    202.  
    203. _wait_next_client:
    204.         pushl       $0          # addrlen
    205.         pushl       $0          # cliaddr
    206.         movl        (sockfd),%EAX
    207.         pushl       %EAX            # sockfd
    208.         movl        %ESP,%ECX
    209.         movl        $SYS_ACCEPT,%EBX
    210.         movl        $SYS_socketcall,%EAX
    211.         int     $0x80
    212.         addl        $0xC,%ESP
    213.  
    214.         movl        %EAX,(connfd)
    215.        
    216.         movl        $SYS_fork,%EAX
    217.         int     $0x80           # create child process
    218.         test        %EAX,%EAX
    219.         jnz     _wait_next_client
    220.  
    221. _next_plain_text:
    222.         movl        (connfd),%EBX
    223.         movl        $buf,%ECX       # ECX->buf
    224.         movl        $1024,%EDX      # 1024 bytes
    225.         movl        $SYS_read,%EAX
    226.         int     $0x80           # wait plain_text
    227.  
    228.         movl        $buf,%ESI
    229.         movl        %ESI,%EDI
    230.         movl        %EAX,%ECX
    231.         movl        %EAX,%EDX
    232. _encrypt:
    233.         lodsb
    234.         cmp     $0x41,%AL       # A
    235.         jb      _next
    236.         cmp     $0x5A,%AL       # Z
    237.         ja      _maybe_small
    238.         incb        %AL
    239.         incb        %AL         # encryption ;)
    240.         cmp     $0x5A,%AL
    241.         jle     _next
    242.         sub     $26,%AL
    243. _maybe_small:
    244.         cmp     $0x61,%AL       # a
    245.         jb      _next
    246.         cmp     $0x7A,%AL       # z
    247.         ja      _next
    248.         incb        %AL
    249.         incb        %AL         # encryption ;)
    250.         cmp     $0x7A,%AL
    251.         jle     _next
    252.         sub     $26,%AL
    253. _next:
    254.         stosb
    255.         loop        _encrypt
    256.        
    257.         movl        (connfd),%EBX
    258.         movl        $buf,%ECX       # ECX->chiper_text
    259.         movl        $SYS_write,%EAX
    260.         int     $0x80           # send plain_text
    261.        
    262.         jmp     _next_plain_text
    263. # *****************************************************
    264.         .data
    265. sockfd:     .long       0
    266. connfd:     .long       0
    267. sockaddr_in:   
    268. sin_family: .word       AF_INET
    269. sin_port:   .word       0x3930          # port:12345
    270. sin_addr:   .long       0           # INADDR_ANY
    271. buf:
    272. # *****************************************************
    Если я к примеру, приложение будет автоматически определять правильную конвенцию syscall'ов, будет ли файл переносимым?

    Проще говоря, приложение должно быть на ассемблере, использовать только системные вызовы, не использовать никаких библиотек (от которых автоматически потянутся зависимости), и нужно чтобы скомпилированный файл запускался на любых системах хотя бы одной ветки, т.е. или Linux или FreeBSD.
     
  2. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    Только твой файл будет запускаться только на Intel x86.

    1) Твоя прога как-то должна определять - на какой системе она запускается и выбирать как на ней делать вызовы (через Int 80h, 7-ой селектор или ещё как).
    2) Подозреваю что сискалы на UNIX'ах немного да отличаются. Программа должна учитывать эти отличия.
    3) Но лучше всего используй макросы в программе, которые в зависимости от системы будут давать компилятору те фрагменты кода, которые реализуют в этой системе возможности программы. Тогда, разумеется, её придется распространять в исходных текстах.
     
  3. bsnake

    bsnake New Member

    Публикаций:
    0
    Регистрация:
    11 сен 2005
    Сообщения:
    91
    Используйте libc :) Программа должна работать на любой системе, где есть libc и которая поддерживает Elf бинарники.
     
  4. meduza

    meduza New Member

    Публикаций:
    0
    Регистрация:
    15 авг 2008
    Сообщения:
    212
    K10
    Возможно. Но надо быть осторожным, потому что набор системных вызовов немного зависит от ветви Unix (Linux, *BSD,..) и от версии ядра одной ОСи.

    п.с. но конечно, так писать не стоит. Потеряешь при таком подходе ты больше, чем получишь. Лучше использовать libc, если думаешь, что так медленней, то не парься, в 95% ты ничего не заметишь. Или, как сказал AndreyMust19, организуй все это дело через макросы и конфигураторы.
     
  5. osrootd

    osrootd New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2008
    Сообщения:
    1.086
    Можно просто использовать не libc.so, а libc_nostd.a
    И тогда вся стандартная библиотека будет помещена в ваш бинарник, но размер у бинарника будет конкретно здоровый:)

    Я щас изучаю аду. Так вот можно проги на аде даже запускать на линухе где ада не стоит.
    То же самое: вместо libgnat.so юзаем libgnat.a
    Правда из 32 килобайт минимальной проги выходит 500.

    Файлы .a - это архивы с набором объектного кода, который включается линкером в вашу прогу.
     
  6. osrootd

    osrootd New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2008
    Сообщения:
    1.086
    Этот пример просто показывает как программировать под никсы.
    Реально, тут POSIX threads рулит, иначе клиенты начнут умирать от старости.

    а вот это:
    откуда взято?
    Поясните, пожалуйста, а то я че то не понял. А как же segfault?

    И еще:
    Почему вы демонов ассоциируете с адом? DAEMON - Disk And Execution MONitor
     
  7. K10

    K10 New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2008
    Сообщения:
    1.590
    osrootd
    Т.е. форк системным вызовом - тормозной?

    Взято отсюда
    http://www.wasm.ru/article.php?article=asmunixlot
    Так что про segfault и демонов - это не ко мне :)

    ПС. да, и вроде расшифровку "Disk And Execution MONitor" придумали гораздо позже самого названия "демон", изначально подразумевался резидентный процесс...
     
  8. osrootd

    osrootd New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2008
    Сообщения:
    1.086
    Потоки и подпроцессы - разные вещи.
     
  9. K10

    K10 New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2008
    Сообщения:
    1.590
    osrootd
    Да уж... Есть еще псевдопотоки фиберы... Другое дело в винде - CreateThread и будет счастье, если нужна высокая производительность, то порты завершения...