亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

如何調用C標準庫的exit函數詳解

2020-01-26 13:26:05
字體:
來源:轉載
供稿:網友

編譯大于運算符

原定的計劃中這一篇應當是要講如何編譯if表達式的,但是我發現沒什么東西可以作為if的test-form的部分的表達式,所以覺得,要不還是先實現一下比較兩個數字這樣子的功能吧。說干就干,我決定用大于運算符來作為例子――大于運算符就是指>啦。所以,我的目標是要編譯下面這樣的代碼

(> 1 2)

并且比較之后的結果要放在EAX寄存器中。鑒于現在這門語言還非常地簡陋,沒有布爾類型這樣子的東西,所以在此仿照C語言的處置方式,以數值0表示邏輯假,其它的值表示邏輯真。所以上面的表達式在編譯成匯編代碼并最終運行后,應當可以看到EAX寄存器中的值為0。

為了編譯大于運算符,并且將結果放入到EAX寄存器中,需要用到新的指令CMP、JG,以及JMP了。我的想法是,先將第一個操作數放入到EAX寄存器,將第二個操作數放入到EBX寄存器。然后,使用CMP指令比較這兩個寄存器。如果EAX中的數值大于EBX,那么就使用JG指令跳到一個MOV指令上,這道MOV會將寄存器EAX的值修改為1;否則,JG不被執行,執行后續的一道MOV指令,將數值0寫入到EAX寄存器,然后使用JMP跳走,避免又執行到了剛才的第一道MOV指令。思路還是挺簡單的。

在修改jjcc2之前,還需要在inside-out/aux中對>予以支持,但沒什么特別的,就是往member的參數中加入>這個符號而已。之后,將jjcc2改為如下的形式

