MapPtrToProcess 用法 WINCE驅動(dòng)分析3
以使用下面的應用程序代碼測試這個(gè)driver,使用evc編譯。
#include windows.h>
#includeWindev.h>
#include stdio.h>
#include "objbase.h"
#include "initguid.h"
#include "foo.h"
//char data1[10];
int WinMain(void)
{
HANDLE hnd;
COPY_STRUCT cs[1];
int i;
//static char data1[10];
auto char data1[10];
auto char data2[10];
static char* p1,*p2;
//cs.pBuffer1 = (char *)malloc(10);
//cs.pBuffer2 = (char*)malloc(10);
//cs.nLen = 10;
p1 = (char *)LocalAlloc(LPTR,10);
p2 = (char *)malloc(10);
//cs[0].pBuffer1 = (char *)malloc(10);
//cs[0].pBuffer2 = (char*)malloc(10);
cs[0].pBuffer1 = data1[0];
cs[0].pBuffer2 = data2[0];
cs[0].nLen = 10;
memset(cs[0].pBuffer1,'a',10);
hnd = CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);
if(hnd==NULL)
{
printf("Open device falied!n");
return;
}
DeviceIoControl(hnd,IOCTL_FOO_XER,cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);
//for(i=0;i9;i++)
//{
//printf(" %c",*(cs.pBuffer2++));
//}
printf("n");
CloseHandle(hnd);
// free(cs[0].pBuffer1);
// free(cs[0].pBuffer2);
}
可以通過(guò)evc的單步調試看結果。好了一切都完成了,我們來(lái)看看系統是怎么工作的吧,從應用程序開(kāi)始,
CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);
會(huì )調用到
FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode)
而FOO_DEV_NAME名字定義在foo.h里面。
#define FOO_DEV_NAME L"Foo1:"
注意后面是 1 ,這個(gè)是和注冊表的這一項匹配的
"Index"=dword:1
當調用CreateFile發(fā)生了什么,slot之間的轉換,一系列系統操作后,調用到我們自己的driver函數FOO_Open,在這個(gè)函數里我們返回了一個(gè)句柄,它可以用來(lái)存儲我們的自己driver的信息。在其它I/O操作中可以使用。
Driver什么時(shí)候加載的?在注冊表里,device manager會(huì )一個(gè)個(gè)的加載,會(huì )調用到FOO_Init函數。這個(gè)函數返回一個(gè)指針,在調用FOO_Open又傳回來(lái)了,這樣我們就可以實(shí)現初始化一些自己driver的東西。
接著(zhù)一個(gè)重要的函數,
DeviceIoControl(hnd,IOCTL_FOO_XER,cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);
調用到
FOO_IOControl
走到這里
case IOCTL_FOO_XER:
if((pInBuf==NULL))
{
SetLastError(ERROR_INVALID_PARAMETER);
break;
}
pcs = (COPY_STRUCT*)pInBuf;
__try{
pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess());
pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess());
DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)rn"),pcs->pBuffer1,pcs->pBuffer2));
memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen);
bResult = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTLrn")));
break;
}
break;
default:
break;
這里又很多東西要研究,
從應用程序傳來(lái)的參數有, control code,IOCTL_FOO_XER和一個(gè)重要的輸入參數cs[0],它是一個(gè)指針。cs 是一個(gè)結構體,定義在FOO.H
typedef struct {
char* pBuffer1;
char* pBuffer2;
int nLen;
}COPY_STRUCT;
而且這個(gè)結構體里有兩個(gè)指針。
DeviceIoControl 傳過(guò)來(lái)的指針可以用嗎?它包含的兩個(gè)指針可以直接用嗎?
按照PB連接幫助文檔看,
The operating system (OS ) manages pointers passed directly as parameters. Drivers must map all pointers contained in structures. DeviceIoControl buffers are often structures that contain data, some of which might be pointers.
You can map a pointer contained in a structure by calling MapPtrToProcess, setting the first parameter to the pointer, and then setting the second parameter to GetCallerProcess.
cs指針已經(jīng)映射好了,但是它指向的結構里的指針我們需要自己使用MapPtrToProcess函數映射。
這也就是:
pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess());
pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess());
的由來(lái),可是后面的代碼沒(méi)有使用pMap1,pMap2。而是直接使用:
memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen);
而且它還工作了,沒(méi)有出現exception。很奇怪。我第一次在一個(gè)家伙的代碼里看見(jiàn)這種情況,很吃驚,但是它工作的很好,是文檔出錯了?
我們來(lái)分析一下,看看應用程序的代碼:
COPY_STRUCT cs[1];
auto char data1[10];
auto char data2[10];
cs結構和data1,data2數組都是自動(dòng)變量,存放在堆棧里。假設這個(gè)應用程序被加載到0x18000000位置的slot里,那么他們的地址都是0x18XXXXXX。不熟悉wince memory architecture的可以看看資料,了解一下slot。當調用了
DeviceIoControl,按照文檔的說(shuō)法,cs指針得到了轉換,因為從應用程序的進(jìn)程轉到了device.exe進(jìn)程,而device進(jìn)程又是當前的運行的進(jìn)程,被映射到了slot0,系統負責轉換cs指針。而cs包含的pBuffer1和pBuffer2是沒(méi)有映射不能直接用的。
事實(shí)上,我們傳過(guò)來(lái)的指針根本就是不需要映射,因為他們都是0x18xxxxxx,在應用程序的slot里,所以只要device.exe有訪(fǎng)問(wèn)應用程序的權限,就可以了。而這個(gè)權限,系統已經(jīng)幫我們設置好了。
那什么情況下要自己映射呢?
如果應用程序在定義 data1和data2使用static關(guān)鍵字,或者使用LocalAlloc,HeapAlloc的時(shí)候,一定要自己映射cs里的指針。
在應用程序里這樣寫(xiě):
cs.pBuffer1 = (char *)malloc(10);
cs.pBuffer2 = (char*)malloc(10);
cs.nLen = 10;
如果不使用MapPtrToProcess完成映射,那就出現data abort exception.
為什么呢?
因為這些變量都是在堆里分配的,而當應用程序運行時(shí),被映射到slot0,堆的地址也就是處于slot的范圍內,傳遞到device.exe后,device.exe被映射到了slot0,這個(gè)時(shí)候必須要將應用程序的指針映射回應用程序所在的slot。否則訪(fǎng)問(wèn)的是device.exe的空間,會(huì )發(fā)生不可知道的結果。
驗證一下上面說(shuō)的地址分配問(wèn)題。
我們這樣定義
COPY_STRUCT cs[1];
static char data1[10]; 堆里
auto char data2[10]; 棧里
這樣賦值:
cs[0].pBuffer1 = data1[0];
cs[0].pBuffer2 = data2[0];
cs[0].nLen = 10;
調試信息:
cs[0].pBuffer1 = data1[0];
180112D0 ldr r2, [pc, #0xD0]
180112D4 str r2, [sp, #0x10]
讀取data1[0]使用的是PC作為基址,而此時(shí)的應用程序處于運行階段映射到slot0,那么pc也就在0~01ffffff范圍,我的調試結果是在0x000112D0+8,使用的是arm,流水線(xiàn)機制,當前指令地址+8才是pc值。
143: cs[0].pBuffer2 = data2[0];
180112D8 add r0, sp, #0x20
180112DC str r0, [sp, #0x14]
讀取data2[0]采用的是sp作為基址,sp在應用程序加載到slot的時(shí)候就確定了的。所以保持了在應用程序slot的值,處于0x18xxxxxx范圍。
我們看到因為wince的slot機制,我們有時(shí)候需要映射,有時(shí)候不需要。所以wince文檔說(shuō)結構里的指針要映射。畢竟你不知道應用程序怎么寫(xiě)。
當然,你可以根本不映射,只要把那個(gè)結構屏蔽調,寫(xiě)一個(gè)STATIC LIBRARY給用戶(hù)使用,自己保證使用正確的地址分配就可以了。上面我說(shuō)的那個(gè)家伙就是這么干的。
好了,接著(zhù)調用:
CloseHandle(hnd);
程序結束了,完成了一次簡(jiǎn)單的拷貝。
這個(gè)框架完成了,driver的基本接口設計,強調了內存指針的使用問(wèn)題。但是相對于一個(gè)真正的driver,還缺少點(diǎn)東西,就是訪(fǎng)問(wèn)硬件的方法。下面繼續討論如何訪(fǎng)問(wèn)硬件
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)
評論