加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 12.4  C、C++ 和 ARM 匯編語言之間的調用
  • 相關推薦
  • 電子產業(yè)圖譜
申請入駐 產業(yè)圖譜

混合使用C、C++和匯編語之: C、C++ 和 ARM 匯編語言之間的調用

2013/09/30
1
閱讀需 21 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

 

12.4  C、C++ 和 ARM 匯編語言之間的調用

本節(jié)提供一些示例,顯示如何從C++調用C和匯編語言代碼,以及從C和匯編語言調用 C++ 代碼。其中包括調用約定和數據類型。主要包括下面內容:

·  相互調用的一般規(guī)則;

·  C++語言的特定信息;

·  調用示例。

只要遵循正確的過程調用標準AAPCS,就可以混合調用C、C++和匯編語言例程。有關 AAPCS 的更多信息,請參閱ARM相關文檔。

12.4.1  相互調用的一般規(guī)則

以下一般規(guī)則適用于C、C++和匯編語言之間的調用。有關的詳細信息,請參閱ARM開發(fā)相關文檔。

嵌入式匯編程序以及其與ARM嵌入式應用程序二進制接口(BSABI,Application Binary Interface for the ARM Architecture)的兼容使得混合語言編程更易于實現(xiàn)。它們可提供以下功能:

·  使用__cpp關鍵字進行名稱延伸;

·  傳遞隱含this參數的方式;

·  調用虛函數的方式;

·  引用的表示;

·  具有基類或虛成員函數的C++類的類型布局;

·  非POD(Plain Old Data)結構的類對象傳遞。

以下一般規(guī)則適用于混合語言編程:

·  使用C調用約定。

·  在C++中,非成員函數可以聲明為extern "C",以指定它們有C鏈接。帶有C鏈接意味著定義函數的符號未延伸。C鏈接可以用于以一種語言實現(xiàn)函數,然后用另一種語言調用它。

·  匯編語言模塊所必須符合的AAPCS調用標準,應當適合于應用程序所使用的存儲器模型。

以下規(guī)則適用于從C和匯編語言調用C++函數:

·  要調用全局(非成員)C++函數,應將它聲明為extern "C",以提供C鏈接。

·  成員函數(靜態(tài)和非靜態(tài))總是有已延伸的名稱。使用嵌入式匯編程序的__cpp關鍵字,可以不必手工尋找已延伸的名稱。

·  不能從C調用C++內聯(lián)函數,除非確保C++編譯器生成了函數的外聯(lián)副本。例如,取得函數地址將導致生成外聯(lián)副本。

·  非靜態(tài)成員函數接受隱含this參數作為r0中的第一個自變量,或作為r1中第二個自變量(如果函數返回非int類結構)。靜態(tài)成員函數不接受隱含this參數。

12.4.2  C++的特定信息

本節(jié)主要介紹一些專門適用于C++的內容。

(1)C++調用約定

ARM C++使用與ARM C相同的調用約定,但在下面的情況下,調用規(guī)則有所不同:

·  調用非靜態(tài)成員函數時,隱含的this參數是第一個自變量,或者是第二個自變量(如果被調用函數返回非int類的struct)。這可能在將來的版本中有所變化。

(2)C++數據類型

ARM C++使用與ARM C相同的數據類型,但在以下幾種情況下,情況有所不同:

·  如果struct或class類型的C++對象沒有基類或虛函數,則它們的布局與ARM C相同。如果這樣的struct沒有用戶定義的復制賦值運算符或用戶定義的析構函數,則它是POD結構。

·  引用表示為指針。

·  C函數指針和C++(非成員)函數指針沒有區(qū)別。

(3)符號名稱延伸

鏈接程序將取消信息中符號名稱的延伸。

在C++程序中,C名稱必須聲明為extern "C"。ARM ISO C頭文件已經完成此操作。詳細信息請參閱ARM相關文檔。

12.4.3  混合編程調用舉例

匯編程序、C程序以及C++程序相互調用時,要特別注意遵守相應的AAPCS。下面一些例子具體說明了在這些混合調用中應注意遵守的AAPCS規(guī)則。這些示例程序默認為使用非軟件棧檢查的ATPCS規(guī)則,因為它們執(zhí)行棧操作時不檢查棧溢出。

(1)從C調用匯編語言

下面的程序顯示如何在C程序中調用匯編語言子程序,該段代碼實現(xiàn)了將一個字符串復制到另一個字符串。

#include <stdio.h>

extern void strcopy(char *d, const char *s);

int main()

{   const char *srcstr = "First string - source ";

    char dststr[] = "Second string - destination ";

/* 下面將dststr作為數組進行操作 */

    printf("Before copying:n");

    printf("  %sn  %sn",srcstr,dststr);

    strcopy(dststr,srcstr);

    printf("After copying:n");

    printf("  %sn  %sn",srcstr,dststr);

    return (0);

}

