日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内存管理内存映射以及通过反汇编定位内存错误问题

發布時間:2023/12/20 linux 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内存管理内存映射以及通过反汇编定位内存错误问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

提到C語言,我們知道C語言和其他高級語言的最大的區別就是C語言是要操作內存的!

?????我們需要知道——變量,其實是內存地址的一個抽像名字罷了。在靜態編譯的程序中,所有的變量名都會在編譯時被轉成內存地址。機器是不知道我們取的名字的,只知道地址。

????內存的使用時程序設計中需要考慮的重要因素之一,這不僅由于系統內存是有限的(尤其在嵌入式系統中),而且內存分配也會直接影響到程序的效率。因此,我們要對C語言中的內存管理,有個系統的了解。

????在C語言中,定義了4個內存區間:代碼區;全局變量和靜態變量區;局部變量區即棧區;動態存儲區,即堆區;具體如下:

????1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。

????2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
????3、全局區(靜態區)(static)—全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的 另一塊區域。 - 程序結束后由系統釋放。
???4、常量區 —常量字符串就是放在這里的。 程序結束后由系統釋放
???5、程序代碼區—存放函數體的二進制代碼。

我們來看張圖:

首先我們要知道,源代碼編譯成程序,程序是放在硬盤上的,而非內存里!只有執行時才會被調用到內存中!

我們來看看程序結構,ELF是是Linux的主要可執行文件格式。ELF文件由4部分組成,分別是ELF頭(ELF header)、程序頭表(Program header table)、節(Section)和節頭表(Section header table)。具體如下:

1、Program header描述的是一個段在文件中的位置、大小以及它被放進內存后所在的位置和大小。即要加載的信息;
2、Sections保存著object 文件的信息,從連接角度看:包括指令,數據,符號表,重定位信息等等。在圖中,我們可以看到Sections中包括:
??????(1)??.text???文本結 存放指令;
??????(2)??.rodata???數據結??readonly;
??????(3)??.data??數據結?可讀可寫;?
3、Section頭表(section header table)包含了描述文件sections的信息。每個section在這個表中有一個入口;每個入口給出了該section的名字,大小,等等信息。相當于 索引!
?????而程序被加載到內存里面,又是如何分布的呢?我們看看上圖中:
1、正文和初始化的數據和未初始化的數據就是我們所說的數據段,正文即代碼段;
2、正文段上面是常量區,常量區上面是全局變量和靜態變量區,二者占據的就是初始化的數據和未初始化的數據那部分;
3、再上面就是堆,動態存儲區,這里是上增長;
4、堆上面是棧,存放的是局部變量,就是局部變量所在代碼塊執行完畢后,這塊內存會被釋放,這里棧區是下增長;
5、命令行參數就是$0 $1之類的,環境變量什么的前面的文章已經講過,有興趣的可以去看看。
?
????我們知道,內存分為動態內存和靜態內存,我們先講靜態內存。

一、靜態內存

內存管理---存儲模型
???????存儲模型決定了一個變量的內存分配方式和訪問特性,在C語言中主要有三個維度來決定:1、存儲時期 2、作用域 3、鏈接

1、存儲時期

? ? 存儲時期:變量在內存中的保留時間(生命周期)

? ? 存儲時期分為兩種情況,關鍵是看變量在程序執行過程中會不會被系統自動回收掉。

?1)??靜態存儲時期 Static
????????在程序執行過程中一旦分配就不會被自動回收。
????????通常來說,任何不在函數級別代碼塊內定義的變量。
????????無論是否在代碼塊內,只要采用static關鍵字修飾的變量。

?2) 自動存儲時期??Automatic
????????除了靜態存儲以外的變量都是自動存儲時期的,或者說只要是在代碼塊內定義的非static的變量,系統會肚臍自動非配和釋放內存;
?
2、作用域
??????作用域:一個變量在定義該變量的自身文件中的可見性(訪問或者引用)
???????在C語言中,一共有3中作用域:
1)??代碼塊作用域
?????在代碼塊中定義的變量都具有該代碼的作用域。從這個變量定義地方開始,到這個代碼塊結束,該變量是可見的;
2)??函數原型作用域
????出現在函數原型中的變量,都具有函數原型作用域,函數原型作用域從變量定義處一直到原型聲明的末尾。
3)??文件作用域
????一個在所有函數之外定義的變量具有文件作用域,具有文件作用域的變量從它的定義處到包含該定義的文件結尾處都是可見的;
?
3、鏈接
??????鏈接:一個變量在組成程序的所有文件中的可見性(訪問或者引用);
??????C語言中一共有三種不同的鏈接:
1)??外部鏈接
??????如果一個變量在組成一個程序的所有文件中的任何位置都可以被訪問,則稱該變量支持外部鏈接;
2)??內部鏈接
????如果一個變量只可以在定義其自身的文件中的任何位置被訪問,則稱該變量支持內部鏈接。
3)??空鏈接???
??????如果一個變量只是被定義其自身的當前代碼塊所私有,不能被程序的其他部分所訪問,則成該變量支持空鏈接
我們來看一個代碼示例:

#include <stdio.h>int a = 0;// 全局初始化區 ? char *p1; //全局未初始化區 ?int main() ? { ?int b; //b在棧區char s[] = "abc"; //棧 ?char *p2; //p2在棧區char *p3 = "123456"; //123456\0在常量區,p3在棧上。 ?static int c =0; //全局(靜態)初始化區p1 = (char *)malloc(10); ?p2 = (char *)malloc(20); ?//分配得來得10和20字節的區域就在堆區。 ?strcpy(p1, "123456"); //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。 ? } ?

二、動態內存

? ? 當程序運行到需要一個動態分配的變量時,必須向系統申請取得堆中的一塊所需大小的存儲空間,用于存儲該變量。當不在使用該變量時,也就是它的生命結束時,要顯示釋放它所占用的存儲空間,這樣系統就能對該空間?進行再次分配,做到重復使用有線的資源。下面介紹動態內存申請和釋放的函數。
1.1 malloc 函數
?malloc函數原型:

#include <stdio.h> void *malloc(size_t size);

size是需要動態申請的內存的字節數。若申請成功,函數返回申請到的內存的起始地址,若申請失敗,返回NULL。我們看下面這個例子:

int *get_memory(int n) {int *p;p = (int *)malloc(sizeof(int));if(p == NULL){printf("malloc error\n");return p;}memset(p,0,n*sizeof(int)); }

使用該函數時,有下面幾點要注意:
1)只關心申請內存的大小;
2)申請的是一塊連續的內存。記得一定要寫出錯判斷;
3)顯示初始化。即我們不知這塊內存中有什么東西,要對其清零;
?
1.2 free函數
????在堆上分配的額內存,需要用free函數顯示釋放,函數原型如下:

#include <stdlib.h> void free(void *ptr);

使用free(),也有下面幾點要注意:
1)必須提供內存的起始地址;
????調用該函數時,必須提供內存的起始地址,不能夠提供部分地址,釋放內存中的一部分是不允許的。
2)malloc和free配對使用;
? ? 編譯器不負責動態內存的釋放,需要程序員顯示釋放。因此,malloc與free是配對使用的,避免內存泄漏。
free(p);
p = NULL;
p = NULL是必須的,因為雖然這塊內存被釋放了,但是p仍指向這塊內存,避免下次對p的誤操作;
3)不允許重復釋放
因為這塊內存被釋放后,可能已另分配,這塊區域被別人占用,如果再次釋放,會造成數據丟失;

?
2、我們經常將堆和棧相比較:
2.1申請方式??
stack:??由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間??
heap:??需要程序員自己申請,并指明大小,在c中malloc函數 ,如p1 = (char *)malloc(10);??
??
2.2??申請后系統的響應??
棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。??
堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。?

2.3申請大小的限制??
棧:棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。?
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。?

2.4申請效率的比較:??
棧由系統自動分配,速度較快。但程序員是無法控制的。??
堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。?

2.5堆和棧中的存儲內容??
棧: 在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。??當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。?
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。?

2.6存取效率的比較?

char s1[] = "aaaaaaaaaaaaaaa";??
char *s2 = "bbbbbbbbbbbbbbbbb";??
aaaaaaaaaaa是在運行時刻賦值的;??
而bbbbbbbbbbb是在編譯時就確定的;??
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。??
比如:??

#include ? void main() ? { ?char a = 1; ?char c[] = "1234567890"; ?char *p ="1234567890"; ?a = c[1]; ?a = p[1]; ?return; ? } ?

對應的匯編代碼?

0: a = c[1]; ? 00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] ? 0040106A 88 4D FC mov byte ptr [ebp-4],cl ? 11: a = p[1]; ? 0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] ? 00401070 8A 42 01 mov al,byte ptr [edx+1] ? 00401073 88 45 FC mov byte ptr [ebp-4],al ?

第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,再根據edx讀取字符,顯然慢了。?


2.7小結:??
???堆和棧的區別可以用如下的比喻來看出:??
???棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。??
???堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。


補充知識點一:

線程和進程各自有什么區別和優劣呢?
? ? ?進程是資源分配的最小單位,線程是程序執行的最小單位。

? ? ?進程有自己的獨立地址空間,每啟動一個進程,系統就會為它分配地址空間,建立數據表來維護代碼段、堆棧段和數據段,這種操作非常昂貴。而線程是共享進程中的數據的,使用相同的地址空間,因此CPU切換一個線程的花費遠比進程要小很多,同時創建一個線程的開銷也比進程要小很多。

? ? ?線程之間的通信更方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信需要以通信的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。

? ? ?但是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉并不會對另外一個進程造成影響,因為進程有自己獨立的地址空間。


補充知識點二:

? ? BSS段通常是指用來存放程序中未初始化的全局變量和靜態變量的一塊內存區域。特點是可讀寫的,在程序執行之前BSS段會自動清0。
? ? 可執行程序包括BSS段、數據段、代碼段(也稱文本段)。
? ? BSS(Block Started by Symbol)通常是指用來存放程序中未初始化的全局變量和靜態變量的一塊內存區域。特點是:可讀寫的,在程序執行之前BSS段會自動清0。所以,未初始的全局變量在程序執行之前已經成0了。
? ? 數據段包括初始化的數據和未初始化的數據(BSS)兩部分 。BSS段存放的是未初始化的全局變量和靜態變量。
? ? 可使用size命令查看可執行文件的段大小信息。如size a.out。

biao@ubuntu:~/test/photo$ size a.out text data bss dec hex filename2876 632 8 3516 dbc a.out biao@ubuntu:~/test/photo$




Linux驅動mmap內存映射

mmap在linux哪里?


什么是mmap?

上圖說了,mmap是操作這些設備的一種方法,所謂操作設備,比如IO端口(點亮一個LED)、LCD控制器、磁盤控制器,實際上就是往設備的物理地址讀寫數據。

但是,由于應用程序不能直接操作設備硬件地址,所以操作系統提供了這樣的一種機制——內存映射,把設備地址映射到進程虛擬地址,mmap就是實現內存映射的接口。

操作設備還有很多方法,如ioctl、ioremap

mmap的好處是,mmap把設備內存映射到虛擬內存,則用戶操作虛擬內存相當于直接操作設備了,省去了用戶空間到內核空間的復制過程,相對IO操作來說,增加了數據的吞吐量。


什么是內存映射?

既然mmap是實現內存映射的接口,那么內存映射是什么呢?看下圖

每個進程都有獨立的進程地址空間,通過頁表和MMU,可將虛擬地址轉換為物理地址,每個進程都有獨立的頁表數據,這可解釋為什么兩個不同進程相同的虛擬地址,卻對應不同的物理地址。


什么是虛擬地址空間?

每個進程都有4G的虛擬地址空間,其中3G用戶空間,1G內核空間(linux),每個進程共享內核空間,獨立的用戶空間,下圖形象地表達了這點

驅動程序運行在內核空間,所以驅動程序是面向所有進程的。

用戶空間切換到內核空間有兩種方法:

(1)系統調用,即軟中斷

(2)硬件中斷


虛擬地址空間里面是什么?

了解了什么是虛擬地址空間,那么虛擬地址空間里面裝的是什么?看下圖

虛擬空間裝的大概是上面那些數據了,內存映射大概就是把設備地址映射到上圖的紅色段了,暫且稱其為“內存映射段”,至于映射到哪個地址,是由操作系統分配的,操作系統會把進程空間劃分為三個部分:

