linux內核中的get_user和put_user
CPU平臺:arm
本文引用地址:http://www.104case.com/article/201611/319994.htm在內核空間和用戶空間交換數據時,get_user和put_user是兩個兩用的函數。相對于copy_to_user和copy_from_user(將在另一篇博客中分析),這兩個函數主要用于完成一些簡單類型變量(char、int、long等)的拷貝任務,對于一些復合類型的變量,比如數據結構或者數組類型,get_user和put_user函數還是無法勝任,這兩個函數內部將對指針指向的對象長度進行檢查,在arm平臺上只支持長度為1,2,4,8的變量。下面我具體分析,首先看get_user的定義(linux/include/asm-arm/uaccess.h):
- externint__get_user_1(void*);
- externint__get_user_2(void*);
- externint__get_user_4(void*);
- externint__get_user_8(void*);
- externint__get_user_bad(void);
- #define__get_user_x(__r2,__p,__e,__s,__i...)
- __asm____volatile__(
- __asmeq("%0","r0")__asmeq("%1","r2")//進行判斷(#define__asmeq(x,y)".ifnc"x","y";.err;.endifnt")
- "bl__get_user_"#__s//根據參數調用不同的函數,此時r0=指向用戶空間的指針,r2=內核空間的變量
- :"=&r"(__e),"=r"(__r2)
- :"0"(__p)
- :__i,"cc")
- #defineget_user(x,p)
- ({
- constregistertypeof(*(p))__user*__pasm("r0")=(p);//__p的數據類型和*(p)的指針數據類型是一樣的,__p=p,且存放在r0寄存器中
- registertypeof(*(p))__r2asm("r2");//__r2的數據類型和*(p)的數據類型是一樣的,且存放在r2寄存器中
- registerint__easm("r0");//定義__e,存放在寄存器r0,作為返回值
- switch(sizeof(*(__p))){//對__p所指向的對象長度進行檢查,并根據長度調用響應的函數
- case1:
- __get_user_x(__r2,__p,__e,1,"lr");
- break;
- case2:
- __get_user_x(__r2,__p,__e,2,"r3","lr");
- break;
- case4:
- __get_user_x(__r2,__p,__e,4,"lr");
- break;
- case8:
- __get_user_x(__r2,__p,__e,8,"lr");
- break;
- default:__e=__get_user_bad();break;//默認處理
- }
- x=__r2;
- __e;
- })
上面的源碼涉及到gcc的內聯匯編,不太了解的朋友可以參考前面的博客(http://blog.csdn.net/ce123/article/details/8209702)。繼續,跟蹤__get_user_1等函數的執行,它們的定義如下(linux/arch/arm/lib/getuser.S)。
- .global__get_user_1
- __get_user_1:
- 1:ldrbtr2,[r0]
- movr0,#0
- movpc,lr
- .global__get_user_2
- __get_user_2:
- 2:ldrbtr2,[r0],#1
- 3:ldrbtr3,[r0]
- #ifndef__ARMEB__
- orrr2,r2,r3,lsl#8
- #else
- orrr2,r3,r2,lsl#8
- #endif
- movr0,#0
- movpc,lr
- .global__get_user_4
- __get_user_4:
- 4:ldrtr2,[r0]
- movr0,#0
- movpc,lr
- .global__get_user_8
- __get_user_8:
- 5:ldrtr2,[r0],#4
- 6:ldrtr3,[r0]
- movr0,#0
- movpc,lr
- __get_user_bad_8:
- movr3,#0
- __get_user_bad:
- movr2,#0
- movr0,#-EFAULT
- movpc,lr
- .section__ex_table,"a"
- .long1b,__get_user_bad
- .long2b,__get_user_bad
- .long3b,__get_user_bad
- .long4b,__get_user_bad
- .long5b,__get_user_bad_8
- .long6b,__get_user_bad_8
- .previous
這段代碼都是單條匯編指令實現的內存操作,就不進行詳細注解了。如果定義__ARMEB__宏,則是支持EABI的大端格式代碼(http://blog.csdn.net/ce123/article/details/8457491),關于大端模式和小端模式的詳細介紹,可以參考http://blog.csdn.net/ce123/article/details/6971544。這段代碼在.section __ex_table, "a"之前都是常規的內存拷貝操縱,特殊的地方在于后面定義“__ex_table”section 。
標號1,2,...,6處是內存訪問指令,如果mov的源地址位于一個尚未被提交物理頁面的空間中,將產生缺頁異常,內核會調用do_page_fault函數處理這個異常,因為異常發生在內核空間,do_page_fault將調用search_exception_tables在“__ex_table”中查找異常指令的修復指令,在上面這段帶面的最后,“__ex_table”section 中定義了如下數據:
- .section__ex_table,"a"
- .long1b,__get_user_bad//其中1b對應標號1處的指令,__get_user_bad是1處指令的修復指令。
- .long2b,__get_user_bad
- .long3b,__get_user_bad
- .long4b,__get_user_bad
- .long5b,__get_user_bad_8
- .long6b,__get_user_bad_8
put_user用于將內核空間的一個簡單類型變量x拷貝到p所指向的用戶空間。該函數可以自動判斷變量的類型,如果執行成功則返回0,否則返回-EFAULT。下面給出它們的定義(linux/include/asm-arm/uaccess.h)。
- externint__put_user_1(void*,unsignedint);
- externint__put_user_2(void*,unsignedint);
- externint__put_user_4(void*,unsignedint);
- externint__put_user_8(void*,unsignedlonglong);
- externint__put_user_bad(void);
- #define__put_user_x(__r2,__p,__e,__s)
- __asm____volatile__(
- __asmeq("%0","r0")__asmeq("%2","r2")
- "bl__put_user_"#__s
- :"=&r"(__e)
- :"0"(__p),"r"(__r2)
- :"ip","lr","cc")
- #defineput_user(x,p)
- ({
- constregistertypeof(*(p))__r2asm("r2")=(x);
- constregistertypeof(*(p))__user*__pasm("r0")=(p);
- registerint__easm("r0");
- switch(sizeof(*(__p))){
- case1:
- __put_user_x(__r2,__p,__e,1);
- break;
- case2:
- __put_user_x(__r2,__p,__e,2);
- break;
- case4:
- __put_user_x(__r2,__p,__e,4);
- break;
- case8:
- __put_user_x(__r2,__p,__e,8);
- break;
- default:__e=__put_user_bad();break;
- }
- __e;
- })
- .global__put_user_1
- __put_user_1:
- 1:strbtr2,[r0]
- movr0,#0
- movpc,lr
- .global__put_user_2
- __put_user_2:
- movip,r2,lsr#8
- #ifndef__ARMEB__
- 2:strbtr2,[r0],#1
- 3:strbtip,[r0]
- #else
- 2:strbtip,[r0],#1
- 3:strbtr2,[r0]
- #endif
- movr0,#0
- movpc,lr
- .global__put_user_4
- __put_user_4:
- 4:strtr2,[r0]
- movr0,#0
- movpc,lr
- .global__put_user_8
- __put_user_8:
- 5:strtr2,[r0],#4
- 6:strtr3,[r0]
- movr0,#0
- movpc,lr
- __put_user_bad:
- movr0,#-EFAULT
- movpc,lr
- .section__ex_table,"a"
- .long1b,__put_user_bad
- .long2b,__put_user_bad
- .long3b,__put_user_bad
- .long4b,__put_user_bad
- .long5b,__put_user_bad
- .long6b,__put_user_bad
- .previous
評論