呼叫c 呼叫C標準庫的exit函式

2021-10-14 13:12:53 字數 4665 閱讀 5580

在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)

;; 據這篇回答(所說,在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))

result))

((eq (first expr) 'setq)

;; 編譯賦值語句的方式比較簡單,就是將被賦值的符號視為乙個全域性變數,然後將eax暫存器中的內容移動到這裡面去

;; todo: 這裡expr的second的結果必須是乙個符號才行

;; fixme: 不知道應該賦值什麼比較好,先隨便寫個0吧

(setf (gethash (second expr) globals) 0)

;; 為了方便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)))

;; 根據這篇文章(中的說法,大於號左邊的數字應該放在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)))

`((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)

;; 據這篇回答(所說,在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語言標準庫的函式的話,只要依葫蘆畫瓢就好了,好像還挺簡單的——天真的我如此天真地想著。

全文完閱讀原文

C如何呼叫C 的庫

前段時間遇到了乙個c呼叫c 的介面的問題,現在把思路整理一下。提供給我們的是c 標頭檔案 h 和靜態庫 a c不可以直接呼叫c 我們採用c 呼叫c 的方法,另外建乙個適配層 在我們的c 適配層中加上extern c c呼叫c 適配層,適配層呼叫c 就ok了。下面用乙個例子來說明一下 1 模擬條件on...

C 呼叫C 庫檔案

winform下呼叫dll檔案,將dll銬入bin目錄下,using system.runtime.interopservices dllimport securitymaker.dll entrypoint security make public static extern void makes...

在C 中呼叫C語言標準動態庫方法

cisco packet tracer 5.0軟體深入詳解使用教程 3 詳解網路嗅探工具的原理 sniffer wireshark 2010 年 07 月 19 日 seth c net程式設計 go to comment out mylib.dll dll implib mylib.lib myl...