• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    Linux下的匯編器

    發布: 2007-7-04 12:06 | 作者: admin | 來源:  網友評論 | 查看: 17次 | 進入軟件測試論壇討論

    領測軟件測試網
      Linux 下兩個最主要的匯編器是 Nasm(free, Netwide Assembler)和 GAS(free, Gnu A
      
      ssembler),
      后一個和 GCC 結合在一起. 在這篇文章里我將集中在 Nasm 上, 把 GAS 放在后面,
      因為它使用 AT&T 的語法, 需要一個長的介紹.
      Nasm 調用時應該帶上 ELF 格式選項("nasm -f elf hello.asm"); 產生的目標文件用
      
      GCC 來鏈接("gcc hello.o"), 產生最終的 ELF 二進制代碼. 下面的這個腳本可用來
      編譯 ASM 的模塊; 我盡量把它寫得簡單, 所以所有它做的就是接受傳給它的第一個
      文件名, 用 Nasm 編譯, 用 GCC 來鏈接.
      #!/bin/sh
      # assemble.sh =========================================================
      outfile=${1%%.*}
      tempfile=asmtemp.o
      nasm -o $tempfile -f elf $1
      gcc $tempfile -o $outfile
      rm $tempfile -f
      #EOF ==================================================================
      基本知識:
      ----------
      當然最好的就是在了解系統細節之前從一個例子開始. 這里是一個最基本的
      "hello-word" 形式的程序:
      ; asmhello.asm ========================================================
      global main
      extern printf
      section .data
      msg db "Helloooooo, nurse!",0Dh,0Ah,0
      section .text
      main:
      push dword msg
      call printf
      pop eax
      ret
      ; EOF =================================================================
      綱要: "global main" 必須聲明為全局的(global) -- 并且既然我們用 GCC 來鏈接,
      進入點必須以 "main" 來命名 -- 從而裝入系統. "extern printf" 只是一個聲明,
      為以后在程序中調用; 注意這是必須的; 參數的大小不需要聲明. 我已經把這個
      例子用標準的 .data, .text 分節, 但這不是嚴格必須的 -- 可能只需要一個 .text
      段, 就像在 DOS 下一樣.
      在代碼的主體部分, 你必須把參數壓棧來傳遞給調用. 在 Nasm 里, 你必須聲明
      所有不明確數據的大小; 因此就有 "dword" 這個限定詞. 注意和其他匯編器一樣,
      Nasm 假設所有的內存/標號的引用都指的是內存地址或者標號, 而不是它的內容.
      因而, 指明字符串 'msg' 的地址, 你應該使用 'push dword msg', 指明字符串 'msg'
      
      
      的內容, 應該用 'push dword [msg]' (這只能包含 'msg' 的前四個字節). 因為 prin
      
      tf
      需要一個指向字符串的指針, 我們應該指明 'msg' 的地址.
      調用 printf 非常的直接. 注意每一次調用后你必須把棧清除(見下); 所以 PUSH 了一
      
      個
      dword 后, 我從棧里把一個 dword POP 進一個無用的寄存器. Linux 程序只簡單的用一
      
      
      個 RET 來返回系統, 由于每個進程都是 shell(或者是 PID)的產物, 所以程序結束后把
      
      
      控制權還給它.
      注意到在 Linux 下, 你是在 "API" 或中斷服務的場所里使用系統帶來的標準共享庫.
      
      所有
      的外部引用由 GCC 管理, 它給 asm 程序員節省了大部分的工作. 一旦你習慣了基本的
      
      技
      巧, Linux 下的匯編編程實際上要比 DOS 簡單的多.
      C 調用的語法
      --------------------
      Linux 使用 C 的調用模式 -- 意味著參數以相反的順序進棧(最后一個最先), 調用者必
      
      須清
      除棧. 你可以從棧里把值 pop 出來:
      push dword szText
      call puts
      pop ecx
      或者直接修改 ESP:
      push dword szText
      call puts
      add esp, 4
      調用的返回值在 eax 或 edx:eax 如果值大于 32 位的話. EBP, ESI, EDI, EBX 由調用
      
      者
      保存和恢復. 你必須保存你要使用的寄存器, 像下面這樣:
      ; loop.asm =================================================================
      
      
      global main
      extern printf
      section .text
      msg db "HoodooVoodoo WeedooVoodoo",0Dh,0Ah,0
      main:
      mov ecx, 0Ah
      push dword msg
      looper:
      call printf
      loop looper
      pop eax
      ret
      ; EOF ======================================================================
      
      
      粗一看, 非常簡單: 因為你在 10 個 printf() 調用用的是同一個字符串, 你不需要清
      
      
      除棧. 但當你編譯以后, 循環不會停止. 為什么? 因為 printf() 里什么地方用了 ECX
      
      
      但沒有保存. 使你的循環正確的工作, 你必須在調用之前保存 ECX 的值, 調用之后
      恢復它, 像這樣:
      ; loop.asm ================================================================
      
      global main
      extern printf
      section .text
      msg db "HoodooVoodoo WeedooVoodoo",0Dh,0Ah,0
      main:
      mov ecx, 0Ah
      looper:
      push ecx ;save Count
      push dword msg
      call printf
      pop eax ;cleanup stack
      pop ecx ;restore Count
      loop looper
      ret
      ; EOF ======================================================================
      
      
      I/O 端口編程
      --------------------
      但直接訪問硬件會怎么樣呢? 在 Linux 下你需要一個核心模式的驅動程序來做這些
      工作... 這意味著你的程序必須分成兩個部分, 一個核心模式提供硬件直接操作的功
      能, 其他的用戶模式提供接口. 一個好消息就是你仍然可以在用戶模式的程序中使用
      IN/OUT 來訪問端口.
      要訪問端口你的程序必須取得系統的同意; 要做這個, 你必須調用 ioperm(). 這個函
      
      數只能被有 root 權限的用戶使用, 所以你必須用 setuid() 使程序到 root 或者直接
      
      
      運行在 root 下. ioperm() 的語法是這樣:
      ioperm( long StartingPort#, long #Ports, BOOL ToggleOn-Off)
      'StartingPort#' 指明要訪問的第一個端口值(0 是端口 0h, 40h 是端口 40h, 等等),
      
      '#Ports'
      指明要訪問多少個端口(也就是說, 'StartingPort# = 30h', '#Port = 10', 可以訪問
      
      端口
      30h - 39h), 'ToggleOn-Off' 如果是 TRUE(1) 就能夠訪問, 是 FALSE(0) 就不能訪問
      
      .
      一旦調用了 ioperm(), 要求的端口就和平常一樣訪問. 程序可以調用 ioperm() 任意多
      
      次,
      而不需要在后來調用 ioperm()(但下面的例子這樣做了), 因為系統會處理這些.
      ; io.asm ===================================================================
      
      =
      BITS 32
      GLOBAL szHello
      GLOBAL main
      EXTERN printf
      EXTERN ioperm
      SECTION .data
      szText1 db 'Enabling I/O Port Access',0Ah,0Dh,0
      szText2 db 'Disabling I/O Port Acess',0Ah,0Dh,0
      szDone db 'Done!',0Ah,0Dh,0
      szError db 'Error in ioperm() call!',0Ah,0Dh,0
      szEqual db 'Output/Input bytes are equal.',0Ah,0Dh,0
      szChange db 'Output/Input bytes changed.',0Ah,0Dh,0
      SECTION .text
      main:
      push dword szText1
      call printf
      pop ecx
      enable_IO:
      push word 1 ; enable mode
      push dword 04h ; four ports
      push dword 40h ; start with port 40
      call ioperm ; Must be SUID "root" for this call!
      add ESP, 10 ; cleanup stack (method 1)
      cmp eax, 0 ; check ioperm() results
      jne Error
      ;---------------------------------------Port Programming Part--------------
      
      SetControl:
      mov al, 96 ; R/W low byte of Counter2, mode 3
      out 43h, al ; port 43h = control register
      WritePort:
      mov bl, 0EEh ; value to send to speaker timer
      mov al, bl
      out 42h, al ; port 42h = speaker timer
      ReadPort:
      in al, 42h
      cmp al, bl ; byte should have changed--this IS a timer
      jne ByteChanged
      BytesEqual:
      push dword szEqual
      call printf
      pop ecx
      jmp disable_IO
      ByteChanged:
      push dword szChange
      call printf
      pop ecx
      ;---------------------------------------End Port Programming Part----------
      
      disable_IO:
      push dword szText2
      call printf
      pop ecx
      push word 0 ; disable mode
      push dword 04h ; four ports
      push dword 40h ; start with port 40h
      call ioperm
      pop ecx ;cleanup stack (method 2)
      pop ecx
      pop cx
      cmp eax, 0 ; check ioperm() results
      jne Error
      jmp Exit
      Error:
      push dword szError
      call printf
      pop ecx
      Exit:
      ret
      ; EOF ======================================================================
      
      
      在 Linux 下使用中斷
      -------------------------
      Linux 是一個運行在保護模式下的共享庫的環境, 意味著沒有中斷服務, Right?
      錯了. 我注意到在 GAS 的例子源碼中用了 INT 80, 注釋是 "sys_write(ebx, ecx, ed
      
      x)".
      這個函數是 Linux 系統調用接口的一部分, 意思是 INT 80 必須是到達系統調用服務
      
      的門戶. 在 Linux 源碼中到處看時(忽略從不要使用 INT 80 接口的警告, 因為函數號
      
      
      可能隨時改變), 我發現 "系統調用號(system call numbers)" -- 就是說, 傳給 INT
      
      80
      的 # 對應著一個系統調用子程序 -- 在 UNISTD.H 中. 一共有 189 個, 所以我不會在
      
      
      這里列出來...但如果你在 Linux 做匯編, 給自己做個好事, 打印出來吧.
      當調用 INT 80 時, eax 設為用調用的功能號. 傳給系統調用則程序的參數必須按順序
      
      
      放在下列寄存器中:
      ebx, ecx, edx, esi, edi
      這樣, 第一個參數就在 ebx 里, 第二個在 ecx 里... 注意在一個系統調用程序里, 不
      
      是
      用棧來傳遞參數. 調用的返回值在 eax 里.
      還有, INT 80 接口和一般的調用一樣. 下面的這個程序就演示了 INT 80h 的使用. 這
      
      個
      程序檢查并顯示了它自己的 PID. 注意 使用 printf() 格式化字符串 -- 這個調用的
      
      C 結構
      是:
      printf( "%dn", curr_PID);
      也要注意結束符在匯編里不一定可靠, 我常用十六進制(0Ah, 0Dh)代表 CRLF.
      ;pid.asm====================================================================
      
      
      BITS 32
      GLOBAL main
      EXTERN printf
      SECTION .data
      szText1 db 'Getting Current Process ID...',0Ah,0Dh,0
      szDone db 'Done!',0Ah,0Dh,0
      szError db 'Error in int 80!',0Ah,0Dh,0
      szOutput db '%d',0Ah,0Dh,0 ;printf() 的格式字符串
      SECTION .text
      main:
      push dword szText1 ;開始信息
      call printf
      pop ecx
      GetPID:
      mov eax, dword 20 ; getpid() 系統調用
      int 80h ; 系統調用中斷
      cmp eax, 0 ; 沒有 PID 0 !
      jb Error
      push eax ; 把返回值傳遞給 printf
      push dword szOutput ; 把格式字符串傳遞給 printf
      call printf
      pop ecx ; 清除棧
      pop ecx
      push dword szDone ; 結束信息
      call printf
      pop ecx
      jmp Exit
      Error:
      push dword szError
      call printf
      pop ecx
      Exit:
      ret
      ; EOF =====================================================================
      
      最后的話
      -----------
      大多數的麻煩來自對 Nasm 的習慣上. 而 nasm 帶有手冊, 但缺省是不安裝的,
      所以你必須把它從
      /user/local/bin/nasm-0.97/nasm.man
      移(cp 或 mv)到
      /usr/local/man/man1/nasm.man.
      格式有點亂, 可以很簡單的用 nroff 指示符來解決. 但它不會給你 Nasm 的整個文
      檔; 要解決這個問題, 把 nasmdoc.txt 從
      /usr/local/bin/nasm-0.97/doc/nasmdoc.txt
      拷貝到
      /usr/local/man/man1/nasmdoc.man
      現在你可以用 'man nasm', ' man nasmdoc' 來看 nasm 的手冊和文檔了
      想得到更多的信息, 查查這里:
      Linux Assembly Language HOWTO (Linux 匯編語言 HOWTO)
      Linux I/O Port Programming Mini-HOWTO (Linux I/O 端口編程 Mini-HOWTO)
      Jan's Linux & Assembler HomePage (http://www.bewoner.dma.be/JanW/eng.html'

    文章來源于領測軟件測試網 http://www.kjueaiud.com/


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備2023014753號-2
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>