下面為調用的匯編程序。

    PRESERVE8

    AREA    SCopy, CODE, READONLY

    EXPORT strcopy

Strcopy ;r0指向目的字符串

;r1指向源字符串

    LDRB r2, [r1],#1 ;加載字節(jié)并更新源字符串指針地址

    STRB r2, [r0],#1 ;存儲字節(jié)并更新目的字符串指針地址

    CMP r2, #0 ;判斷是否為字符串結尾

    BNE strcopy ;如果不是,程序跳轉到strcopy繼續(xù)拷貝

    MOV pc,lr ;程序返回

    END

按以下步驟從命令行編譯該示例:

① 輸入armasm -g scopy.s編譯匯編語言源代碼。

② 輸入armcc -c -g strtest.c編譯C源代碼。

③ 輸入armlink strtest.o scopy.o -o strtest鏈接目標文件。

④ 將ELF/DWARF2兼容調試器與相應調試目標配合使用,運行映像。

 

(2)匯編語言調用C程序

下面的例子顯示了如何從匯編語言調用C程序。

下面的子程序段定義了C語言函數。

int g(int a, int b, int c, int d, int e) 

{

                return a + b + c + d + e;

}

下面的程序段顯示了匯編語言調用。假設程序進入f時,r0中的值為i。

  ; int f(int i) { return g(i, 2*i, 3*i, 4*i, 5*i); }

  PRESERVE8

  EXPORT f

  AREA f, CODE, READONLY

  IMPORT g // 聲明C程序g()

  STR lr, [sp, #-4]! // 保存返回地址 lr

  ADD r1, r0, r0 // 計算2*i(第2個參數)

  ADD r2, r1, r0 // 計算3*i(第3個參數)

  ADD r3, r1, r2 // 計算5*i

  STR r3, [sp, #-4]! // 第五個參數通過堆棧傳遞

  ADD r3, r1, r1 // 計算4*i(第4個參數)

  BL g // 調用C程序

  ADD sp, sp, #4 // 從堆棧中刪除第5個參數

  LDR pc, [sp], #4 // 返回

  END

(3)從C++調用C

下面的例子顯示了如何從C++程序中調用C函數。

下面的C++程序調用了C程序。

struct S { // 本結構沒有基類和虛函數

      S(int s):i(s) { }

      int i;

};

extern "C" void cfunc(S *); 

// 被調用的C函數使用extern“C”聲明

int f(){

       S s(2); // 初始化 's'

       cfunc(&s); // 調用C函數 'cfunc' 將改變 's'

       return si*3;

}

下面顯示了被調用的C程序代碼。

struct S {

      int i;

};

void cfunc(struct S *p) {

/*定義被調用的C功能 */

      p->i += 5;

}

(4)從C++中調用匯編

下面的例子顯示了如何從C++中調用匯編程序。

下面的例子為調用匯編程序的C++代碼。

struct S { // 本結果沒有基類和虛擬函數

// 

      S(int s) : i(s) { }

      int i;

};

extern "C" void asmfunc(S *); // 聲明被調用的匯編函數

int f() {

       S s(2); // 初始化結構體 's'

       asmfunc(&s); // 調用匯編子程序 'asmfunc' 

       return s.i * 3;

}

下面是被調用的匯編程序。

     PRESERVE8

     AREA Asm, CODE

     EXPORT asmfunc

asmfunc                // 被調用的匯編程序定義

     LDR r1, [r0]       

     ADD r1, r1, #5

     STR r1, [r0]

     MOV pc, lr

     END

(5)從C中調用C++

下面的例子顯示了如何從C++代碼中調用C程序。

下面的代碼顯示了被調用C++代碼。

struct S {        // 本結構沒有基類和虛擬函數

      S(int s) : i(s) { }

      int i;

};

extern "C" void cppfunc(S *p) {    

// 定義被調用的C++代碼

// 連接了C功能

      p->i += 5;                //

}

調用了C++代碼的C函數。

struct S {

      int i;

};

extern void cppfunc(struct S *p); 

/* 聲明將會被調用的C++功能 */

int f(void) {

       struct S s;

       s.i = 2;                /* 初始化S */

       cppfunc(&s);            /* 調用cppfunc函數,該函數可能改變S的值 */

       return s.i * 3;

}

 

(6)從匯編中調用C++程序

下面的代碼顯示了如何從匯編中調用C++程序。

下面是被調用的C++程序。

struct S {           // 本結構沒有基類和虛擬函數

      S(int s) : i(s) { }

      int i;

};

extern "C" void cppfunc(S * p) {

// 定義被調用的C++功能

// 功能函數體

      p->i += 5;

}

在匯編語言中,聲明要調用的C++功能,使用帶連接的跳轉指令調用C++功能。

    AREA Asm, CODE

    IMPORT cppfunc ;聲明被調用的 C++ 函數名

    EXPORT   f

f

    STMFD  sp!,{lr}

    MOV    r0,#2

    STR    r0,[sp,#-4]! ;初始化結構體

    MOV    r0,sp ;調用參數為指向結構體的指針

    BL     cppfunc ;調用C++功能'cppfunc' 

    LDR    r0, [sp], #4

    ADD    r0, r0, r0,LSL #1

    LDMFD  sp!,{pc}

    END

(7)在C和C++函數間傳遞參數

下面的例子顯示了如何在C和C++函數間傳遞參數。

下面的代碼為C++函數。

extern "C" int cfunc(const int&); 

// 聲明被調用的C函數

extern "C" int cppfunc(const int& r) {

// 定義將被C調用的C++函數

      return 7 * r;

}

int f() {

      int i = 3;

      return cfunc(i); // 相C函數傳參

}

下面為C函數。

extern int cppfunc(const int*);    

/* 聲明將被調用的C++函數 */

int cfunc(const int *p) {       

/*定義被C++調用的C函數*/

     int k = *p + 4;

     return cppfunc(&k);

}

(8)從C或匯編語言調用C++

下面的例子綜合顯示了如何從C或匯編語言中調用非靜態(tài)、非虛的C++成員函數。可以使用編譯器編譯出的匯編程序查找已延伸的函數名。

下面是被調用的C++成員函數。

struct T {

      T(int i) : t(i) { }

      int t;

      int f(int i);

};

int T::f(int i) { return i + t; }   

// 定義將被C調用的C++功能函數

extern "C" int cfunc(T*);

// 聲明將被C++調用的C函數

int f() {

      T t(5);                    // create an object of type T

      return cfunc(&t);

}

下面為調用C++的C語言函數。

struct T;

extern int _ZN1T1fEi(struct T*, int);

      /* 被調用的C++函數名 */

int cfunc(struct T* t) {   

/* 定義被C++調用的C函數 */

      return 3 * _ZN1T1fEi(t, 2);    /* 實現(xiàn)3乘以t->f(2)功能 */

}

下面為調用C++的匯編函數。

     EXPORT cfunc

     AREA foo, CODE

     IMPORT  _ZN1T1fEi

cfunc

     STMFD   sp!,{lr} ;此時r0已經包含了指向對象的指針

     MOV r1, #2

     BL _ZN1T1fEi

     ADD r0, r0, r0, LSL #1 ;r0乘以3

     LDMFD sp!,{pc}

     END

下面的例子顯示了如何用嵌入式匯編語言實現(xiàn)上面的例子。在此例中,使用 __cpp 關鍵字引用該函數。因此,用戶不必了解已延伸的函數名。

struct T {

     T(int i) : t(i) { }

     int t;

     int f(int i);

};

int T::f(int i) { return i + t; }

// 定義被C++調用的匯編功能

__asm int asm_func(T*) {

    STMFD sp!, {lr}

    MOV r1, #2;

    BL __cpp(T::f);

    ADD r0, r0, r0, LSL #1 ;r0乘以3

    LDMFD sp!, {pc}

}

int f() {

      T t(5); // 創(chuàng)建T類型的對象

      return asm_func(&t);

}

聯(lián)系方

Arm

Arm

ARM公司是一家知識產權(IP)供應商,主要為國際上其他的電子公司提供高性能RISC處理器、外設和系統(tǒng)芯片技術授權。目前,ARM公司的處理器內核已經成為便攜通訊、手持計算設備、多媒體數字消費品等方案的RISC標準。公司1990年11月由Acorn、Apple和VLSI合并而成。

ARM公司是一家知識產權(IP)供應商,主要為國際上其他的電子公司提供高性能RISC處理器、外設和系統(tǒng)芯片技術授權。目前,ARM公司的處理器內核已經成為便攜通訊、手持計算設備、多媒體數字消費品等方案的RISC標準。公司1990年11月由Acorn、Apple和VLSI合并而成。收起

查看更多

相關推薦

電子產業(yè)圖譜

華清遠見(www.farsight.com.cn)是國內領先嵌入師培訓機構,2004年注冊于中國北京海淀高科技園區(qū),除北京總部外,上海、深圳、成都、南京、武漢、西安、廣州均有直營分公司。華清遠見除提供嵌入式相關的長期就業(yè)培訓、短期高端培訓、師資培訓及企業(yè)員工內訓等業(yè)務外,其下屬研發(fā)中心還負責嵌入式、Android及物聯(lián)網方向的教學實驗平臺的研發(fā)及培訓教材的出版,截止目前為止已公開出版70余本嵌入式/移動開發(fā)/物聯(lián)網相關圖書。企業(yè)理念:專業(yè)始于專注 卓識源于遠見。企業(yè)價值觀:做良心教育、做專業(yè)教育,更要做受人尊敬的職業(yè)教育。