(1)未分配的,即進程還未使用的地址

(2)緩存的,緩存在ram中的頁

(3)未緩存的,沒有緩存在ram中

操作系統會在未分配的地址空間分配一段虛擬地址,用來和設備地址建立映射,至于怎么建立映射,后面再揭曉。

現在大概明白了“內存映射”是什么了,那么內核是怎么管理這些地址空間的呢?任何復雜的理論最終也是通過各種數據結構體現出來的,而這里這個數據結構就是進程描述符。從內核看,進程是分配系統資源(CPU、內存)的載體,為了管理進程,內核必須對每個進程所做的事情進行清楚的描述,這就是進程描述符,內核用task_struct結構體來表示進程,并且維護一個該結構體鏈表來管理所有進程。該結構體包含一些進程狀態、調度信息等上千個成員,我們這里主要關注進程描述符里面的內存描述符(struct mm_struct mm)


內存描述符

具體的結構,請參考下圖

現在已經知道了內存映射是把設備地址映射到進程空間地址(注意:并不是所有內存映射都是映射到進程地址空間的,ioremap是映射到內核虛擬空間的,mmap是映射到進程虛擬地址的),實質上是分配了一個vm_area_struct結構體加入到進程的地址空間,也就是說,把設備地址映射到這個結構體,映射過程就是驅動程序要做的事了。


內存映射的實現

以字符設備驅動為例,一般對字符設備的操作都如下框圖

而內存映射的主要任務就是實現內核空間中的mmap()函數,先來了解一下字符設備驅動程序的框架

以下是mmap_driver.c的源代碼

//所有的模塊代碼都包含下面兩個頭文件?? #include?<linux/module.h>?? #include?<linux/init.h>??#include?<linux/types.h>?//定義dev_t類型?? #include?<linux/cdev.h>?//定義struct?cdev結構體及相關操作?? #include?<linux/slab.h>?//定義kmalloc接口?? #include?<asm/io.h>//定義virt_to_phys接口?? #include?<linux/mm.h>//remap_pfn_range?? #include?<linux/fs.h>??#define?MAJOR_NUM?990?? #define?MM_SIZE?4096??static?char?driver_name[]?=?"mmap_driver1";//驅動模塊名字?? static?int?dev_major?=?MAJOR_NUM;?? static?int?dev_minor?=?0;?? char?*buf?=?NULL;?? struct?cdev?*cdev?=?NULL;??static?int?device_open(struct?inode?*inode,?struct?file?*file)?? {??printk(KERN_ALERT"device?open\n");??buf?=?(char?*)kmalloc(MM_SIZE,?GFP_KERNEL);//內核申請內存只能按頁申請,申請該內存以便后面把它當作虛擬設備??return?0;?? }??static?int?device_close(struct?inode?*indoe,?struct?file?*file)?? {??printk("device?close\n");??if(buf)??{??kfree(buf);??}??return?0;?? }??static?int?device_mmap(struct?file?*file,?struct?vm_area_struct?*vma)?? {??vma->vm_flags?|=?VM_IO;//表示對設備IO空間的映射??vma->vm_flags?|=?VM_RESERVED;//標志該內存區不能被換出,在設備驅動中虛擬頁和物理頁的關系應該是長期的,應該保留起來,不能隨便被別的虛擬頁換出??if(remap_pfn_range(vma,//虛擬內存區域,即設備地址將要映射到這里??vma->vm_start,//虛擬空間的起始地址??virt_to_phys(buf)>>PAGE_SHIFT,//與物理內存對應的頁幀號,物理地址右移12位??vma->vm_end?-?vma->vm_start,//映射區域大小,一般是頁大小的整數倍??vma->vm_page_prot))//保護屬性,??{??return?-EAGAIN;??}??return?0;?? }??static?struct?file_operations?device_fops?=?? {??.owner?=?THIS_MODULE,??.open??=?device_open,??.release?=?device_close,??.mmap?=?device_mmap,?? };??static?int?__init?char_device_init(?void?)?? {??int?result;??dev_t?dev;//高12位表示主設備號,低20位表示次設備號??printk(KERN_ALERT"module?init2323\n");??printk("dev=%d",?dev);??dev?=?MKDEV(dev_major,?dev_minor);??cdev?=?cdev_alloc();//為字符設備cdev分配空間??printk(KERN_ALERT"module?init\n");??if(dev_major)??{??result?=?register_chrdev_region(dev,?1,?driver_name);//靜態分配設備號??printk("result?=?%d\n",?result);??}??else??{??result?=?alloc_chrdev_region(&dev,?0,?1,?driver_name);//動態分配設備號??dev_major?=?MAJOR(dev);??}??if(result?<?0)??{??printk(KERN_WARNING"Cant't?get?major?%d\n",?dev_major);??return?result;??}??cdev_init(cdev,?&device_fops);//初始化字符設備cdev??cdev->ops?=?&device_fops;??cdev->owner?=?THIS_MODULE;??result?=?cdev_add(cdev,?dev,?1);//向內核注冊字符設備??printk("dffd?=?%d\n",?result);??return?0;?? }??static?void?__exit?char_device_exit(?void?)?? {??printk(KERN_ALERT"module?exit\n");??cdev_del(cdev);??unregister_chrdev_region(MKDEV(dev_major,?dev_minor),?1);?? }??module_init(char_device_init);//模塊加載?? module_exit(char_device_exit);//模塊退出??MODULE_LICENSE("GPL");?? MODULE_AUTHOR("ChenShengfa");?

下面是測試代碼test_mmap.c

#include?<stdio.h>?? #include?<fcntl.h>?? #include?<sys/mman.h>?? #include?<stdlib.h>?? #include?<string.h>??int?main(?void?)?? {??int?fd;??char?*buffer;??char?*mapBuf;??fd?=?open("/dev/mmap_driver",?O_RDWR);//打開設備文件,內核就能獲取設備文件的索引節點,填充inode結構??if(fd<0)??{??printf("open?device?is?error,fd?=?%d\n",fd);??return?-1;??}??/*測試一:查看內存映射段*/??printf("before?mmap\n");??sleep(15);//睡眠15秒,查看映射前的內存圖cat?/proc/pid/maps??buffer?=?(char?*)malloc(1024);??memset(buffer,?0,?1024);??mapBuf?=?mmap(NULL,?1024,?PROT_READ|PROT_WRITE,?MAP_SHARED,?fd,?0);//內存映射,會調用驅動的mmap函數??printf("after?mmap\n");??sleep(15);//睡眠15秒,在命令行查看映射后的內存圖,如果多出了映射段,說明映射成功??/*測試二:往映射段讀寫數據,看是否成功*/??strcpy(mapBuf,?"Driver?Test");//向映射段寫數據??memset(buffer,?0,?1024);??strcpy(buffer,?mapBuf);//從映射段讀取數據??printf("buf?=?%s\n",?buffer);//如果讀取出來的數據和寫入的數據一致,說明映射段的確成功了??munmap(mapBuf,?1024);//去除映射??free(buffer);??close(fd);//關閉文件,最終調用驅動的close??return?0;?? }??

下面是makefile文件

ifneq?($(KERNELRELEASE),)??obj-m?:=?mmap_driver.o??else?? KDIR?:=?/lib/modules/3.2.0-52-generic/build??all:??make?-C?$(KDIR)?M=$(PWD)?modules?? clean:??rm?-f?*.ko?*.o?*.mod.o?*.mod.c?*~?*.symvers?*.order??endif??

下面命令演示一下驅動程序的編譯、安裝、測試過程(注:其他用戶在mknod之后還需要chmod改變權限)

# make??? //編譯驅動

# insmod mmap_driver.ko??? //安裝驅動

# mknod?/dev/mmap_driver c 999 0??? //創建設備文件

# gcc test_mmap.c -o test.o??? //編譯應用程序

# ./test.o??? //運行應用程序來測試驅動程序


拓展:

關于這個過程,涉及一些術語

(1)設備文件:linux中對硬件虛擬成設備文件,對普通文件的各種操作均適用于設備文件

(2)索引節點:linux使用索引節點來記錄文件信息(如文件長度、創建修改時間),它存儲在磁盤中,讀入內存后就是一個inode結構體,文件系統維護了一個索引節點的數組,每個元素都和文件或者目錄一一對應。

(3)主設備號:如上面的999,表示設備的類型,比如該設備是lcd還是usb等

(4)次設備號:如上面的0,表示該類設備上的不同設備

(5)文件(普通文件或設備文件)的三個結構

??????? ①文件操作:struct file_operations

??????? ②文件對象:struct file

??????? ③文件索引節點:struct inode

關于驅動程序中內存映射的實現,先了解一下open和close的流程

(1)設備驅動open流程

①應用程序調用open("/dev/mmap_driver", O_RDWR);

②Open就會通過VFS找到該設備的索引節點(inode),mknod的時候會根據設備號把驅動程序的file_operations結構填充到索引節點中(關于mknod?/dev/mmap_driver c 999 0,這條指令創建了設備文件,在安裝驅動(insmod)的時候,會運行驅動程序的初始化程序(module_init),在初始化程序中,會注冊它的主設備號到系統中(cdev_add),如果mknod時的主設備號999在系統中不存在,即和注冊的主設備號不同,則上面的指令會執行失敗,就創建不了設備文件)

③然后根據設備文件的索引節點中的file_operations中的open指針,就調用驅動的open方法了。

④生成一個文件對象files_struct結構,系統維護一個files_struct的鏈表,表示系統中所有打開的文件

⑤返回文件描述符fd,把fd加入到進程的文件描述符表中

(2)設備驅動close流程

應用程序調用close(fd),最終可調用驅動的close,為什么根據一個簡單的int型fd就可以找到驅動的close函數?這就和上面說的三個結構(struct file_operations、struct file、struct inode)息息相關了,假如fd = 3

(3)設備驅動mmap流程

由open和close得知,同理,應用程序調用mmap最終也會調用到驅動程序中mmap方法

①應用程序test.mmap.c中mmap函數

void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

addr:映射后虛擬地址的起始地址,通常為NULL,內核自動分配

length:映射區的大小

prot:頁面訪問權限(PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE)

flags:參考網絡資料

fd:文件描述符

offset:文件映射開始偏移量

②驅動程序的mmap_driver.c中mmap函數

上面說了,mmap的主要工作是把設備地址映射到進程虛擬地址,也即是一個vm_area_struct的結構體,這里說的映射,是一個很懸的東西,那它在程序中的表現是什么呢?——頁表,沒錯,就是頁表,映射就是要建立頁表。進程地址空間就可以通過頁表(軟件)和MMU(硬件)映射到設備地址上了

virt_to_phys(buf),buf是在open時申請的地址,這里使用virt_to_phys把buf轉換成物理地址,是模擬了一個硬件設備,即把虛擬設備映射到虛擬地址,在實際中可以直接使用物理地址。

總結

①從以上看到,內核各個模塊錯綜復雜、相互交叉

②單純一個小小驅動模塊,就涉及了進程管理(進程地址空間)、內存管理(頁表與頁幀映射)、虛擬文件系統(structfile、structinode)

③并不是所有設備驅動都可以使用mmap來映射,比如像串口和其他面向流的設備,并且必須按照頁大小進行映射。



?ELF文件格式分析

一、概述

1.ELF全稱Executable and Linkable Format,可執行連接格式,ELF格式的文件用于存儲Linux程序。ELF文件(目標文件)格式主要三種:

  • 可重定向文件:文件保存著代碼和適當的數據,用來和其他的目標文件一起來創建一個可執行文件或者是一個共享目標文件。(目標文件或者靜態庫文件,即linux通常后綴為.a和.o的文件)
  • 可執行文件:文件保存著一個用來執行的程序。(例如bash,gcc等)
  • 共享目標文件:共享庫。文件保存著代碼和合適的數據,用來被下連接編輯器和動態鏈接器鏈接。(linux下后綴為.so的文件。)
    目標文件既要參與程序鏈接又要參與程序執行:

一般的 ELF 文件包括三個索引表:ELF header,Program header table,Section header table。

  • ELF header:在文件的開始,保存了路線圖,描述了該文件的組織情況。
  • Program header table:告訴系統如何創建進程映像。用來構造進程映像的目標文件必須具有程序頭部表,可重定位文件不需要這個表。
  • Section header table:包含了描述文件節區的信息,每個節區在表中都有一項,每一項給出諸如節區名稱、節區大小這類信息。用于鏈接的目標文件必須包含節區頭部表,其他目標文件可以有,也可以沒有這個表。

