2013-06-25 39 views
1

我的問題如下。我有一個代碼單元由不同的C文件,例如說在VxWorks中覆蓋函數調用

  • file1.c中
  • file2.c中
  • file3.c中

那都是用gcc編譯爲一個唯一的對象「object.o」,然後將其與其他對象鏈接,最後在VxWorks上運行可執行文件「application.out」。

因爲我在做「object.o」的單元測試,所以我的需求是通過代碼來激發所有可能的方法。具體來說,在某些情況下,爲了模擬錯誤發生,我應該執行模擬函數而不是原始函數。 假設例如有一個函數「func_caller」,我試圖測試它,在執行過程中的某個點,調用另一個函數「func_called」(聲明爲static)。

由於我不想修改原始代碼,我不知道是否有一種方式來操縱指令指針,這樣當調用「func_called」時,它實際上會執行另一個模擬函數「func_called_mock」,並且調用者「func_caller」沒有注意到任何事情。

在此先感謝。

回答

2

覆蓋函數調用的最直接方法是使用VxWorks的加載時間鏈接。請看下面的源:

file1.c中:

#include <stdio.h> 

int function1 (void); 

int function1() 
{ 
    printf ("function1 called\n"); 

    return 1; 
} 

file2.c中:

#include <stdio.h> 

int function2 (void); 

int function2() 
{ 
    printf ("function2 called\n"); 

    return 2; 
} 

file3.c中:

int function1 (void); 
int function2 (void); 

int function3 (void); 

int function3() 
{ 
    function1(); 
    function2(); 

    return 0; 
} 

mock.c:

#include <stdio.h> 

int function1 (void); 
int function2 (void); 

int function1() 
{ 
    printf ("mock function1 called\n"); 

    return 1; 
} 

int function2() 
{ 
    printf ("mock function2 called\n"); 

    return 2; 
} 

加載對象時,其功能將被添加到全局符號表中。

-> ld < file1.o 
value = 273740816 = 0x1050f410 
-> lkup "function" 
function1     0x108b0000 text  (file1.o) 
value = 0 = 0x0 
-> 

當裝載在符號表中已經使用功能的對象,每次通話將被立即解決與表中的那個符號相關聯的最後一個地址。

-> ld < file2.o 
value = 292535232 = 0x116fbbc0 
-> ld < file3.o 
value = 292537592 = 0x116fc4f8 
-> lkup "function" 
function1     0x108b0000 text  (file1.o) 
function2     0x108d0000 text  (file2.o) 
function3     0x108f0000 text  (file3.o) 
value = 0 = 0x0 
-> l function3 
      function3: 
0x108f0000 55      PUSH   EBP 
0x108f0001 89 e5     MOV   EBP, ESP 
0x108f0003 56      PUSH   ESI 
0x108f0004 57      PUSH   EDI 
0x108f0005 e8 f6 ff fb ff   CALL   function1 
0x108f000a e8 f1 ff fd ff   CALL   function2 
0x108f000f 31 c0     XOR   EAX, EAX 
0x108f0011 5f      POP   EDI 
0x108f0012 5e      POP   ESI 
0x108f0013 89 ec     MOV   ESP, EBP 
value = 0 = 0x0 
-> function3 
function1 called 
function2 called 
value = 0 = 0x0 
-> 

雖然l()有益顯示功能名稱,沒有符號被實際加載到與所述對象存儲器。而是加載與該函數關聯的最後一個地址的調用。因此,加載另一個同名的函數可能會覆蓋以前加載的函數。

-> unld "file3.o" 
value = 0 = 0x0 
-> ld < mock.o 
value = 292537592 = 0x116fc4f8 
-> ld < file3.o 
value = 292539496 = 0x116fcc68 
-> lkup "function" 
function1     0x108f0000 text  (mock.o) 
function1     0x108b0000 text  (file1.o) 
function2     0x108f0020 text  (mock.o) 
function2     0x108d0000 text  (file2.o) 
function3     0x10910000 text  (file3.o) 
value = 0 = 0x0 
-> l function3 
      function3: 
