日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

uboot2012(一)分析重定位

發布時間:2025/4/5 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uboot2012(一)分析重定位 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 引入
  • 環境配置
  • 編譯體驗
  • 入口查找
  • 代碼分析
  • board_init_f
  • pie
  • 內存分布分析
  • SP設置
  • board_init_f
  • 重定位
  • 代碼段重定位實現
  • 變量地址修改
  • 參考

引入

關于移植,搜索關鍵英文詞語portting

移植簡單的介紹在readme中,手冊是它的使用幫助

代碼倉庫地址 02-uboot重定位加入自己的代碼

環境配置

這里使用編譯工具arm-linux-gcc-4.3.2.tar,具體安裝參考更換gcc工具鏈.md

編譯體驗

make smdk2410_configmake

入口查找

我們可以從頂層Makefile開始分析,也可以直接看到編譯結果,查看最后的鏈接過程如下,搜索arm-linux-

arm-linux-ld -pie -T u-boot.lds -Bstatic -Ttext 0x0 $UNDEF_SYM arch/arm/cpu/arm920t/start.o --start-group api/libapi.o

可以看到這里指定了鏈接腳本以及代碼段的地址在-Ttext 0,第一個文件是arch/arm/cpu/arm920t/start.o

代碼分析

簡單的流程如下:

  • set the cpu to SVC32 mode
  • close watchdog
  • mask all IRQs by setting all bits in the INTMR
  • FCLK:HCLK:PCLK = 1:2:4
  • cpu_init_crit
  • flush v4 I/D caches
  • disable MMU stuff and caches
  • lowlevel_init
  • memory control configuration [set sdram]
  • set sp [Set stackpointer in internal RAM]
  • board_init_f
  • init_sequence
  • board_early_init_f
  • set clock
  • gpio
  • ----....---
  • relocate_code
  • copy_loop 復制代碼
  • fixloop 修改全局變量等數據段
  • clear_bss
  • board_init_f

    這里有一個關鍵的變量gd,可以看到文件頭上面有個宏DECLARE_GLOBAL_DATA_PTR,定義如下

    #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

    也就是定義gd為寄存器變量r8,可以看到編譯輸出有這么一句,表示編譯器不使用r8寄存器

    -fno-common -ffixed-r8

    pie

    新版本的uboot是在初始化完執行完board_init_f后進行重定位代碼,但是代碼本身的鏈接腳本就是在0地址的,那么它為什么需要再重定位代碼?

    我們可以看到鏈接命令輸出如下

    arm-linux-ld -pie -T u-boot.lds -Bstatic -Ttext 0x0 $UNDEF_SYM arch/arm/cpu/arm920t/start.o --start-group api/libapi.o

    搜索下pie相關的內容,就是說創建位置無關可執行程序

    $ arm-linux-ld --help | grep "pie"-pie, --pic-executable Create a position independent executable

    內存分布分析

    我們從進入c函數的地方開始分析,前面的就不看了,c函數跳轉首先需要這是sp

    SP設置

    到設置堆棧的代碼為入口分析

    /* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f:ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ldr r0,=0x00000000bl board_init_f

    這里設置sp=CONFIG_SYS_INIT_SP_ADDR,我們可以查看反匯編得到實際的值是0x30000f80

    00000098 <call_board_init_f>:98: e59fd3d8 ldr sp, [pc, #984] ; 478 <fiq+0x58>9c: e3cdd007 bic sp, sp, #7 ; 0x7a0: e3a00000 mov r0, #0 ; 0x0a4: eb0007f1 bl 2070 <board_init_f>478: 30000f80 .word 0x30000f80

    接下來仔細看下代碼,這里有個宏DEFINE,目的是將后面的參數1作為一個宏傳遞給匯編文件,值是第二個參數,這里的值就是global_data也就是gd_t向上16對齊

    #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */ #define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */ #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - \GENERATED_GBL_DATA_SIZE)lib\asm-offsets.cDEFINE(GENERATED_GBL_DATA_SIZE,(sizeof(struct global_data) + 15) & ~15);typedef struct global_data {bd_t *bd;unsigned long flags;unsigned long baudrate;unsigned long have_console; /* serial_init() was called */unsigned long env_addr; /* Address of Environment struct */unsigned long env_valid; /* Checksum of Environment valid? */unsigned long fb_base; /* base address of frame buffer */#ifdef CONFIG_ARM/* "static data" needed by most of timer.c on ARM platforms */unsigned long timer_rate_hz;unsigned long tbl;unsigned long tbu;unsigned long long timer_reset_value;unsigned long lastinc; #endifunsigned long relocaddr; /* Start address of U-Boot in RAM */phys_size_t ram_size; /* RAM size */unsigned long mon_len; /* monitor len */unsigned long irq_sp; /* irq stack pointer */unsigned long start_addr_sp; /* start_addr_stackpointer */unsigned long reloc_off; #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))unsigned long tlb_addr; #endifconst void *fdt_blob; /* Our device tree, NULL if none */void **jt; /* jump table */char env_buf[32]; /* buffer for getenv() before reloc. */ } gd_t;

    剛開始的時候,沒有考慮到CONFIG_ARM,計算出來的值與實際對不上,后來仔細搜索發現是定義了的

    # grep -nR "CONFIG_ARM" ./ ./arch/arm/config.mk:34:PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__ ./include/autoconf.mk:126:CONFIG_ARM=y

    這里理論計算的就是22*4+32=120===+15&-15=128

    最終sp=0x30000000+0x1000-128=0x30000F80與匯編結果是一致的

    接著我們仔細看下這個來自內核的宏DEFINE,它是嵌入匯編,實際上他是編譯不了的,因為沒有->指令,實際上,這個asm-offsets.c文件根本不是用來運行的,只是在編譯的時候,用它生成一個asm-offsets.s文件,然后Kbuild會處理這個asm-offsets.s文件,生成asm-offsets.h文件。這個頭文件最終被匯編文件引用,其中定義的變量最終得到應用。
    如果要在自己的內核模塊中使用這些變量,引用這個頭文件即可#include <asm/asm-offsets.h>. 摘自內核黑科技之DEFINE宏

    include\linux\kbuild.h #define DEFINE(sym, val) \asm volatile("\n->" #sym " %0 " #val : : "i" (val))

    后來在編譯結果中搜索CONFIG_SYS_INIT_SP_ADDR

    ./include/generated/generic-asm-offsets.h:10:#define GENERATED_GBL_DATA_SIZE (128) /* (sizeof(struct global_data) + 15) & ~15 */

    其實這個是我看了說明之后才去搜索的,這個DEFINE就是根據lib/asm-offsets.c生成asm-offsets.h

    $ cat asm-offsets.h #ifndef DO_DEPS_ONLY#include <generated/generic-asm-offsets.h> /* #include <generated/asm-offsets.h> */#endif

    最終的宏也就在generated/generic-asm-offsets.h中了

    #define GENERATED_GBL_DATA_SIZE (128) /* (sizeof(struct global_data) + 15) & ~15 */ #define GENERATED_BD_INFO_SIZE (48) /* (sizeof(struct bd_info) + 15) & ~15 */

    接下來就是初始化以及內存分布設置

    board_init_f

    簡單的代碼解釋如下

    call_board_init_f:ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)bic sp, sp, #7 /* 8-byte alignment for ABI compliance 清除低3位 */ldr r0,=0x00000000bl board_init_fboard_init_f /*這里指的就是棧頂 調用之前是 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 */gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);gd->mon_len = _bss_end_ofs; /**bss_end-start 就是整個程序的大小//*一些初始化操作*/*init_sequence()addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; //這個ram_size在dram_init 初始化為64M,CONFIG_SYS_SDRAM_BASE 也就是sdram基地址//addr 就是ram最高地址 0x04000000+0x3000,0000/* reserve TLB table */ //保留4kb給tlb后向下64kb對齊,也就是消除0xffff 0x3400,0000-0x4000=0x33ffc,0000addr -= (4096 * 4); // 對齊后就是 0x33ff,0000 /* round down to next 64 kB limit */addr &= ~(0x10000 - 1);gd->tlb_addr = addr;/* //代碼段,gd->mon_len=_bss_end_ofs* reserve memory for U-Boot code, data & bss* round down to next 4 kB limit*/addr -= gd->mon_len;addr &= ~(4096 - 1);/** reserve memory for malloc() arena*/addr_sp = addr - TOTAL_MALLOC_LEN;/** (permanently) allocate a Board Info struct* and a permanent copy of the "global" data*/addr_sp -= sizeof (bd_t);bd = (bd_t *) addr_sp;gd->bd = bd; addr_sp -= sizeof (gd_t);id = (gd_t *) addr_sp;/* leave 3 words for abort-stack */addr_sp -= 12;/* 8-byte alignment for ABI compliance */addr_sp &= ~0x07;gd->relocaddr = addr;gd->start_addr_sp = addr_sp;gd->reloc_off = addr - _TEXT_BASE;memcpy(id, (void *)gd, sizeof(gd_t));/*這里最后進行重定位代碼 */relocate_code(addr_sp, id, addr);relocate_code:mov r4, r0 /* save addr_sp */mov r5, r1 /* save addr of gd */mov r6, r2 /* save addr of destination */int dram_init(void) {/* dram_init must store complete ramsize in gd->ram_size */gd->ram_size = PHYS_SDRAM_1_SIZE; //64Mreturn 0; }

    最后的分配內存如下

    重定位

    uboot的鏈接地址是0,那么它剛開始在nor上運行是能夠讀取指令運行的,但是他的變量怎么辦呢?

    比如我有一個變量在0x100,需要修改,nor上的數據并不能像內存一樣修改?那么怎么解決呢?

    這里我們假設想要在sdram上運行,那么我們搬運到0x3200,0000上去,那么我們不僅需要搬運代碼,還要修改代碼也就是說將變量0x100變為0x3200,0100

    那么我們怎么知道舊變量的地址0x100,這里就是在鏈接的時候加入pie選項,會有新的段生成,可以看下lds文件

    .rel.dyn : {__rel_dyn_start = .;*(.rel*)__rel_dyn_end = .;}.dynsym : {__dynsym_start = .;*(.dynsym)}

    代碼段重定位實現

    代碼重定位是在board_init_f中調用的

    // addr_sp 最后的sp // id gd結構的位置 // addr 重定位的位置 relocate_code(addr_sp, id, addr)

    我們來計算下實際的代碼段加bss段的大小

    .globl _bss_start_ofs _bss_start_ofs:.word __bss_start - _start.globl _bss_end_ofs _bss_end_ofs:.word __bss_end__ - _start.globl _end_ofs _end_ofs:.word _end - _start// 查看具體的反匯編00000040 <_TEXT_BASE>:40: 00000000 .word 0x0000000000000044 <_bss_start_ofs>:44: 0006b568 .word 0x0006b56800000048 <_bss_end_ofs>:48: 000ae4e0 .word 0x000ae4e00000004c <_end_ofs>:4c: 000736d8 .word 0x000736d8

    可以看到整個的大小是_bss_end_ofs=0x000ae4e0,我們計算下

    gd->relocaddr = addr=0x33ff,0000-0x000ae4e0=33F41B20 4k對齊 &~(4096-1)=fff =33F4,1000

    具體的匯編如下

    .globl relocate_code relocate_code:mov r4, r0 /* save addr_sp */mov r5, r1 /* save addr of gd */mov r6, r2 /* save addr of destination *//*這里設置新的sp*//* Set up the stack */ stack_setup:mov sp, r4adr r0, _startcmp r0, r6beq clear_bss /* skip relocation */mov r1, r6 /* r1 <- scratch for copy_loop */ldr r3, _bss_start_ofsadd r2, r0, r3 /* r2 <- source end address *//*支持nor的復制,不支持nand的*/ copy_loop:ldmia r0!, {r9-r10} /* copy from source address [r0] */stmia r1!, {r9-r10} /* copy to target address [r1] */cmp r0, r2 /* until source end address [r2] */blo copy_loop

    變量地址修改

    程序的鏈接地址是0,訪問全局變量、靜態變量、調用函數時是使"基于0地址編譯得到的地址

    現在把程序復制到了SDRAM,需要修改代碼,把"基于0地址編譯得到的地址"改為新地址

    程序里有些地址在鏈接時不能確定,要到運行前才能確定:fixabs

    我們先來看下全局變量是怎么在匯編中使用的?查看文檔全局變量反匯編與重定位.md,在文檔中已經知道要怎么做了, 接下去看這個匯編的實現即可,uboot總結來說有兩種情況,我只了解第一種情況

    從.rel.dyn 段中依次獲得要修改的變量地址如果下一個值Y是0x17*(adr+offset)=*(adr+offset)+offset如果下一個值Y的低8位是0x02,這里加的是絕對地址,和自身存儲的地址值無關*(adr+offset)=*[Y>>4+段dynsym_r10+4]+offset
  • 正常的我們全局變量,指針的處理

  • 某個lable存的值是個地址,他是一個絕對的偏移,與當前的位置無關,這個老師說是動態鏈接的時候是需要這樣的,沒有了解動態鏈接,暫時不去深究.

    從代碼的意思來說,就是該地址是固定的,它是一個確定的位置*[Y>>4+段dynsym_r10+4]這個值就是表格里面死的值

  • 比如我們代碼偏移了offset=20,有兩個地址單元【4】=14,【5】=15

    • 假設都為0x17標記,則【24】=14+20=34,【25】=15+20=35
    • 【5】為0x02標記,則【24】=14+20=34,【25】=固定的值+20

    在代碼上體現是如下

    /*1. 計算偏移地址2. 獲得特殊段的 _dynsym_start_ofs _rel_dyn_start_ofs 位置 *//** fix .rel.dyn relocations*/ldr r0, _TEXT_BASE /* r0 <- Text base */sub r9, r6, r0 /* r9 <- relocation offset */ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */add r10, r10, r0 /* r10 <- sym table in FLASH */ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */add r3, r3, r0 /* r3 <- rel dyn end in FLASH *//*從.rel.dyn 段中依次獲得要修改的變量地址如果下一個值Y是0x17*(adr+offset)=*(adr+offset)+offset如果下一個值Y的低8位是0x02,這里加的是絕對地址,和自身存儲的地址值無關*(adr+offset)=*[Y>>4+段dynsym_r10+4]+offset */ fixloop:ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */add r0, r0, r9 /* r0 <- location to fix up in RAM */ldr r1, [r2, #4]and r7, r1, #0xffcmp r7, #23 /* relative fixup? */beq fixrelcmp r7, #2 /* absolute fixup? */beq fixabs/* ignore unknown type of fixup */b fixnext fixabs:/* absolute fix: set location to (offset) symbol value */mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */add r1, r10, r1 /* r1 <- address of symbol in table */ldr r1, [r1, #4] /* r1 <- symbol value */add r1, r1, r9 /* r1 <- relocated sym addr */b fixnext fixrel:/* relative fix: increase location by offset */ldr r1, [r0]add r1, r1, r9 fixnext:str r1, [r0]add r2, r2, #8 /* each rel.dyn entry is 8 bytes */cmp r2, r3blo fixloop

    進一步每行代碼分析如下

    調用之前的值mov r4, r0 /* save addr_sp */mov r5, r1 /* save addr of gd */mov r6, r2 /* save addr of destination */ r0 鏈接地址這里是0r6 目標地址r9 這里就是flash與sdram的偏移地址了/** fix .rel.dyn relocations*/ldr r0, _TEXT_BASE /* r0 <- Text base */sub r9, r6, r0 /* r9 <- relocation offset */-----------------------------------------r0 鏈接地址這里是0r6 目標地址r9 這里就是flash與sdram的偏移地址了 --------------------------------------------ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */add r10, r10, r0 /* r10 <- sym table in FLASH */-----------------------------------------------_dynsym_start_ofs:.word __dynsym_start - _startr10 就是 __dynsym_start 在flash 的實際地址-----------------------------------------------ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */add r2, r2, r0 /* r2 <- rel dyn start in FLASH */----------------------------------------------- _rel_dyn_start_ofs:.word __rel_dyn_start - _startr2 就是 __rel_dyn_start 在flash 的實際地址-----------------------------------------------ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */add r3, r3, r0 /* r3 <- rel dyn end in FLASH */----------------------------------------------- _rel_dyn_end_ofs:.word __rel_dyn_end - _startr3 就是 __rel_dyn_end 在flash 的實際地址----------------------------------------------------------------------------------------------總結來說就是先確定了具體在flash上的地址.rel.dyn : {__rel_dyn_start = .; ---------r2*(.rel*)__rel_dyn_end = .; ---------r3}.dynsym : {__dynsym_start = .; ----------r10*(.dynsym)}0006b568 <__rel_dyn_start>: //接下去用A標志這里 r26b568: 00000020 .word 0x000000206b56c: 00000017 .word 0x000000176b570: 00000024 .word 0x000000246b574: 00000017 .word 0x000000176b578: 00000028 .word 0x000000286b57c: 00000017 .word 0x00000017..................................................end=r300073608 <__dynsym_start>: //接下去用B標志這里 r10...73624: 00010003 .word 0x0001000373628: 00000000 .word 0x000000007362c: 00068de4 .word 0x00068de473630: 00000000 .word 0x0000000073634: 00050003 .word 0x0005000373638: 00000049 .word 0x000000497363c: 0006b568 .word 0x0006b568 -----------------------------------------------這里的 r2 可以先理解為是nor上的地址 也就是加載地址 fixloop:// r2 表示當前從 A 取址的地址ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ // 從A表示的地址 取出具體的值 add r0, r0, r9 /* r0 <- location to fix up in RAM */ // 將其值加上offset ldr r1, [r2, #4] and r7, r1, #0xffcmp r7, #23 /* relative fixup? */ //取出下一個A的值,如果是0x17 跳轉到 fixrel // 如果是0x02 跳轉到 fixabs//--- r0 是修正過的地址值 //--- r1 是__rel_dyn_start下一個的值beq fixrelcmp r7, #2 /* absolute fixup? */beq fixabs/* ignore unknown type of fixup */b fixnext fixabs://到這里的時候 從A取出來的值已經加上偏移了/* absolute fix: set location to (offset) symbol value */mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ // r1 右移4位 這里應該是后四位無效用來表示 #2 // 猜測 r1 這里表示的是 B的序號 也就是第幾個了add r1, r10, r1 /* r1 <- address of symbol in table */ldr r1, [r1, #4] /* r1 <- symbol value */add r1, r1, r9 /* r1 <- relocated sym addr */b fixnext fixrel: //到這里的時候 從A取出來的值r0已經加上偏移了/* relative fix: increase location by offset */ldr r1, [r0] // 從重定位后的ram中取出值.老師的筆記這里寫錯了哦 ######################### add r1, r1, r9 // 將這個值加上offset r1 fixnext:str r1, [r0]add r2, r2, #8 /* each rel.dyn entry is 8 bytes */cmp r2, r3blo fixloop

    參考

    內核黑科技之DEFINE宏

    全局變量反匯編與重定位

    轉載:https://www.cnblogs.com/zongzi10010/p/10424480.html?

    總結

    以上是生活随笔為你收集整理的uboot2012(一)分析重定位的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 国产免费午夜 | 日本精品一区二区视频 | 怡红院成永久免费人全部视频 | 欧美乱大交xxxxx潮喷l头像 | 亚洲激情在线播放 | av不卡免费观看 | 久久大尺度 | 精品视频免费 | 日韩精品一区二区亚洲av观看 | 人人澡人人澡 | 在线观看www | 视频一区国产 | 日日摸日日碰夜夜爽无码 | 黑丝美女av | 丝袜制服一区 | 国产xxxxxxxxx | 日本黄网站 | 精品少妇人妻av免费久久久 | www,xxx日本| 天天天干 | 日韩欧美99 | 亚色在线 | 关之琳三级全黄做爰在线观看 | 中文字幕日韩久久 | 怒海潜沙秦岭神树 | 天天色综合av| 国产欧美一区二区三区国产幕精品 | 熟妇高潮一区二区三区在线播放 | 操极品女神 | 欧美三级日本三级 | 国产伦理久久精品久久久久 | 久久色在线视频 | 国产又黄又粗又猛又爽 | 91福利社在线观看 | 国产精品毛片一区二区在线看舒淇 | 狂野欧美性猛交xxxx巴西 | 午夜污 | 九九热视频这里只有精品 | 精品国模一区二区三区欧美 | 欧美 日韩 人妻 高清 中文 | 中文理论片 | 全黄性高潮 | 九九爱精品视频 | 性感美女一级片 | 精品人妻一区二区三区久久夜夜嗨 | 91免费精品视频 | 女王脚交玉足榨精调教 | 91在线| 亚洲av无码乱码国产精品fc2 | 国产99在线播放 | 糖心av| 麻豆免费在线观看 | 涩涩精品 | www在线看片 | 国产一区二区三区日韩 | 偷拍欧美另类 | 精品无码人妻一区二区三区 | 欧美视频免费在线 | 亚洲最大的av网站 | 人人妻人人澡人人爽国产一区 | 一区二区在线观看免费 | 国产精品永久免费 | 日韩精品三区 | 国产偷人妻精品一区二区在线 | 日韩美女福利视频 | 日韩六十路 | 中文字幕av观看 | 美女视频一区二区三区 | 香蕉视频一区二区 | 亚洲av成人片色在线观看高潮 | 草草影院国产 | 久久久久久久一区二区 | 国产精品久久午夜夜伦鲁鲁 | 午夜影院在线观看免费 | 人成网站在线观看 | 国产在线色站 | 影音先锋亚洲资源 | 在线观看一级片 | 日本一区二区免费在线观看 | 成人视屏在线 | 丝袜国产在线 | 超碰免费在线播放 | 天天干夜夜怕 | 芭乐视频色 | 男人的天堂中文字幕 | 日韩午夜精品 | 日本在线一级 | 精品一区二区三区免费 | 激情aaa| 午夜性福利视频 | 在线看h网站 | 欧美日韩二三区 | 最新中文字幕av专区 | 中文字幕人妻丝袜二区 | 日韩特黄一级片 | 日本a级无毛 | 黄色免费成人 | 欧美久草 | 伊人动漫 |