二、分析ELF文件頭(ELF header)

  • 進入終端輸入:cd /usr/include 進入include文件夾后查看elf.h文件,查看ELF的文件頭包含整個文件的控制結構

  • 寫一個小程序(hello 20135328)進行編譯,生成hello可執行文件。
    使用‘readelf –a hello’命令,都得到下面的ELF Header頭文件的信息,如下圖:

  • 通過上圖信息,可以得出Elf Header的Size為52bytes,所以可以使用hexdump工具將頭文件的16進制表打開。
    如下圖使用:‘hexdump -x hello -n 52 ’命令來查看hello文件頭的16進制表(前52bytes)對格式進行分析。

  • 第一行
    • 對應e_ident[EI_NIDENT]。實際表示內容為7f454c46010100010000000000000000,前四個字節7f454c46(0x45,0x4c,0x46是'e','l','f'對應的ascii編碼)是一個魔數,表示這是一個ELF對象。
    • 接下來的一個字節01表示是一個32位對象,接下來的一個字節01表示是小端法表示,再接下來的一個字節01表示文件頭版本。剩下的默認都設置為0.
  • 第二行
    • e_type值為0x0002,表示是一個可執行文件
    • 。e_machine值為0x003e,表示是Advanced Micro Devices X86-64處理器體系結構。
    • e_version值為0x00000001,表示是當前版本。
    • e_entry值為0x 08048320,表示入口點。
  • 第三行
    • e_phof f值為0x1178,表示程序頭表。
    • e_shoff值為0x0034,表示段表的偏移地址。
  • 第四行
    • e_flags值為0x001e,表示未知處理器特定標志。
    • e_ehsize值為0x0034,表示elf文件頭大小(正好是52bytes)。
    • e_phentsize表示一個program header表中的入口的長度,值為0x0020。
    • e_phnum的值為0x0009,給出program header表中的入口數目。
    • e_shentsize值為0x0028表示段頭大小為40個字節。e_shnum值為0x001e,表示段表入口有30個。
    • e_shstrndx值為0x001b,表示段名串表的在段表中的索引號。

      三、通過文件頭找到section header table,理解其內容

  • file elf顯示生成的目標文件hello的類型
  • elf是一個可執行文件。輸入:ls –l hello查看hello的大小:
  • 如圖可知,hello大小為7336字節。
    輸入:hexdump –x hello來用16進制的數字來顯示hello的內容
    (其中,第二列是16進制表示的偏移地址)
  • 輸入:objdump –x hello來顯示hello中各個段以及符號表的相關信息:
  • 輸入:readelf –a hello來查看各個段信息:
  • ELF文件頭信息:
  • 段表Section header table:
  • 符號表 Symbol table:


    四、通過section header table找到各section

    在一個ELF文件中有一個section header table,通過它我們可以定位到所有的 section,而 ELF header 中的e_shoff 變量就是保存 section header table 入口對文件頭的偏移量。而每個 section 都會對應一個 section header ,所以只要在 section header table 中找到每個 section header,就可以通過 section header 找到你想要的 section。

下面以可執行文件hello為例,以保存代碼段的 section 為例來講解讀取某個section 的過程。
使用‘vi /usr/include/elf.h ’命令查看Sections Header的結構體:

由上面分析可知,section headers table中的每一個section header所占的size均為64字節,ELF header得到了e_shoff變量的值為0X0034,也就是table入口的偏移量,通過看e_shnum值為0x001e,表示段表入口有30個。
所以從0x00000034開始有30個段,每個段占40個字節大小,輸入 hexdump hello查看:

  • 第一個段,其中內容全部為0,所以不表示任何段。
  • 第二個段,為.interp段

  • 第三個段,為.note.ABI-tag段

  • 第四個段,為.note.gnu.build-i段

  • 第五個段,為.gnu.hash段

.......

  • 第十四個段,為.text段

  • 第十六個段,為.rodata段

  • 第二十五個段,為.data段

  • 第二十六個段,為.bss段

  • 第二十九個段, 為.symtab段

  • 第三十個段, 為.strtab段

我們用readelf 命令去查看.text這個 section 中的內容,
輸入readelf –x 13 hello,(.text前面的標號為13)對13索引號的.text的section的內容進行查看:

下面用 hexdump 的方法去讀取.text這個 section 中的內容,通過看section header中.text中offset和size分別是0x320和0x192
輸入 hexdump –C hello
找到320后的192個


得到了和上面的readelf得到的相同。
使用下面命令對hello的文本段(.text)進行反匯編:
objdump –d hello 得到如下圖:


可以看出,使用反匯編的16進制數據和前面查找到的是相同的。