0x10910000 55      PUSH   EBP 
0x10910001 89 e5     MOV   EBP, ESP 
0x10910003 56      PUSH   ESI 
0x10910004 57      PUSH   EDI 
0x10910005 e8 f6 ff fd ff   CALL   function1 
0x1091000a e8 11 00 fe ff   CALL   function2 
0x1091000f 31 c0     XOR   EAX, EAX 
0x10910011 5f      POP   EDI 
0x10910012 5e      POP   ESI 
0x10910013 89 ec     MOV   ESP, EBP 
value = 0 = 0x0 
-> function3 
mock function1 called 
mock function2 called 
value = 0 = 0x0 
-> 

請注意,這種方法工作,被叫和主叫功能不能被編譯成同一個對象。您也可能注意到要調用的地址與符號表中的地址不匹配。這是在VxSim中執行上述操作的結果。 VxSim加載器實際上調用底層操作系統的加載器。因此,這些地址與符號表中的地址不匹配,並且該程序集反映了運行WorkBench的底層Pentium架構。

函數調用也可以通過直接操作要在內存中調用的地址來覆蓋。這個方法將取決於實現。下面,使用gcc -mlongcall選項爲PPC編譯的源代碼演示了這一點。這已經在實際的目標上運行,而不是VxSim。

-> ld < file1.o 
value = 33538216 = 0x1ffc0a8 = function1 + 0x498 
-> ld < file2.o 
value = 33548336 = 0x1ffe830 = function2 + 0x80 
-> ld < mock.o 
value = 33549600 = 0x1ffed20 = function2 + 0x570 
-> ld < file3.o 
value = 33550744 = 0x1fff198 = function2 + 0x9e8 
-> 
-> lkup "function" 
function1     0x01ffbef8 text  (mock.o) 
function1     0x01ffbc10 text  (file1.o) 
function2     0x01ffbf58 text  (mock.o) 
function2     0x01ffe7b0 text  (file2.o) 
function3     0x01ffe558 text  (file3.o) 
value = 0 = 0x0 
-> 
-> function3 
mock function1 called 
mock function2 called 
value = 0 = 0x0 
-> 
-> l function3 
         function3: 
0x1ffe558 9421ffe8 stwu  r1,-24(r1) 
0x1ffe55c 7c0802a6 mfspr  r0,LR 
0x1ffe560 93a1000c stw   r29,12(r1) 
0x1ffe564 93c10010 stw   r30,16(r1) 
0x1ffe568 93e10014 stw   r31,20(r1) 
0x1ffe56c 9001001c stw   r0,28(r1) 
0x1ffe570 7c3f0b78 or   r31,r1,r1 
0x1ffe574 3d200200 lis   r9,512 
0x1ffe578 3ba9bef8 addi  r29,r9,-16648 
0x1ffe57c 7fa803a6 mtspr  LR,r29 
value = 33547648 = 0x1ffe580 = function3 + 0x28 
-> 
-> *0x1ffe578 
function3 + 0x20 = 0x1ffe578: value = 1000980216 = 0x3ba9bef8 
-> *0x1ffe578 = 0x3ba9bc10 
function3 + 0x20 = 0x1ffe578: value = 1000979472 = 0x3ba9bc10 
-> *0x1ffe578 
function3 + 0x20 = 0x1ffe578: value = 1000979472 = 0x3ba9bc10 
-> 
-> function3 
function1 called 
mock function2 called 
value = 0 = 0x0 
-> 

顯然,直接操縱內存中的指針會很快變得單調乏味。此外,內存保護將阻止您更改在VxSim中加載的RTP或對象。 (因此,爲什麼我在實際的硬件上運行這個)。我提到了這種可能性,主要是因爲它似乎最符合您的問題陳述。

最後,對於非平凡的單元測試,您可能需要考慮專門爲該任務設計的工具。嘗試搜索「vxworks單元測試框架」。我對任何特定工具都沒有深入的經驗(並且不想遇到垃圾郵件)。也許,這裏的其他人可以提供一個很好的建議。