(defun jjcc2 (expr globals) "支持兩個數的四則運算的編譯器" (check-type globals hash-table) (cond ((eq (first expr) '+)  `((movl ,(get-operand expr 0) %eax)  (movl ,(get-operand expr 1) %ebx)  (addl %ebx %eax))) ((eq (first expr) '-)  `((movl ,(get-operand expr 0) %eax)  (movl ,(get-operand expr 1) %ebx)  (subl %ebx %eax))) ((eq (first expr) '*)  ;; 將兩個數字相乘的結果放到第二個操作數所在的寄存器中  ;; 因為約定了用EAX寄存器作為存放最終結果給continuation用的寄存器,所以第二個操作數應當為EAX  `((movl ,(get-operand expr 0) %eax)  (movl ,(get-operand expr 1) %ebx)  (imull %ebx %eax))) ((eq (first expr) '/)  `((movl ,(get-operand expr 0) %eax)  (cltd)  (movl ,(get-operand expr 1) %ebx)  (idivl %ebx))) ((eq (first expr) 'progn)  (let ((result '()))  (dolist (expr (rest expr))  (setf result (append result (jjcc2 expr globals))))  result)) ((eq (first expr) 'setq)  ;; 編譯賦值語句的方式比較簡單,就是將被賦值的符號視為一個全局變量,然后將eax寄存器中的內容移動到這里面去  ;; TODO: 這里expr的second的結果必須是一個符號才行  ;; FIXME: 不知道應該賦值什么比較好,先隨便寫個0吧  (setf (gethash (second expr) globals) 0)  (values (append (jjcc2 (third expr) globals)    ;; 為了方便stringify函數的實現,這里直接構造出RIP-relative形式的字符串    `((movl %eax ,(get-operand expr 0))))   globals)) ((eq (first expr) '_exit)  ;; 因為知道_exit只需要一個參數,所以將它的第一個操作數塞到EDI寄存器里面就可以了  ;; TODO: 更好的寫法,應該是有一個單獨的函數來處理這種參數傳遞的事情(以符合calling convention的方式)  `((movl ,(get-operand expr 0) %edi)  (movl #x2000001 %eax)  (syscall))) ((eq (first expr) '>)  ;; 為了可以把比較之后的結果放入到EAX寄存器中,以我目前不完整的匯編語言知識,可以想到的方法如下  (let ((label-greater-than (intern (symbol-name (gensym)) :keyword))  (label-end (intern (symbol-name (gensym)) :keyword)))  ;; 根據這篇文章(https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow#Comparison_Instructions)中的說法,大于號左邊的數字應該放在CMP指令的第二個操作數中,右邊的放在第一個操作數中  `((movl ,(get-operand expr 0) %eax)  (movl ,(get-operand expr 1) %ebx)  (cmpl %ebx %eax)  (jg ,label-greater-than)  (movl $0 %eax)  (jmp ,label-end)  ,label-greater-than  (movl $1 %eax)  ,label-end)))))

然后便可以在REPL中運行下列代碼了

(let* ((ht (make-hash-table)) (asm (jjcc2 (inside-out '(_exit (> 1 2))) ht))) (stringify asm ht))

輸出的匯編代碼為

 .dataG809: .long 0 .section __TEXT,__text,regular,pure_instructions .globl _main_main: MOVL $1, %EAX MOVL $2, %EBX CMPL %EBX, %EAX JG G810 MOVL $0, %EAX JMP G811G810: MOVL $1, %EAXG811: MOVL %EAX, G809(%RIP) MOVL G809(%RIP), %EDI MOVL $33554433, %EAX SYSCALL

編譯鏈接運行后,就可以得到預期的結果了。下面開始本文的正文

調用C標準庫的exit函數

在上面的介紹中,實現了對大于號(>)的處理,那么對if表達式的編譯也就是信手拈來的事了,不解釋太多。在本篇中,將會講述一下如何產生可以調用來自于C語言標準庫的exit(3)函數的匯編代碼。

在Common Lisp中并沒有一個叫做EXIT的內置函數,所以如同之前實現的_exit一樣,我會新增一種需要識別的(first expr),即符號exit。為了可以調用C語言標準庫中的exit函數,需要遵循調用約定。對于exit這種只有一個參數的函數而言,情形比較簡單,只需要跟對_exit一樣處理即可。剛開始,我寫下的代碼是這樣的

(defun jjcc2 (expr globals) ;; 省略不必要的內容 (cond ;; 省略不必要的內容 ((member (first expr) '(_exit exit))  ;; 暫時以硬編碼的方式識別一個函數是否來自于C語言的標準庫  `((movl ,(get-operand expr 0) %edi)  (call :|_exit|)))))

對(exit 1)進行編譯,會得到如下的代碼

 .data .section __TEXT,__text,regular,pure_instructions .globl _main_main: MOVL $1, %EDI CALL _exit

不過這樣的代碼經過編譯鏈接之后,一運行就會遇到段錯誤(segmentation fault)。經過一番放狗搜索后,才知道原來在macOS上調用C函數的時候,需要先將棧對齊到16字節――我將其理解為將指向棧頂的指針對齊到16字節。于是乎,我將jjcc2修改為如下的形式

(defun jjcc2 (expr globals) ;; 省略不必要的內容 (cond ;; 省略不必要的內容 ((member (first expr) '(_exit exit))  ;; 暫時以硬編碼的方式識別一個函數是否來自于C語言的標準庫  `((movl ,(get-operand expr 0) %edi)  ;; 據這篇回答(https://stackoverflow.com/questions/12678230/how-to-print-argv0-in-nasm)所說,在macOS上調用C語言函數,需要將棧對齊到16位  ;; 假裝要對齊的是棧頂地址。因為棧頂地址是往低地址增長的,所以只需要將地址的低16位抹掉就可以了  (and ,(format nil "$0x~X" #XFFFFFFF0) %esp)  (call :|_exit|)))))

結果發現還是不行。最后,實在沒轍了,只好先寫一段簡單的C代碼,然后用gcc -S生成匯編代碼,來看看究竟應當如何處理這個棧的對齊要求。一番瞎折騰之后,發現原來是要處理RSP寄存器而不是ESP寄存器――我也不曉得這是為什么,ESP不就是RSP的低32位而已么。

最后,把jjcc2寫成下面這樣后,終于可以成功編譯(exit 1)了

(defun jjcc2 (expr globals) "支持兩個數的四則運算的編譯器" (check-type globals hash-table) (cond ((eq (first expr) '+)   `((movl ,(get-operand expr 0) %eax)   (movl ,(get-operand expr 1) %ebx)   (addl %ebx %eax)))  ((eq (first expr) '-)   `((movl ,(get-operand expr 0) %eax)   (movl ,(get-operand expr 1) %ebx)   (subl %ebx %eax)))  ((eq (first expr) '*)   ;; 將兩個數字相乘的結果放到第二個操作數所在的寄存器中   ;; 因為約定了用EAX寄存器作為存放最終結果給continuation用的寄存器,所以第二個操作數應當為EAX   `((movl ,(get-operand expr 0) %eax)   (movl ,(get-operand expr 1) %ebx)   (imull %ebx %eax)))  ((eq (first expr) '/)   `((movl ,(get-operand expr 0) %eax)   (cltd)   (movl ,(get-operand expr 1) %ebx)   (idivl %ebx)))  ((eq (first expr) 'progn)   (let ((result '()))   (dolist (expr (rest expr))    (setf result (append result (jjcc2 expr globals))))   result))  ((eq (first expr) 'setq)   ;; 編譯賦值語句的方式比較簡單,就是將被賦值的符號視為一個全局變量,然后將eax寄存器中的內容移動到這里面去   ;; TODO: 這里expr的second的結果必須是一個符號才行   ;; FIXME: 不知道應該賦值什么比較好,先隨便寫個0吧   (setf (gethash (second expr) globals) 0)   (values (append (jjcc2 (third expr) globals)       ;; 為了方便stringify函數的實現,這里直接構造出RIP-relative形式的字符串       `((movl %eax ,(get-operand expr 0))))     globals))  ;; ((eq (first expr) '_exit)  ;; ;; 因為知道_exit只需要一個參數,所以將它的第一個操作數塞到EDI寄存器里面就可以了  ;; ;; TODO: 更好的寫法,應該是有一個單獨的函數來處理這種參數傳遞的事情(以符合calling convention的方式)  ;; `((movl ,(get-operand expr 0) %edi)  ;; (movl #x2000001 %eax)  ;; (syscall)))  ((eq (first expr) '>)   ;; 為了可以把比較之后的結果放入到EAX寄存器中,以我目前不完整的匯編語言知識,可以想到的方法如下   (let ((label-greater-than (intern (symbol-name (gensym)) :keyword))    (label-end (intern (symbol-name (gensym)) :keyword)))   ;; 根據這篇文章(https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow#Comparison_Instructions)中的說法,大于號左邊的數字應該放在CMP指令的第二個操作數中,右邊的放在第一個操作數中   `((movl ,(get-operand expr 0) %eax)    (movl ,(get-operand expr 1) %ebx)    (cmpl %ebx %eax)    (jg ,label-greater-than)    (movl $0 %eax)    (jmp ,label-end)    ,label-greater-than    (movl $1 %eax)    ,label-end)))  ((eq (first expr) 'if)   ;; 假定if語句的測試表達式的結果也是放在%eax寄存器中的,所以只需要拿%eax寄存器中的值跟0做比較即可(類似于C語言)   (let ((label-else (intern (symbol-name (gensym)) :keyword))    (label-end (intern (symbol-name (gensym)) :keyword)))   (append (jjcc2 (second expr) globals)     `((cmpl $0 %eax)      (je ,label-else))     (jjcc2 (third expr) globals)     `((jmp ,label-end)      ,label-else)     (jjcc2 (fourth expr) globals)     `(,label-end))))  ((member (first expr) '(_exit exit))   ;; 暫時以硬編碼的方式識別一個函數是否來自于C語言的標準庫   `((movl ,(get-operand expr 0) %edi)   ;; 據這篇回答(https://stackoverflow.com/questions/12678230/how-to-print-argv0-in-nasm)所說,在macOS上調用C語言函數,需要將棧對齊到16位   ;; 假裝要對齊的是棧頂地址。因為棧頂地址是往低地址增長的,所以只需要將地址的低16位抹掉就可以了   (and ,(format nil "$0x~X" #XFFFFFFFFFFFFFFF0) %rsp)   (call :|_exit|)))))

生成的匯編代碼如下

  .data  .section __TEXT,__text,regular,pure_instructions  .globl _main_main:  MOVL $1, %EDI  AND $0xFFFFFFFFFFFFFFF0, %RSP  CALL _exit

好了,這個時候我就在想,如果想要支持其它來自C語言標準庫的函數的話,只要依葫蘆畫瓢就好了,好像還挺簡單的――天真的我如此天真地想著。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩在线观看免费高清完整版| 亚洲欧美日韩精品久久奇米色影视| 亚洲视频电影图片偷拍一区| 精品久久久视频| 久久久在线免费观看| 91国产美女在线观看| 国产成人精品a视频一区www| 色爱精品视频一区| 欧美日韩不卡合集视频| 久久久久久九九九| 欧美性jizz18性欧美| 久久九九国产精品怡红院| 欧美日韩色婷婷| 亚洲自拍小视频免费观看| 欧美日韩国产精品一区二区三区四区| 成人欧美一区二区三区在线湿哒哒| 亚洲片国产一区一级在线观看| 日韩av中文在线| 国产中文日韩欧美| 美日韩丰满少妇在线观看| 欧美在线一区二区视频| 在线观看欧美成人| 日韩成人在线网站| 亚洲福利在线视频| 亚洲国产毛片完整版| 久久人人爽人人爽爽久久| 日韩在线小视频| 久久精品免费播放| 日韩中文字幕久久| 亚洲国产福利在线| 欧美成人精品一区| 日韩在线视频一区| 黑人巨大精品欧美一区二区一视频| 亚洲国内精品在线| 色琪琪综合男人的天堂aⅴ视频| 国产精品成人久久久久| 国产久一一精品| 亚洲精品免费在线视频| zzjj国产精品一区二区| 一区二区三区国产视频| 国产成人啪精品视频免费网| 欧美日韩激情视频8区| 欧美大片大片在线播放| 欧美一级淫片videoshd| 中文字幕久久精品| 欧美专区中文字幕| 日韩av黄色在线观看| 国产精品人人做人人爽| 国产99视频精品免视看7| 亚洲精品永久免费精品| 亚洲人成网在线播放| 亚洲视频自拍偷拍| 久久精品91久久香蕉加勒比| 国产精品亚洲自拍| 日韩高清人体午夜| 91亚洲国产成人久久精品网站| 亚洲欧美日本伦理| 亚洲丝袜一区在线| 日韩免费高清在线观看| 国产精品草莓在线免费观看| xxxxxxxxx欧美| 国产精品永久免费在线| 日韩av一区在线| 红桃av永久久久| 欧美日韩在线看| 亚洲欧洲一区二区三区久久| 国产一区二区丝袜高跟鞋图片| 国产精品久久久久久久久免费| 国产女同一区二区| 亚洲第一精品夜夜躁人人爽| 亚洲白拍色综合图区| 欧洲亚洲在线视频| 中文字幕日韩av| 亚洲有声小说3d| 92看片淫黄大片看国产片| 久久久久久国产| 欧美日韩国产中字| 日韩中文字幕在线精品| 欧美做爰性生交视频| 国产精品精品一区二区三区午夜版| 久久久久久亚洲精品不卡| 国产精品久久久av| 久久久久久久久久国产精品| 国产成人综合亚洲| 亚洲精品视频二区| 亚洲精品中文字幕女同| 欧美成人高清视频| 成人免费淫片aa视频免费| 成人性生交大片免费看视频直播| 国产精品第8页| 国产mv免费观看入口亚洲| 国产精品电影一区| 日韩男女性生活视频| 欧美性猛交xxxx免费看久久久| 永久免费毛片在线播放不卡| 欧美第一页在线| 日韩一区av在线| 97精品国产91久久久久久| 国产精品女人久久久久久| 国内揄拍国内精品少妇国语| 国产精品久久久久福利| 91免费视频国产| 日本精品视频在线播放| 久久精彩免费视频| 国产精品99久久久久久人| 久久久久久国产精品| 在线播放国产一区中文字幕剧情欧美| 亚洲欧美综合图区| 日韩久久精品电影| 久久777国产线看观看精品| 九九九热精品免费视频观看网站| 亚洲黄色有码视频| 欧美极品少妇全裸体| 欧美成人精品在线播放| 亚洲人成在线一二| 91精品国产乱码久久久久久蜜臀| 美女av一区二区| 国产精品视频免费观看www| 国产精品久久久久久久美男| 亚洲美女视频网| 久久亚洲国产精品成人av秋霞| 欧美午夜丰满在线18影院| 亚洲2020天天堂在线观看| 亚洲精品中文字幕有码专区| 久久精品最新地址| 成人激情综合网| 国产精品久久色| 国产欧美精品在线| 亚洲第一区第二区| 91深夜福利视频| 亚洲欧美一区二区精品久久久| 亚洲一区二区三区乱码aⅴ| 国产精品91在线观看| 美女视频黄免费的亚洲男人天堂| 欧美日韩午夜视频在线观看| 国产精品日韩在线播放| 668精品在线视频| 亚洲三级黄色在线观看| 精品日本美女福利在线观看| 亚洲精品女av网站| 国产欧美日韩丝袜精品一区| 国产精品第一区| 国产精品91久久| 国产日韩精品在线| 国产精品一区二区三区久久| 成人精品一区二区三区电影黑人| 欧美肥老妇视频| 中文字幕免费精品一区高清| 日本精品久久中文字幕佐佐木| 中文字幕日韩欧美精品在线观看| 亚洲性视频网址| 欧美激情久久久久久| 欧美国产日韩中文字幕在线| 在线观看日韩视频| 夜夜嗨av一区二区三区免费区| 91精品国产高清久久久久久| 久久99久国产精品黄毛片入口| 欧美日韩国产综合视频在线观看中文| 777国产偷窥盗摄精品视频| 亚洲三级黄色在线观看| 欧美又大又硬又粗bbbbb| 国产精品久久久久久搜索| 国产精品久久久久久久久久久久久| 亚洲国产精品一区二区三区|