五、理解常見.text .strtab .symtab .rodata等section

  • text section是可執行指令的集合,.data和.text都是屬于PROGBITS類型的section,這是將來要運行的程序與代碼。查詢段表可知.text section的位偏移為0x0000320,size為0x0000192。
  • strtab section是屬于STRTAB類型的section,可以在文件中看到,它存著字符串,儲存著符號的名字。位偏移為0x000106f,size為0x0000106
  • symtab section存放所有section中定義的符號名字,比如“data_items”,“start_loop”。 .symtab section是屬于SYMTAB類型的section,它描述了.strtab中的符號在“內存”中對應的“內存地址”。 位偏移為0x0001628,size為0x0000430。
  • rodata section,ro代表read only。位偏移為0x000050c,size為0x00000b0。


  • readelf命令和ELF文件詳解?

    ELF(Executable and Linking Format)是一個定義了目標文件內部信息如何組成和組織的文件格式。內核會根據這些信息加載可執行文件,內核根據這些信息可以知道從文件哪里獲取代碼,從哪里獲取初始化數據,在哪里應該加載共享庫,等信息。?
    ELF文件有下面三種類型:?
    1.目標文件

    $ gcc -c test.c?
    得到的test.o就是目標文件,目標文件通過鏈接可生成可執行文件。?
    靜態庫其實也算目標文件,靜態庫是通過ar命令將目標打包為.a文件。?
    如:ar crv libtest.a test.o

    2.可執行文件?
    $gcc -o test test.c?
    得到的test文件就是可執行的二進制文件。

    3.共享庫?
    $ gcc test.c -fPIC -shared -o libtest.so?
    得到的文件listtest.so就是共享庫。

    可以通過readelf來區分上面三種類型的ELF文件,每種類型文件的頭部信息是不一樣的。?
    $readelf -h test.o?
    目標文件

    ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: REL (Relocatable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x0Start of program headers: 0 (bytes into file)Start of section headers: 456 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 0 (bytes)Number of program headers: 0Size of section headers: 64 (bytes)Number of section headers: 13Section header string table index: 10

    $readelf -h test?

    可執行文件

    ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x400420Start of program headers: 64 (bytes into file)Start of section headers: 2696 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 8Size of section headers: 64 (bytes)Number of section headers: 30Section header string table index: 27

    $readelf -h libtest.so?
    共享庫

    ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: DYN (Shared object file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x570Start of program headers: 64 (bytes into file)Start of section headers: 2768 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 6Size of section headers: 64 (bytes)Number of section headers: 29Section header string table index: 26

    下面是test.c文件內容:

    #include<stdio.h>int global_data = 4; int global_data_2; int main(int argc, char **argv) { int local_data = 3; printf("Hello World\n"); printf("global_data = %d\n", global_data); printf("global_data_2 = %d\n", global_data_2); printf("local_data = %d\n", local_data); return (0); }

    $gcc -o test test.c?
    生成可執行文件test,然后使用readelf對其進行分析。

    $readelf -h test?
    下面是輸出結果:

    ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x400420Start of program headers: 64 (bytes into file)Start of section headers: 2696 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 8Size of section headers: 64 (bytes)Number of section headers: 30Section header string table index: 27

    上面的信息可以告訴我們什么信息??
    1.根據Class、Type和Machine,可以知道該文件在X86-64位機器上生成的64位可執行文件。?
    2.根據Entry point address,可以知道當該程序啟動時從虛擬地址0x400420處開始運行。這個地址并不是main函數的地址,而是_start函數的地址,_start由鏈接器創建,_start是為了初始化程序。通過這個命令可以看到_start函數,objdump -d -j .text test?
    3.根據Number of program headers,可以知道該程序有8個段。?
    4.根據Number of section headers,可以知道該程序有30個區。?
    區中存儲的信息是用來鏈接使用的,主要包括:程序代碼、程序數據(變量)、重定向信息等。比如:Code section保存的是代碼,data section保存的是初始化或未初始化的數據,等等。

    Linux內核無法以區的概念來識別可執行文件。內核使用包括連續頁的VMA(virtual memory area)來識別進程。在每個VMA中可能映射了一個或多個區。每個VMA代表一個ELF文件的段。?
    那么,內核如何知道哪個區屬于某個VMA(段)?映射關系保存在Program Header Table(PHT)中。

    下面查看區的內容:?

    $readelf -S test

    There are 30 section headers, starting at offset 0xa88:Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000400200 00000200000000000000001c 0000000000000000 A 0 0 1[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c0000000000000020 0000000000000000 A 0 0 4[ 3] .note.gnu.build-i NOTE 000000000040023c 0000023c0000000000000024 0000000000000000 A 0 0 4[ 4] .gnu.hash GNU_HASH 0000000000400260 00000260000000000000001c 0000000000000000 A 5 0 8[ 5] .dynsym DYNSYM 0000000000400280 000002800000000000000078 0000000000000018 A 6 1 8[ 6] .dynstr STRTAB 00000000004002f8 000002f80000000000000044 0000000000000000 A 0 0 1[ 7] .gnu.version VERSYM 000000000040033c 0000033c000000000000000a 0000000000000002 A 5 0 2[ 8] .gnu.version_r VERNEED 0000000000400348 000003480000000000000020 0000000000000000 A 6 1 8[ 9] .rela.dyn RELA 0000000000400368 000003680000000000000018 0000000000000018 A 5 0 8[10] .rela.plt RELA 0000000000400380 000003800000000000000048 0000000000000018 A 5 12 8[11] .init PROGBITS 00000000004003c8 000003c80000000000000018 0000000000000000 AX 0 0 4[12] .plt PROGBITS 00000000004003e0 000003e00000000000000040 0000000000000010 AX 0 0 4[13] .text PROGBITS 0000000000400420 000004200000000000000238 0000000000000000 AX 0 0 16[14] .fini PROGBITS 0000000000400658 00000658000000000000000e 0000000000000000 AX 0 0 4[15] .rodata PROGBITS 0000000000400668 000006680000000000000053 0000000000000000 A 0 0 8[16] .eh_frame_hdr PROGBITS 00000000004006bc 000006bc0000000000000024 0000000000000000 A 0 0 4[17] .eh_frame PROGBITS 00000000004006e0 000006e0000000000000007c 0000000000000000 A 0 0 8[18] .ctors PROGBITS 0000000000600760 000007600000000000000010 0000000000000000 WA 0 0 8[19] .dtors PROGBITS 0000000000600770 000007700000000000000010 0000000000000000 WA 0 0 8[20] .jcr PROGBITS 0000000000600780 000007800000000000000008 0000000000000000 WA 0 0 8[21] .dynamic DYNAMIC 0000000000600788 000007880000000000000190 0000000000000010 WA 6 0 8[22] .got PROGBITS 0000000000600918 000009180000000000000008 0000000000000008 WA 0 0 8[23] .got.plt PROGBITS 0000000000600920 000009200000000000000030 0000000000000008 WA 0 0 8[24] .data PROGBITS 0000000000600950 000009500000000000000008 0000000000000000 WA 0 0 4[25] .bss NOBITS 0000000000600958 000009580000000000000018 0000000000000000 WA 0 0 8[26] .comment PROGBITS 0000000000000000 00000958000000000000002c 0000000000000001 MS 0 0 1[27] .shstrtab STRTAB 0000000000000000 0000098400000000000000fe 0000000000000000 0 0 1[28] .symtab SYMTAB 0000000000000000 000012080000000000000648 0000000000000018 29 46 8[29] .strtab STRTAB 0000000000000000 00001850000000000000021e 0000000000000000 0 0 1 Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)

    .text區存儲的是程序的代碼(二進制指令),該區的標志為X表示可執行。

    下面使用objdump反匯編查看.text的內容:?
    $objdump -d -j .text test?
    -d選項告訴objdump反匯編機器碼,-j選項告訴objdump只關心.text區。

    test: file format elf64-x86-64Disassembly of section .text:0000000000400420 <_start>:400420: 31 ed xor %ebp,%ebp400422: 49 89 d1 mov %rdx,%r9400425: 5e pop %rsi400426: 48 89 e2 mov %rsp,%rdx400429: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp40042d: 50 push %rax40042e: 54 push %rsp40042f: 49 c7 c0 80 05 40 00 mov $0x400580,%r8400436: 48 c7 c1 90 05 40 00 mov $0x400590,%rcx40043d: 48 c7 c7 04 05 40 00 mov $0x400504,%rdi400444: e8 c7 ff ff ff callq 400410 <__libc_start_main@plt>400449: f4 hlt 40044a: 90 nop40044b: 90 nop000000000040044c <call_gmon_start>:40044c: 48 83 ec 08 sub $0x8,%rsp400450: 48 8b 05 c1 04 20 00 mov 0x2004c1(%rip),%rax # 600918 <_DYNAMIC+0x190>400457: 48 85 c0 test %rax,%rax40045a: 74 02 je 40045e <call_gmon_start+0x12>40045c: ff d0 callq *%rax40045e: 48 83 c4 08 add $0x8,%rsp400462: c3 retq 400463: 90 nop400464: 90 nop400465: 90 nop400466: 90 nop400467: 90 nop400468: 90 nop400469: 90 nop40046a: 90 nop40046b: 90 nop40046c: 90 nop40046d: 90 nop40046e: 90 nop40046f: 90 nop0000000000400470 <__do_global_dtors_aux>:400470: 55 push %rbp400471: 48 89 e5 mov %rsp,%rbp400474: 53 push %rbx400475: 48 83 ec 08 sub $0x8,%rsp400479: 80 3d d8 04 20 00 00 cmpb $0x0,0x2004d8(%rip) # 600958 <__bss_start>400480: 75 4b jne 4004cd <__do_global_dtors_aux+0x5d>400482: bb 78 07 60 00 mov $0x600778,%ebx400487: 48 8b 05 d2 04 20 00 mov 0x2004d2(%rip),%rax # 600960 <dtor_idx.6349>40048e: 48 81 eb 70 07 60 00 sub $0x600770,%rbx400495: 48 c1 fb 03 sar $0x3,%rbx400499: 48 83 eb 01 sub $0x1,%rbx40049d: 48 39 d8 cmp %rbx,%rax4004a0: 73 24 jae 4004c6 <__do_global_dtors_aux+0x56>4004a2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)4004a8: 48 83 c0 01 add $0x1,%rax4004ac: 48 89 05 ad 04 20 00 mov %rax,0x2004ad(%rip) # 600960 <dtor_idx.6349>4004b3: ff 14 c5 70 07 60 00 callq *0x600770(,%rax,8)4004ba: 48 8b 05 9f 04 20 00 mov 0x20049f(%rip),%rax # 600960 <dtor_idx.6349>4004c1: 48 39 d8 cmp %rbx,%rax4004c4: 72 e2 jb 4004a8 <__do_global_dtors_aux+0x38>4004c6: c6 05 8b 04 20 00 01 movb $0x1,0x20048b(%rip) # 600958 <__bss_start>4004cd: 48 83 c4 08 add $0x8,%rsp4004d1: 5b pop %rbx4004d2: c9 leaveq 4004d3: c3 retq 4004d4: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1)4004db: 00 00 00 00 00 00000000004004e0 <frame_dummy>:4004e0: 48 83 3d 98 02 20 00 cmpq $0x0,0x200298(%rip) # 600780 <__JCR_END__>4004e7: 00 4004e8: 55 push %rbp4004e9: 48 89 e5 mov %rsp,%rbp4004ec: 74 12 je 400500 <frame_dummy+0x20>4004ee: b8 00 00 00 00 mov $0x0,%eax4004f3: 48 85 c0 test %rax,%rax4004f6: 74 08 je 400500 <frame_dummy+0x20>4004f8: bf 80 07 60 00 mov $0x600780,%edi4004fd: c9 leaveq 4004fe: ff e0 jmpq *%rax400500: c9 leaveq 400501: c3 retq 400502: 90 nop400503: 90 nop0000000000400504 <main>:400504: 55 push %rbp400505: 48 89 e5 mov %rsp,%rbp400508: 48 83 ec 20 sub $0x20,%rsp40050c: 89 7d ec mov %edi,-0x14(%rbp)40050f: 48 89 75 e0 mov %rsi,-0x20(%rbp)400513: c7 45 fc 03 00 00 00 movl $0x3,-0x4(%rbp)40051a: bf 78 06 40 00 mov $0x400678,%edi40051f: e8 dc fe ff ff callq 400400 <puts@plt>400524: 8b 15 2a 04 20 00 mov 0x20042a(%rip),%edx # 600954 <global_data>40052a: b8 84 06 40 00 mov $0x400684,%eax40052f: 89 d6 mov %edx,%esi400531: 48 89 c7 mov %rax,%rdi400534: b8 00 00 00 00 mov $0x0,%eax400539: e8 b2 fe ff ff callq 4003f0 <printf@plt>40053e: 8b 15 24 04 20 00 mov 0x200424(%rip),%edx # 600968 <global_data_2>400544: b8 96 06 40 00 mov $0x400696,%eax400549: 89 d6 mov %edx,%esi40054b: 48 89 c7 mov %rax,%rdi40054e: b8 00 00 00 00 mov $0x0,%eax400553: e8 98 fe ff ff callq 4003f0 <printf@plt>400558: b8 aa 06 40 00 mov $0x4006aa,%eax40055d: 8b 55 fc mov -0x4(%rbp),%edx400560: 89 d6 mov %edx,%esi400562: 48 89 c7 mov %rax,%rdi400565: b8 00 00 00 00 mov $0x0,%eax40056a: e8 81 fe ff ff callq 4003f0 <printf@plt>40056f: b8 00 00 00 00 mov $0x0,%eax400574: c9 leaveq 400575: c3 retq 400576: 90 nop400577: 90 nop400578: 90 nop400579: 90 nop40057a: 90 nop40057b: 90 nop40057c: 90 nop40057d: 90 nop40057e: 90 nop40057f: 90 nop0000000000400580 <__libc_csu_fini>:400580: f3 c3 repz retq 400582: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)400589: 1f 84 00 00 00 00 00 0000000000400590 <__libc_csu_init>:400590: 48 89 6c 24 d8 mov %rbp,-0x28(%rsp)400595: 4c 89 64 24 e0 mov %r12,-0x20(%rsp)40059a: 48 8d 2d bb 01 20 00 lea 0x2001bb(%rip),%rbp # 60075c <__init_array_end>4005a1: 4c 8d 25 b4 01 20 00 lea 0x2001b4(%rip),%r12 # 60075c <__init_array_end>4005a8: 4c 89 6c 24 e8 mov %r13,-0x18(%rsp)4005ad: 4c 89 74 24 f0 mov %r14,-0x10(%rsp)4005b2: 4c 89 7c 24 f8 mov %r15,-0x8(%rsp)4005b7: 48 89 5c 24 d0 mov %rbx,-0x30(%rsp)4005bc: 48 83 ec 38 sub $0x38,%rsp4005c0: 4c 29 e5 sub %r12,%rbp4005c3: 41 89 fd mov %edi,%r13d4005c6: 49 89 f6 mov %rsi,%r144005c9: 48 c1 fd 03 sar $0x3,%rbp4005cd: 49 89 d7 mov %rdx,%r154005d0: e8 f3 fd ff ff callq 4003c8 <_init>4005d5: 48 85 ed test %rbp,%rbp4005d8: 74 1c je 4005f6 <__libc_csu_init+0x66>4005da: 31 db xor %ebx,%ebx4005dc: 0f 1f 40 00 nopl 0x0(%rax)4005e0: 4c 89 fa mov %r15,%rdx4005e3: 4c 89 f6 mov %r14,%rsi4005e6: 44 89 ef mov %r13d,%edi4005e9: 41 ff 14 dc callq *(%r12,%rbx,8)4005ed: 48 83 c3 01 add $0x1,%rbx4005f1: 48 39 eb cmp %rbp,%rbx4005f4: 72 ea jb 4005e0 <__libc_csu_init+0x50>4005f6: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx4005fb: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp400600: 4c 8b 64 24 18 mov 0x18(%rsp),%r12400605: 4c 8b 6c 24 20 mov 0x20(%rsp),%r1340060a: 4c 8b 74 24 28 mov 0x28(%rsp),%r1440060f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15400614: 48 83 c4 38 add $0x38,%rsp400618: c3 retq 400619: 90 nop40061a: 90 nop40061b: 90 nop40061c: 90 nop40061d: 90 nop40061e: 90 nop40061f: 90 nop0000000000400620 <__do_global_ctors_aux>:400620: 55 push %rbp400621: 48 89 e5 mov %rsp,%rbp400624: 53 push %rbx400625: 48 83 ec 08 sub $0x8,%rsp400629: 48 8b 05 30 01 20 00 mov 0x200130(%rip),%rax # 600760 <__CTOR_LIST__>400630: 48 83 f8 ff cmp $0xffffffffffffffff,%rax400634: 74 19 je 40064f <__do_global_ctors_aux+0x2f>400636: bb 60 07 60 00 mov $0x600760,%ebx40063b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)400640: 48 83 eb 08 sub $0x8,%rbx400644: ff d0 callq *%rax400646: 48 8b 03 mov (%rbx),%rax400649: 48 83 f8 ff cmp $0xffffffffffffffff,%rax40064d: 75 f1 jne 400640 <__do_global_ctors_aux+0x20>40064f: 48 83 c4 08 add $0x8,%rsp400653: 5b pop %rbx400654: c9 leaveq 400655: c3 retq 400656: 90 nop400657: 90 nop

    下面使用objdump反匯編查看.data的內容:?
    $objdump -d -j .data test?
    .data區保存的是初始化的全局變量。

    test: file format elf64-x86-64Disassembly of section .data:0000000000600950 <__data_start>:600950: 00 00 add %al,(%rax)...0000000000600954 <global_data>:600954: 04 00 00 00

    下面使用objdump反匯編查看.bss的內容:?
    $objdump -d -j .bss test?
    .bss區保存的是未初始化的全局變量,linux會默認將未初始化的變量置為0。

    test: file format elf64-x86-64Disassembly of section .bss:0000000000600958 <completed.6347>:...0000000000600960 <dtor_idx.6349>:...0000000000600968 <global_data_2>:

    下面命令可以看到test文件中所有的符號:?
    $readelf -s test?
    Value的值是符號的地址。

    Symbol table '.dynsym' contains 5 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)Symbol table '.symtab' contains 67 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000400200 0 SECTION LOCAL DEFAULT 1 2: 000000000040021c 0 SECTION LOCAL DEFAULT 2 3: 000000000040023c 0 SECTION LOCAL DEFAULT 3 4: 0000000000400260 0 SECTION LOCAL DEFAULT 4 5: 0000000000400280 0 SECTION LOCAL DEFAULT 5 6: 00000000004002f8 0 SECTION LOCAL DEFAULT 6 7: 000000000040033c 0 SECTION LOCAL DEFAULT 7 8: 0000000000400348 0 SECTION LOCAL DEFAULT 8 9: 0000000000400368 0 SECTION LOCAL DEFAULT 9 10: 0000000000400380 0 SECTION LOCAL DEFAULT 10 11: 00000000004003c8 0 SECTION LOCAL DEFAULT 11 12: 00000000004003e0 0 SECTION LOCAL DEFAULT 12 13: 0000000000400420 0 SECTION LOCAL DEFAULT 13 14: 0000000000400658 0 SECTION LOCAL DEFAULT 14 15: 0000000000400668 0 SECTION LOCAL DEFAULT 15 16: 00000000004006bc 0 SECTION LOCAL DEFAULT 16 17: 00000000004006e0 0 SECTION LOCAL DEFAULT 17 18: 0000000000600760 0 SECTION LOCAL DEFAULT 18 19: 0000000000600770 0 SECTION LOCAL DEFAULT 19 20: 0000000000600780 0 SECTION LOCAL DEFAULT 20 21: 0000000000600788 0 SECTION LOCAL DEFAULT 21 22: 0000000000600918 0 SECTION LOCAL DEFAULT 22 23: 0000000000600920 0 SECTION LOCAL DEFAULT 23 24: 0000000000600950 0 SECTION LOCAL DEFAULT 24 25: 0000000000600958 0 SECTION LOCAL DEFAULT 25 26: 0000000000000000 0 SECTION LOCAL DEFAULT 26 27: 000000000040044c 0 FUNC LOCAL DEFAULT 13 call_gmon_start28: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c29: 0000000000600760 0 OBJECT LOCAL DEFAULT 18 __CTOR_LIST__30: 0000000000600770 0 OBJECT LOCAL DEFAULT 19 __DTOR_LIST__31: 0000000000600780 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__32: 0000000000400470 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux33: 0000000000600958 1 OBJECT LOCAL DEFAULT 25 completed.634734: 0000000000600960 8 OBJECT LOCAL DEFAULT 25 dtor_idx.634935: 00000000004004e0 0 FUNC LOCAL DEFAULT 13 frame_dummy36: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c37: 0000000000600768 0 OBJECT LOCAL DEFAULT 18 __CTOR_END__38: 0000000000400758 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__39: 0000000000600780 0 OBJECT LOCAL DEFAULT 20 __JCR_END__40: 0000000000400620 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux41: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c42: 0000000000600920 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_43: 000000000060075c 0 NOTYPE LOCAL DEFAULT 18 __init_array_end44: 000000000060075c 0 NOTYPE LOCAL DEFAULT 18 __init_array_start45: 0000000000600788 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC46: 0000000000600950 0 NOTYPE WEAK DEFAULT 24 data_start47: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.548: 0000000000400580 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini49: 0000000000400420 0 FUNC GLOBAL DEFAULT 13 _start50: 0000000000600968 4 OBJECT GLOBAL DEFAULT 25 global_data_251: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__52: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.554: 0000000000400658 0 FUNC GLOBAL DEFAULT 14 _fini55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_56: 0000000000400668 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used57: 0000000000600950 0 NOTYPE GLOBAL DEFAULT 24 __data_start58: 0000000000400670 0 OBJECT GLOBAL HIDDEN 15 __dso_handle59: 0000000000600778 0 OBJECT GLOBAL HIDDEN 19 __DTOR_END__60: 0000000000400590 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init61: 0000000000600958 0 NOTYPE GLOBAL DEFAULT ABS __bss_start62: 0000000000600970 0 NOTYPE GLOBAL DEFAULT ABS _end63: 0000000000600958 0 NOTYPE GLOBAL DEFAULT ABS _edata64: 0000000000600954 4 OBJECT GLOBAL DEFAULT 24 global_data65: 0000000000400504 114 FUNC GLOBAL DEFAULT 13 main66: 00000000004003c8 0 FUNC GLOBAL DEFAULT 11 _init

    下面命令來查看文件的段信息:?
    $readelf -l test?
    區到段的映射,基本上是按照區的順序進行映射。?
    如果Flags為R和E,表示該段可讀和可執行。?
    如果Flags為W,表示該段可寫。?
    VirtAddr是每個段的虛擬起始地址。這個地址并不是位于真正內存上的地址(物理地址)。

    Elf file type is EXEC (Executable file) Entry point 0x400420 There are 8 program headers, starting at offset 64Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000000000400040 0x00000000004000400x00000000000001c0 0x00000000000001c0 R E 8INTERP 0x0000000000000200 0x0000000000400200 0x00000000004002000x000000000000001c 0x000000000000001c R 1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD 0x0000000000000000 0x0000000000400000 0x00000000004000000x000000000000075c 0x000000000000075c R E 200000LOAD 0x0000000000000760 0x0000000000600760 0x00000000006007600x00000000000001f8 0x0000000000000210 RW 200000DYNAMIC 0x0000000000000788 0x0000000000600788 0x00000000006007880x0000000000000190 0x0000000000000190 RW 8NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c0x0000000000000044 0x0000000000000044 R 4GNU_EH_FRAME 0x00000000000006bc 0x00000000004006bc 0x00000000004006bc0x0000000000000024 0x0000000000000024 R 4GNU_STACK 0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 RW 8Section to Segment mapping:Segment Sections...00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07

    如上所示,段有多種類型,下面介紹LOAD類型?
    LOAD:該段的內容從可執行文件中獲取。Offset標識內核從文件讀取的位置。FileSiz標識讀取多少字節。

    那么,執行test之后的進程的段布局是如何呢??
    可以通過cat /proc/pid/maps來查看。pid是進程的pid。?
    但是該test運行時間很短,可以使用gdb加斷點來運行,或者在return語句之前加上sleep()。

    下面使用gdb加斷點的形式:

    GNU gdb (GDB) Red Hat Enterprise Linux (7.2-50.el6) Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /data/readyao/qqlive_zb_prj/server/cgi_push_post_replay/lib/test...(no debugging symbols found)...done. (gdb) b main Breakpoint 1 at 0x400508 (gdb) r Starting program: /data/readyao/qqlive_zb_prj/server/cgi_push_post_replay/lib/test [Thread debugging using libthread_db enabled]Breakpoint 1, 0x0000000000400508 in main () Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.49.tl1.x86_64

    $cat /proc/6929/maps?

    00400000-00401000 r-xp 00000000 ca:11 8626925 /test 00600000-00601000 rw-p 00000000 ca:11 8626925 /test 7ffff762d000-7ffff7644000 r-xp 00000000 ca:01 332328 /lib64/libpthread-2.12.so 7ffff7644000-7ffff7843000 ---p 00017000 ca:01 332328 /lib64/libpthread-2.12.so 7ffff7843000-7ffff7844000 r--p 00016000 ca:01 332328 /lib64/libpthread-2.12.so 7ffff7844000-7ffff7845000 rw-p 00017000 ca:01 332328 /lib64/libpthread-2.12.so 7ffff7845000-7ffff7849000 rw-p 00000000 00:00 0 7ffff7849000-7ffff784b000 r-xp 00000000 ca:01 332237 /lib64/libdl-2.12.so 7ffff784b000-7ffff7a4b000 ---p 00002000 ca:01 332237 /lib64/libdl-2.12.so 7ffff7a4b000-7ffff7a4c000 r--p 00002000 ca:01 332237 /lib64/libdl-2.12.so 7ffff7a4c000-7ffff7a4d000 rw-p 00003000 ca:01 332237 /lib64/libdl-2.12.so 7ffff7a4d000-7ffff7bd3000 r-xp 00000000 ca:01 332102 /lib64/libc-2.12.so 7ffff7bd3000-7ffff7dd3000 ---p 00186000 ca:01 332102 /lib64/libc-2.12.so 7ffff7dd3000-7ffff7dd7000 r--p 00186000 ca:01 332102 /lib64/libc-2.12.so 7ffff7dd7000-7ffff7dd8000 rw-p 0018a000 ca:01 332102 /lib64/libc-2.12.so 7ffff7dd8000-7ffff7ddd000 rw-p 00000000 00:00 0 7ffff7ddd000-7ffff7dfd000 r-xp 00000000 ca:01 332126 /lib64/ld-2.12.so 7ffff7ed9000-7ffff7edc000 rw-p 00000000 00:00 0 7ffff7eeb000-7ffff7eee000 r-xp 00000000 ca:01 336319 /lib64/libonion_security.so.1.0.13 7ffff7eee000-7ffff7fee000 ---p 00003000 ca:01 336319 /lib64/libonion_security.so.1.0.13 7ffff7fee000-7ffff7fef000 rw-p 00003000 ca:01 336319 /lib64/libonion_security.so.1.0.13 7ffff7fef000-7ffff7ffb000 rw-p 00000000 00:00 0 7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 7ffff7ffc000-7ffff7ffd000 r--p 0001f000 ca:01 332126 /lib64/ld-2.12.so 7ffff7ffd000-7ffff7ffe000 rw-p 00020000 ca:01 332126 /lib64/ld-2.12.so 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 7ffffffea000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

    前面一部分是VMA的起始地址和結束地址。?
    最后一部分是該區域內容所屬文件。?
    在32位系統中,進程地址空間為4G,分為用戶空間和內核空間。?


    從下面可以看到棧的地址是向下生長,堆的地址是向上生長。?

    通過反匯編定位段錯誤:

    段錯誤是程序員最討厭的問題之一,其發生往往很突然,且破壞巨大。典型的段錯誤是由于操作內存不當引起的(如使用野指針或訪問受保護的地址等),發生段錯誤時,內核以一個信號SIGSEGV強行終止進程,留下的出錯信息極少,從而導致難以定位。但利用gdb和反匯編工具,可以較準確地定位段錯誤產生的原因。但想用這種方法調試,一些準備工作和工具是必需的。

    準備工作:

    (1)coredump:進程異常中止時,內核生成的記錄文件,其中保存了進程異常時所占用的內存和CPU資源,如pc計數器、各個寄存器的值等。這個文件是調試段錯誤最重要的依據。要使內核生成coredump,需要在內核配置中打開CONFIG_ELF_CORE選項,如果沒有打開,將其選上后重新編譯內核即可。

    此外,利用命令ulimit -c unlimited,可以設置coredump大小為不受限制,可以保存更完整的信息。文件/proc/sys/kernel/core_pattern可以配置生成coredump的命名格式,如果不設置格式,則coredump默認生成的位置在出錯進程的目錄下,且生成的core同名,也就意味著舊的coredump可能被新的coredump所覆蓋。如果我想在/tmp目錄下生成以core.pid格式命名的coredump文件,只需執行命令:

    echo "/tmp/core.%p" > /proc/sys/kernel/core_pattern

    (2)編譯:為了利用gdb進行調試,在編譯程序時,需要在編譯選項中加入-g 選項,以將調試信息編譯到目標文件中。

    (3)反匯編:顧名思義,反匯編就是將編譯好的二進制可執行文件翻譯成匯編文件。一般來說,編譯器會自帶一套反匯編工具,只有選擇正確的工具才能正確地進行反匯編,這不難理解。比如我是用gcc4.6.3編譯的用于mips的應用程序,那么,在編譯器的目錄下可以找到gcc463/usr/bin/mipsel-buildroot-linux-uclibc-objdump,這就是我要使用的反匯編工具。將二進制文件反匯編成匯編文件只需執行命令:XXXX-objdump -S XXXX(程序名),即可生成可以閱讀的、關聯到C代碼的匯編代碼,如下所示:

    ?? ?status = httpRpmPost(reqId);?? ?42f208:?? ?0320f809 ?? ?jalr?? ?t942f20c:?? ?00808021 ?? ?move?? ?s0,a042f210:?? ?8fbc0018 ?? ?lw?? ?gp,24(sp)if (RPM_OK != status && RPM_DONE != status)42f214:?? ?10400009 ?? ?beqz?? ?v0,42f23c <postDataApStatusJson.part.4+0x68>42f218:?? ?3c120061 ?? ?lui?? ?s2,0x6142f21c:?? ?24030002 ?? ?li?? ?v1,242f220:?? ?10430006 ?? ?beq?? ?v0,v1,42f23c <postDataApStatusJson.part.4+0x68>42f224:?? ?3c040061 ?? ?lui?? ?a0,0x61{RPM_AP_ERROR("httpRpmPost error!");42f228:?? ?2484c928 ?? ?addiu?? ?a0,a0,-1404042f22c:?? ?2645cc98 ?? ?addiu?? ?a1,s2,-1316042f230:?? ?8f999e50 ?? ?lw?? ?t9,-25008(gp)42f234:?? ?0810bca3 ?? ?j?? ?42f28c <postDataApStatusJson.part.4+0xb8>42f238:?? ?24060327 ?? ?li?? ?a2,807return RPM_ERROR;}?? ? ?httpStatusSet(reqId, HTTP_OK);42f23c:?? ?8f998088 ?? ?lw?? ?t9,-32632(gp)42f240:?? ?02002021 ?? ?move?? ?a0,s042f244:?? ?0320f809 ?? ?jalr?? ?t942f248:?? ?00002821 ?? ?move?? ?a1,zero42f24c:?? ?8fbc0018 ?? ?lw?? ?gp,24(sp)

    可以看到,C代碼下面跟著一串匯編代碼,而匯編代碼前面有一段地址,這個地址是什么呢?如果熟悉Linux進程空間的概念,很容易就可以聯想到,這個地址其實就是相應的匯編指令在.text段(即代碼段)中的地址。也就是說,這個地址就是我們用于定位具體出錯地點的依據。(4)gdb:可以說是Linux下調試程序最常用的工具,功能強大,操作也很簡單。對于mips程序調試,只需安裝相應的gdb:mips-linux-gdb即可。


    開始調試:

    上面的準備工作都完成后,就可以開始調試了。當進程再次異常終止時,就可以在/tmp目錄下找到coredump文件:比如core.126(進程id為126的進程生成的coredump)。
    用gdb的-c選項打開coredump:mips-linux-gdb -c /tmp/core.126,可以看到如下信息:

    <span style="font-size:14px;">GNU gdb (GDB) 7.4.1 Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. ?Type "show copying" and "show warranty" for details. This GDB was configured as "--host=i686-pc-linux-gnu --target=mips-linux". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Core was generated by `/usr/bin/httpd'. Program terminated with signal 11, Segmentation fault. #0 ?0x2b17ff50 in ?? () (gdb) </span>

    前面是gdb的版本信息,不必理會。我們主要關注下面的內容:
    <span style="font-size:14px;">Core was generated by `/usr/bin/httpd'.
    Program terminated with signal 11, Segmentation fault.
    #0 ?0x2b17ff50 in ?? ()</span>
    表示這個coredump是為進程httpd生成的,而進程退出的原因是signal 11,即SIGSEGV,這正是我們想要的。最后一行,0x2b17ff50是一個地址,這里??的地方本來應該顯示一個函數名,之所以這里沒有顯示,我猜想這應該是一個庫函數,而編譯這個庫時,并沒有帶入-g信息。
    不要緊,接下來只需要輸入where,即可顯示信號產生時程序中止的位置:

    <span style="font-size:14px;">Core was generated by `/usr/bin/httpd'. Program terminated with signal 11, Segmentation fault. #0 ?0x2b17ff50 in ?? () (gdb) where #0 ?0x2b17ff50 in ?? () #1 ?0x0045c034 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?) (gdb) </span>

    ? ? 至此,我們已經拿到最重要的信息:0x0045c034,就是進程中止時停留的位置。對照上面生成的反匯編文件,搜索45c034,即可找到:

    ? ? out = cJSON_Print(root);45c00c:?? ?8f9981ec ?? ?lw?? ?t9,-32276(gp)45c010:?? ?00000000 ?? ?nop45c014:?? ?0320f809 ?? ?jalr?? ?t945c018:?? ?02402021 ?? ?move?? ?a0,s245c01c:?? ?8fbc0010 ?? ?lw?? ?gp,16(sp)httpnPrintf(reqId, strlen(out) + 1, "%s\n", out);45c020:?? ?00402021 ?? ?move?? ?a0,v045c024:?? ?8f999fe4 ?? ?lw?? ?t9,-24604(gp)45c028:?? ?00000000 ?? ?nop45c02c:?? ?0320f809 ?? ?jalr?? ?t945c030:?? ?00408021 ?? ?move?? ?s0,v045c034:?? ?8fbc0010 ?? ?lw?? ?gp,16(sp)45c038:?? ?8fa47e48 ?? ?lw?? ?a0,32328(sp)45c03c:?? ?3c060062 ?? ?lui?? ?a2,0x6245c040:?? ?8f9981f0 ?? ?lw?? ?t9,-32272(gp)45c044:?? ?24450001 ?? ?addiu?? ?a1,v0,145c048:?? ?24c6cdb8 ?? ?addiu?? ?a2,a2,-1287245c04c:?? ?0320f809 ?? ?jalr?? ?t945c050:?? ?02003821 ?? ?move?? ?a3,s045c054:?? ?8fbc0010 ?? ?lw?? ?gp,16(sp)45c058:?? ?00000000 ?? ?nopRPM_MONITORAP_TRACE("\r\n%s\r\n\r\n", out);

    可以看到,出錯時,對應的C函數是httpnPrintf,對應的匯編代碼為:????lw????gp, 16(sp)。

    在反匯編文件中再稍微對照上下文,即可知道具體是哪個模塊、哪個文件中的調用。如果看得懂匯編代碼,基本可以定位到函數中的具體語句,即使看不懂匯編,利用打印調試或者靜態代碼分析等常規調試手段也基本可以定位到具體的出錯原因了。在本例中,最終確定這個函數出錯的原因是操作了調用malloc(0)而獲取的一個空指針(malloc(0)返回什么),著實令人始料未及。
    ?

    不能使用GDB的地方,也可以使用反匯編來定位段錯誤的位置。一般在發生段錯誤的時候,都會有些信息打印出來,如下:

    Oops: Data Abort caused by READ instruction! Fault: Alignment fault pc: 0021034c r0: 20000053 r1: 00000001 r2: 00000000 r3: 20000053 r4: aaaaaaaa r5: 00256984 r6: aaaaaaaa r7: dddddddd r8: aaaaaaaa r9: dddddddd r10: 00060000 fp: 00000000 ip: 00000000 sp: 00240bc0 SPSR: 600000d3

    ? ? 從上面看出,PC就是程序計數器,即程序運行到此位置發生錯誤,然后我們利用objdump將運行的程序反匯編,跟蹤查詢到問題出現的位置。
    版權說明:文章內容轉載自下面文章

    ? ?《ELF文件格式分析》轉載自:https://www.cnblogs.com/cdcode/p/5551649.html
    ? ?《Linux驅動mmap內存映射》轉載自https://www.cnblogs.com/wanghuaijun/p/7624564.html
    ? ?《通過反匯編定位段錯誤》轉載自https://blog.csdn.net/ringrang/article/details/60596846
    ? ?《Linux C 內存管理》轉載自:?https://blog.csdn.net/zqixiao_09/article/details/50381476?
    ? ?《readelf命令和ELF文件詳解》轉載自:https://blog.csdn.net/linux_ever/article/details/78210089?

    《Understanding_ELF.pdf》 是官方elf 文件格式的翻譯,對elf格式有做詳細的介紹。下載地址:https://download.csdn.net/download/li_wen01/12446168

    ?

    ----------------------------------------------------------------2022.08.28----------------------------------------------------------------

    ?新的文章內容和附件工程文件

    已更新在博客首頁和:

    gong 眾 hao :?liwen01

    總結

    以上是生活随笔為你收集整理的Linux内存管理内存映射以及通过反汇编定位内存错误问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    成人国产精品一区二区 | 天天曰天天射 | 亚洲丁香日韩 | 欧美极品一区二区三区 | 欧美日韩精品在线免费观看 | 精品国产aⅴ麻豆 | 成人黄色在线观看视频 | 精品国产一区二区三区在线 | 天堂成人在线 | 日韩精品一区二区久久 | 五月婷婷色 | 看国产黄色片 | 91久久国产精品 | 久章草在线 | 成人h在线观看 | 久草在线免费看视频 | 99re久久资源最新地址 | 色综合久久久久久久久五月 | 欧美伦理一区二区三区 | 久久99久久99精品免观看粉嫩 | 青青久草在线视频 | 五月婷婷开心 | 波多野结衣日韩 | 亚洲国产精品成人va在线观看 | 极品久久久久久久 | 亚洲女人天堂成人av在线 | 2023亚洲精品国偷拍自产在线 | 日韩成人精品在线观看 | 国产最顶级的黄色片在线免费观看 | 少妇搡bbbb搡bbb搡忠贞 | 一区二区三区四区久久 | 亚洲爱爱视频 | 色在线视频 | 国产精品久久网站 | 日韩视频在线观看免费 | 免费av一级电影 | 国产清纯在线 | 婷婷色在线观看 | 国产在线观看高清视频 | 国产色视频网站2 | 精品国产乱码一区二区三区在线 | 国产黄免费在线观看 | 亚洲japanese制服美女 | 久草爱 | 蜜桃传媒一区二区 | 99九九99九九九视频精品 | 三级小视频在线观看 | 黄色成人av | 9色在线视频 | 五月婷婷黄色网 | 久久久久久电影 | 久久久久成人精品 | 网站在线观看日韩 | 在线观看视频一区二区三区 | 天天操天天射天天舔 | 色99中文字幕 | 国产在线播放观看 | 欧洲视频一区 | 天天摸天天舔天天操 | 久草网站在线 | 天天操天天艹 | 亚洲精品视频在线免费 | 激情欧美xxxx | 天天操天天舔天天爽 | 日本黄色片一区二区 | 91精品久久久久久久久久久久久 | 日韩视频在线不卡 | 一级精品视频在线观看宜春院 | 久久五月婷婷丁香 | 欧美日韩国产一区二区三区在线观看 | 五月婷婷色综合 | 日日夜夜精品免费 | 最新av网站在线观看 | 日韩专区在线播放 | www91在线观看 | 日韩精品在线播放 | 国产精品自产拍 | 五月婷丁香 | 99久久久精品 | 国产成人在线播放 | 在线观看视频日韩 | 国产一级特黄毛片在线毛片 | 亚洲乱码精品久久久久 | 天天天天天天天天操 | 欧美老女人xx | 四虎成人精品永久免费av | 99久久精品国产一区二区三区 | 久久蜜臀一区二区三区av | 日韩av在线资源 | 在线国产中文字幕 | 在线观看成人国产 | 国产在线视频导航 | 91丨九色丨蝌蚪丰满 | 日韩色一区二区三区 | 视频国产在线观看18 | av一区二区三区在线观看 | 精品一区电影 | 午夜手机看片 | 成人试看120秒 | 中文字幕永久在线 | 日韩黄色免费电影 | 高清有码中文字幕 | 欧美一级视频免费看 | 97理论片| 国产中文在线字幕 | 亚洲精品免费观看视频 | 国产 日韩 中文字幕 | 欧美午夜视频在线 | 超碰97人 | 在线免费观看麻豆视频 | 免费a级大片 | 国产1区2区3区精品美女 | 日日日操操 | 免费a网 | 91麻豆精品国产午夜天堂 | 久久综合婷婷 | 国产青春久久久国产毛片 | 一本色道久久综合亚洲二区三区 | 韩国一区二区三区在线观看 | 最新久久免费视频 | 久久久久久毛片 | 中文字幕视频三区 | 狠狠狠操 | 亚洲国产成人高清精品 | 91香蕉视频好色先生 | 黄毛片在线观看 | 精品欧美一区二区精品久久 | av免费网站在线观看 | 亚洲国产日韩一区 | 国产精品国产亚洲精品看不卡 | 久久在线影院 | 日女人电影 | 国产视频久久久 | 国产黄色视 | 亚洲热视频 | 国产网红在线观看 | 一区二区三区四区久久 | 丝袜美女在线观看 | 特级大胆西西4444www | 91在线公开视频 | 午夜少妇一区二区三区 | 精品一区二区在线免费观看 | 亚洲综合色网站 | 91传媒视频在线观看 | 久久电影中文字幕视频 | 久久国产精品视频免费看 | 岛国片在线| 天天操综合网站 | 国产很黄很色的视频 | 午夜精品一区二区三区免费视频 | 天天操夜夜看 | 色偷偷男人的天堂av | 久久久亚洲国产精品麻豆综合天堂 | 欧美极度另类性三渗透 | 久久网页 | 中国一级片免费看 | 日日天天 | 最近2019好看的中文字幕免费 | 亚洲免费精品一区二区 | 国产成人精品日本亚洲999 | 日韩免费网站 | 一级片免费观看 | 久免费| 欧美黑人性爽 | 国产精品观看视频 | 日韩最新中文字幕 | 精品久久久999 | 日韩在线观看中文字幕 | 99精品视频精品精品视频 | 又湿又紧又大又爽a视频国产 | 色网站在线免费 | 久久成人一区 | 超碰在线最新地址 | 国产欧美综合视频 | 欧美激情在线看 | 欧美极品少妇xbxb性爽爽视频 | 久艹视频免费观看 | 九九九热精品免费视频观看 | 九九久久久久久久久激情 | 日韩欧美高清一区二区 | 免费又黄又爽视频 | 亚洲精品18日本一区app | 国产精品一区二区三区电影 | 99re久久资源最新地址 | 久久久精品成人 | 国产视频在线观看一区 | 黄色在线观看污 | 99热国产在线 | 碰碰影院 | 成 人 黄 色视频免费播放 | 国产精品久久99综合免费观看尤物 | 国产精品久久久久久一区二区三区 | 成人h电影在线观看 | 久久涩视频 | 国产亚洲精品无 | 97人人模人人爽人人少妇 | 国产一级黄色免费看 | 国产精品日韩 | 日韩免费大片 | 久久九九精品久久 | 在线看免费 | 日韩深夜在线观看 | 99人成在线观看视频 | 亚州精品在线视频 | 午夜av在线电影 | 成人性生交大片免费看中文网站 | 亚洲一区天堂 | 欧美日韩另类在线 | 久久久www成人免费毛片 | 欧美国产日韩一区二区 | 日本xxxx裸体xxxx17 | 亚洲精品综合在线观看 | 色诱亚洲精品久久久久久 | 婷婷色六月天 | 最近免费中文字幕大全高清10 | 在线精品亚洲一区二区 | 亚洲国产中文字幕在线视频综合 | 91麻豆精品国产自产在线 | 国产小视频免费在线观看 | 久久婷婷色 | 亚洲理论在线观看 | 欧美a级在线 | 亚洲一区日韩精品 | 日韩av电影中文字幕 | 国产精品亚洲片夜色在线 | av免费片| 一区二区三区在线观看 | 精品一区二区在线免费观看 | 欧美另类性 | 久久成人18免费网站 | 中文字幕 成人 | 国产一区二区三区高清播放 | 麻豆成人小视频 | 免费观看xxxx9999片 | 国产精品精品国产婷婷这里av | 日韩电影一区二区三区 | 天天草天天操 | 在线免费黄色毛片 | 色婷婷综合视频在线观看 | 香蕉视频国产在线 | 国产69久久精品成人看 | 久久久久久免费毛片精品 | 91九色免费视频 | 久久久久久免费 | 久草色在线观看 | 欧美日韩亚洲在线 | 久久性生活片 | 99re久久资源最新地址 | 色永久免费视频 | 国产成人av在线影院 | 免费观看日韩 | 中文字幕第一页av | 中文字幕字幕中文 | 激情六月婷婷久久 | 国产精品原创av片国产免费 | 久久久久久久影院 | 婷婷在线网站 | 91尤物在线播放 | 日韩免费一级a毛片在线播放一级 | 中文字幕2021 | 亚洲综合国产精品 | 亚洲少妇自拍 | 久久国产美女视频 | 少妇资源站 | 日韩高清免费无专码区 | 99精品久久99久久久久 | 免费看黄在线看 | 国产精品久久99综合免费观看尤物 | 亚洲日本欧美在线 | 91资源在线 | 久久久久国产一区二区三区 | 免费看wwwwwwwwwww的视频 久久久久久99精品 91中文字幕视频 | 欧美一二三在线 | 在线观av | 蜜臀久久99精品久久久无需会员 | 国产精品色在线 | 在线中文字幕av观看 | 免费精品在线视频 | 成人免费毛片aaaaaa片 | 久久综合之合合综合久久 | 欧洲一区二区在线观看 | 欧美日韩不卡一区 | 久久久久久欧美二区电影网 | 色99视频 | 丁香激情婷婷 | 色狠狠干 | 色精品视频 | 狠狠狠色狠狠色综合 | 麻豆免费看片 | 国产亚洲欧美日韩高清 | 国产在线国产 | 处女av在线 | 综合色站导航 | 日韩91精品 | 欧美日韩有码 | 欧美小视频在线观看 | 欧美一区二区三区在线观看 | 久草在线高清 | 丁香婷婷深情五月亚洲 | 激情影院在线观看 | www视频在线播放 | 久久av伊人 | 免费日韩一区二区 | 日韩欧美aaa| 日韩乱理 | 精品中文字幕在线播放 | 五月婷婷激情 | 久久精品国产亚洲aⅴ | 免费av片在线 | 亚洲成aⅴ人片久久青草影院 | 久久视频在线 | 久久官网| 久久一区精品 | 中文字幕 国产视频 | 国产一区免费观看 | 国产亚洲人 | 一区二区影视 | 91手机视频 | 久久精品视频在线播放 | 欧美综合在线观看 | 久久久亚洲成人 | 中文字幕xxxx | 乱男乱女www7788 | 国内精品久久久久久久久久久久 | a视频免费在线观看 | 五月婷影院 | 欧美成年人在线观看 | 黄色免费视频在线观看 | av大全在线看 | 人人干人人搞 | 久久精品视频2 | 在线色吧| av3级在线| 在线免费黄色av | 2021国产在线 | 久久精品视频4 | 免费在线观看污网站 | 综合网五月天 | 欧美日韩国产一区二区三区在线观看 | 久久69精品久久久久久久电影好 | 国产成人精品久久久 | 国产专区在线 | 久草在线在线精品观看 | 久久在线观看视频 | 久久免费视频这里只有精品 | 色婷婷在线视频 | 伊人久久国产精品 | 国产午夜精品一区二区三区 | 欧美福利久久 | 亚洲精品乱码白浆高清久久久久久 | 国产一区二区久久久久 | 欧美夫妻生活视频 | 九七视频在线观看 | 成人免费观看大片 | 美女天天操| 欧美最新另类人妖 | 99国产免费网址 | 欧美综合久久 | 免费在线观看91 | 日韩免费播放 | 一区二区三区免费在线观看视频 | 国产成人精品av在线 | 天天爱av导航| 成人午夜免费福利 | 日韩av片无码一区二区不卡电影 | 91视频免费网址 | 特级西西444www大精品视频免费看 | 成年美女黄网站色大片免费看 | 日韩欧美xxx| 韩国av一区二区 | 久久免费公开视频 | 91精品国产一区二区在线观看 | 久久亚洲专区 | 综合精品久久久 | 久久 亚洲视频 | 亚洲免费观看在线视频 | 国产成人精品一二三区 | 欧美成人猛片 | 特级西西人体444是什么意思 | 日韩一区正在播放 | 国产精品成人国产乱 | 国产一区二区免费看 | 亚洲精品黄色在线观看 | 日韩精品免费在线观看视频 | 2024国产精品视频 | 精品视频免费在线 | 日韩精品免费在线视频 | 一区二区中文字幕在线 | 在线免费av播放 | 日本h视频在线观看 | 国产99免费视频 | 中文字幕国产亚洲 | 国产99区| 激情网综合 | 日韩资源在线 | 成人一级免费电影 | 九九天堂| 97电影手机 | 黄色一级大片免费看 | 天天干夜夜擦 | 色婷婷综合久色 | 中文字幕欧美日韩va免费视频 | 国产成人三级一区二区在线观看一 | 精品久久99| 在线看片91 | 一级片色播影院 | 亚洲精品美女在线观看 | 91视频一8mav | 久久艹99 | 欧美日韩国产三级 | 国产视频精品免费 | 欧美精品一区二区蜜臀亚洲 | 五月色丁香 | 国产精品久久久久久久久久久久午夜片 | 一级欧美黄| 在线天堂v| 欧美日韩中文字幕在线视频 | 日韩av在线网站 | 国产无遮挡又黄又爽在线观看 | 玖玖爱在线观看 | 欧美一级电影片 | 成人黄色短片 | 国产精品一区二区三区四区在线观看 | 精品黄色视| 麻豆94tv免费版 | 天堂av在线免费 | 91精品啪 | 天堂av在线免费 | 日韩欧美在线影院 | 成人黄色大片 | 久久国产一二区 | 福利视频网站 | 五月天综合 | 国色天香在线观看 | 伊人影院99 | 日本性高潮视频 | 亚洲国产资源 | 国产一区二区在线影院 | 天堂在线免费视频 | 久久综合婷婷综合 | 婷婷 综合 色 | 看黄色.com | 亚一亚二国产专区 | 九九综合九九综合 | 国产 欧美 日韩 | av福利在线导航 | 在线看成人 | www.午夜| 久久av免费观看 | 在线免费中文字幕 | 中文一区在线观看 | 久久在线播放 | 色在线亚洲 | 在线高清 | 国产极品尤物在线 | 久久精品视频免费观看 | 在线观看国产成人av片 | 日韩精品免费在线播放 | 黄色电影网站在线观看 | 成年人国产在线观看 | 久久国产精品免费一区 | 精品在线观 | 香蕉在线视频观看 | 久久久免费播放 | 婷婷深爱激情 | 热精品 | 成年人免费电影在线观看 | a久久久久| 在线色亚洲 | 97香蕉超级碰碰久久免费软件 | 福利一区二区三区四区 | 一区二区中文字幕在线 | 人人干在线观看 | 国产理论免费 | 亚洲首页| 亚洲理论片在线观看 | 五月天视频网 | 黄色的视频网站 | av免费片 | 色婷婷亚洲婷婷 | 日韩色高清 | 亚洲va欧美va人人爽春色影视 | 西西444www大胆高清视频 | adn—256中文在线观看 | 在线免费av观看 | 日韩视频在线观看视频 | 欧美a级成人淫片免费看 | 国产无遮挡又黄又爽在线观看 | 黄色毛片电影 | 国产一区免费在线观看 | 91插插插网站 | av超碰免费在线 | 日日夜夜天天人人 | 精品在线观看一区二区 | av先锋影音少妇 | 天天色综合天天 | 91免费的视频在线播放 | 一区二区三区日韩在线 | 亚洲永久精品在线 | 亚洲 成人 一区 | 成人黄色一级视频 | 亚洲另类人人澡 | 麻豆影视在线免费观看 | 天天插夜夜操 | 欧美不卡在线 | 久久影院中文字幕 | 久草| 久久精品国产一区 | 7777xxxx| 国产一区二区中文字幕 | 手机看片午夜 | 婷婷在线视频 | 精品欧美在线视频 | 久久夜色精品国产欧美一区麻豆 | 97精品在线视频 | 日韩中文字幕免费 | 麻豆91精品| 久久99热这里只有精品国产 | 综合网伊人 | 人人狠狠综合久久亚洲 | 亚洲精欧美一区二区精品 | 国产精品久久久久影视 | 九九热精品视频在线观看 | 9999亚洲| 国产欧美日韩一区 | 特级毛片在线免费观看 | 最新国产精品亚洲 | 91九色视频网站 | 91视频大全| 最近日本韩国中文字幕 | 亚洲精品国偷拍自产在线观看蜜桃 | 97天天综合网 | 亚洲欧美日韩国产精品一区午夜 | 色香蕉网 | 天天摸天天干天天操天天射 | 国产在线观看污片 | 91av久久 | 国产精品免费观看网站 | 国产精品永久免费在线 | 激情综合亚洲 | 亚洲综合在线发布 | 狠狠撸电影 | 亚洲极色 | 毛片3| 国内成人精品2018免费看 | 国产综合小视频 | 在线观看中文字幕av | 日韩av中文 | 嫩小bbbb摸bbb摸bbb | 欧美亚洲久久 | 999在线精品 | 国产精品原创 | 日批视频 | 激情综合一区 | 欧美一级视频一区 | 在线视频日韩欧美 | 91精品一区在线观看 | 国产精品亚洲成人 | 亚洲黄色一级大片 | 国产视频 亚洲视频 | www.天天综合 | 亚洲人成影院在线 | 特级毛片在线 | 国产精品一区二区免费看 | 天天草天天干天天射 | 久久的色 | 综合久久久久久久久 | 91免费观看视频网站 | 亚洲成aⅴ人在线观看 | 国产区在线视频 | 99热官网| 在线之家免费在线观看电影 | 天天射天天艹 | av网站大全免费 | www国产亚洲精品久久网站 | 91一区二区三区在线观看 | 国产精品久久嫩一区二区免费 | 免费日韩一区二区 | 午夜 在线| www.日日日.com| 亚洲精品黄色 | 黄色资源网站 | 天天做天天爱天天爽综合网 | 国产高清中文字幕 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 国产一级黄色电影 | 国产成人精品av | 九九九在线观看视频 | 91插插插网站 | 91黄色在线视频 | 9797在线看片亚洲精品 | 中文字幕中文字幕 | 久久综合色8888 | 亚洲国产成人精品电影在线观看 | 欧美日韩视频在线一区 | a级成人毛片 | 日韩色一区二区三区 | 国产.精品.日韩.另类.中文.在线.播放 | 四虎永久网站 | 中文字幕乱码一区二区 | av黄网站| a天堂一码二码专区 | 亚洲黄色在线播放 | 日韩av美女| 婷婷性综合 | 国产小视频福利在线 | 国产在线精品区 | 97超碰人人澡人人爱学生 | 国产中文字幕三区 | www.久久婷婷| 热久久免费国产视频 | 精品在线小视频 | 美女视频久久黄 | 粉嫩一区二区三区粉嫩91 | 国产精品久久久久久久久久新婚 | 福利网址在线观看 | 菠萝菠萝在线精品视频 | 99精品视频在线观看免费 | 国模吧一区 | 日一日干一干 | 国产精品岛国久久久久久久久红粉 | 91精品国产自产在线观看 | 亚洲精品中文字幕在线观看 | 激情综合狠狠 | 天天操天天操天天操天天操天天操天天操 | 亚洲精品综合一二三区在线观看 | 丁香高清视频在线看看 | 免费福利在线播放 | 成人午夜久久 | 日韩欧美网站 | 亚洲一级黄色av | 99久视频 | 亚洲电影久久 | 亚洲日本va午夜在线影院 | 国产美女无遮挡永久免费 | 色视频在线免费 | 在线观看mv的中文字幕网站 | 久久99久久99精品 | 国产精品免费一区二区三区在线观看 | 成人在线视频在线观看 | 在线v | 色伊人网 | 狠狠色丁香婷婷综合欧美 | 中文字幕av有码 | 欧美日韩免费在线视频 | 国产 日韩 在线 亚洲 字幕 中文 | 黄色在线免费观看网址 | 久草久草久草久草 | 黄色成人免费电影 | 国产小视频在线免费观看 | 国产美女在线精品免费观看 | 91网页版免费观看 | 91成人在线网站 | 日韩精品视频在线观看网址 | 99视频在线免费 | 中文字幕精品一区久久久久 | 亚洲老妇xxxxxx | 三级黄色在线观看 | 国产精品久久在线观看 | 国产精品区在线观看 | 中文字幕在线观看第一区 | 久草视频免费在线播放 | 欧美亚洲成人免费 | 欧美一区视频 | 国内精品久久天天躁人人爽 | 久久精品99国产精品 | 亚洲欧洲国产日韩精品 | 国产午夜精品av一区二区 | 中文字幕第一页在线播放 | 在线观看一二三区 | 在线观看成年人 | 亚洲国产精品激情在线观看 | 在线观看免费黄视频 | 婷婷色五 | 五月婷婷在线视频观看 | 五月婷婷一区 | 99精品国产一区二区三区不卡 | 色婷婷成人| 欧美性色xo影院 | 在线v| 亚洲天堂网视频在线观看 | 精品在线观看国产 | 亚洲播放一区 | 国内精品久久久久影院优 | 天天操天天射天天操 | 在线视频免费观看 | 啪啪肉肉污av国网站 | 日韩精品一区二区三区在线视频 | 国产黄色成人 | 亚洲国产剧情 | 97超在线 | av在线免费网站 | 久久久久亚洲天堂 | 免费观看91视频大全 | 日日夜色| 一区二区三区电影大全 | 日本激情中文字幕 | 国产精品麻豆果冻传媒在线播放 | 国产精品久久久久影视 | 久草在线手机观看 | 日韩电影中文字幕 | 伊人天堂久久 | 久久精品欧美一 | 国模精品一区二区三区 | 久久精品国产免费看久久精品 | 国产婷婷在线观看 | 国产精品一码二码三码在线 | 在线观看视频你懂得 | 在线影院av | 精品久久久国产 | 亚洲成人精品在线 | 成人永久在线 | 中文字幕久久精品亚洲乱码 | 在线天堂日本 | 午夜资源站 | 国产精品一区免费看8c0m | 五月天亚洲激情 | 182午夜在线观看 | 特黄特黄的视频 | 久草av在线播放 | 插久久 | 午夜丁香视频在线观看 | av免费播放 | 亚洲综合导航 | 亚洲 欧美 综合 在线 精品 | 在线观看视频亚洲 | 天天综合网~永久入口 | 人人澡人人干 | 国产你懂的在线 | 国精产品一二三线999 | 国产黄色观看 | 人人爱人人做人人爽 | 久久99精品久久久久久 | 国产婷婷精品av在线 | 日韩在线视频精品 | 成年人免费观看在线视频 | 久久99久久精品 | a午夜在线 | 午夜 免费 | 91在线91拍拍在线91 | 夜夜嗨av色一区二区不卡 | 91精品夜夜| 亚洲五月婷婷 | 中文字幕一区二区三区在线视频 | 国产一级二级视频 | 欧美日韩精品免费观看视频 | 国产亚洲欧美精品久久久久久 | 久久久久国产精品午夜一区 | 免费观看视频黄 | 亚洲国产精品女人久久久 | 免费日韩一区二区三区 | 日韩成人免费在线观看 | 久久久国产网站 | 一区免费观看 | 日本最新高清不卡中文字幕 | 精品国产_亚洲人成在线 | 亚洲午夜激情网 | 国产一区网址 | 日韩欧美综合视频 | 中文字幕成人在线 | 天天草天天干天天射 | 精品久久久久久久久久久院品网 | 天天摸天天干天天操天天射 | 欧美性色xo影院 | 亚洲一区免费在线 | 天天色天天射综合网 | 欧美一区二区三区在线看 | 美女网站在线观看 | 在线观看一 | 黄色一级影院 | 丝袜少妇在线 | 精品国产一区二区三区免费 | 免费人成在线观看网站 | 在线观看亚洲a | 欧美一级淫片videoshd | 久久视频在线观看免费 | 手机看片99 | 欧美一区日韩精品 | 九色在线| 欧美激情视频在线免费观看 | 国产日韩三级 | 日av免费| 免费看搞黄视频网站 | 欧美精品在线一区二区 | 日本在线观看中文字幕无线观看 | 成人av电影免费在线播放 | 成片免费观看视频 | 久久久久久久久久久久99 | 久久精品欧美 | 亚洲国产精品久久久久婷婷884 | 亚洲精品视频在线观看免费视频 | wwwwww国产 | 天天干天天射天天操 | 婷婷丁香导航 | 99re8这里有精品热视频免费 | 成人看片 | 国产一区二区综合 | 天天色播 | 国产电影黄色av | 91av国产视频| 日韩动漫免费观看高清完整版在线观看 | 国产午夜精品在线 | 欧美性生活小视频 | www.国产在线| 国产精品久久久久久久免费大片 | 国产久草在线观看 | 91精品久| 中文字幕在线观看你懂的 | 亚洲天堂在线观看完整版 | 国产一区二区观看 | 欧美日韩国产成人 | 久久久麻豆精品一区二区 | 久久久久久久久艹 | 日日爱视频 | 国产成人亚洲精品自产在线 | 亚洲国产成人av网 | 在线欧美小视频 | 成人一级电影在线观看 | 成人va视频 | 亚洲人人网| 91看片麻豆 | 国产视频二| 中文字幕亚洲欧美 | 免费三及片 | 日日爱av| 久久免费成人精品视频 | 99精品视频免费 | 亚洲天堂网在线观看视频 | 国产麻豆精品免费视频 | 最新日韩精品 | 久久久久久久影视 | 亚洲精品玖玖玖av在线看 | 国产免费亚洲 | 国产第一页在线观看 | 国产一区免费在线 | 人人添人人澡 | 色大片免费看 | 久久精品精品 | 爱色婷婷| 日韩av免费大片 | 婷婷综合伊人 | 亚洲激情一区二区三区 | 亚洲日韩精品欧美一区二区 | 国产69精品久久久久久 | 久久精品中文 | 一级黄色免费 | 91精品在线看 | 国产精品99久久久久人中文网介绍 | 国产精品一区二区在线免费观看 | 中文字幕电影高清在线观看 | 亚洲成av人片 | 国产精品久久久久免费a∨ 欧美一级性生活片 | 91在线蜜桃臀 | 国产精品毛片久久久 | 久久视频在线 | 精品久久久久国产免费第一页 | 国产精品999久久久 久产久精国产品 | 国产精品乱码一区二区视频 | 中文字幕中文字幕在线中文字幕三区 | 成人蜜桃网 | 久久亚洲福利 | 色婷婷狠狠五月综合天色拍 | 亚洲精品欧美专区 | 日韩高清一二区 | 激情视频免费观看 | 亚洲区精品视频 | 午夜影院三级 | 亚洲在线国产 | 在线观看中文字幕 | av色综合| 久久亚洲日本 | 久久久国际精品 | 成人不用播放器 | 久久久久久久久久网 | 免费看成人a | 日韩欧美精品在线观看 | 五月丁香 | 久久久久国产精品厨房 | 中文字幕在线免费看 | 日本资源中文字幕在线 | 国产一区二区影院 | 欧美日韩久 | 91麻豆免费视频 | 在线观看一级片 | av在线播放中文字幕 | 久久免费片 | 国产黄在线 | 久久精品小视频 | 久久综合福利 | 在线观看一 | 久久免费国产 | 91手机视频 | 午夜在线免费观看 | 999久久久久久 | 久久婷婷激情 | 伊人狠狠色 | 日韩视频免费 | 91av成人| 精品久久国产一区 | 欧美激情在线网站 | 不卡视频在线 | 日批在线观看 | 久久久久综合精品福利啪啪 | 黄色高清视频在线观看 | 成人9ⅰ免费影视网站 | 色成人亚洲网 | av超碰在线 | 精品uu| av电影一区二区 | 人人揉人人揉人人揉人人揉97 | 欧美亚洲另类在线视频 | 黄污视频网站 | www,黄视频| a在线免费观看视频 | 亚洲乱码精品久久久 | 欧美a在线看 | 免费av一级电影 | 国产黄色片在线免费观看 | 996久久国产精品线观看 | 国产欧美精品xxxx另类 | 久久综合国产伦精品免费 | 欧美久久久久久久久久久久 | 2023天天干 | 久久曰视频| 国产精品高清免费在线观看 | 爱av在线网 | 午夜.dj高清免费观看视频 | 国产青青青 | 四虎国产精品永久在线国在线 | 精品9999| 狠狠躁夜夜躁人人爽超碰91 | 又黄又刺激视频 | 亚洲国产精品人久久电影 | 国产久视频 | 亚洲天堂网视频 | 国产小视频在线免费观看视频 | 丁香九月婷婷 | 国产精品亚洲a | 日韩精品欧美视频 | 成人a视频在线观看 | 欧美91片 | 国产五月天婷婷 | 久久成人一区二区 | 欧美精品一区二区在线播放 | 美女免费视频黄 | 国产精品高潮在线观看 | 亚洲综合小说 | a级国产乱理论片在线观看 特级毛片在线观看 | 国产成人一区二区三区久久精品 | 91毛片在线 | 国产精品毛片久久久久久 | 97超碰人人澡人人爱学生 | 久久综合天天 | 91在线精品一区二区 | 国产小视频在线看 | 久久久www成人免费毛片 | 色a网 | 日韩久久一区二区 | www.久久免费视频 | 亚洲在线综合 | 亚洲国产播放 | 99久久精品国产欧美主题曲 | 精品久久久久免费极品大片 | 天天干天天爽 | 91亚洲精品久久久中文字幕 | 尤物一区二区三区 | 亚洲精品国产自产拍在线观看 | 韩日视频在线 | 大胆欧美gogo免费视频一二区 | 精品国产一二三四区 | av片子在线观看 | 青青河边草免费观看完整版高清 | 91麻豆操| 国产中文伊人 | 依人成人综合网 | 天天插天天干天天操 | 久久久精品网站 | 免费观看黄| 91成人精品在线 | 中文字幕在线观看免费观看 | 亚洲黄色片| 成人国产精品久久久 | 久久在线观看 | 日韩 国产 | 国产色在线视频 | 亚洲伊人婷婷 | 中文字幕在线免费 | 深爱五月激情五月 | 欧美日视频 | av 一区二区三区四区 | 久久国产一区二区三区 | 亚洲四虎影院 | 国产一级性生活视频 | 偷拍精品一区二区三区 | 特黄一级毛片 | 午夜神马福利 | 久久久网站 | 这里有精品在线视频 |