失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 模拟链接器 重定位符号表

模拟链接器 重定位符号表

时间:2021-06-23 17:19:57

相关推荐

模拟链接器 重定位符号表

模拟连接器创建文件头

user_app.asm 的头部模拟各种段选择子填充, 符号表的占位

app_core.asm 用于模拟加载程序

realloc_user_symbol 用来重定位符号表

load_user_app 用来加载用户程序

mk_gdt_d 建立一个新的描述符

add_gdt_d 新增一个描述符到GDT

app_mbr.asm 用来加载一个加载器

摘要 user_app.asm 自定义构建头部

1.所有的段选择子将被加载器计算后填充

2.各个段的长度用于计算界限值

3.stack由加载器动态分配

4.符号表为了方便起见, 每个符号定长32字节,不足则用0填充字符串

5.符号地址表被用来填充加载器提供的函数( 系统函数 )

每个系统函数占6个字节(偏移 低4字节, 段选择子 高2字节)

max_api_str_len equ 32 ;api字符串定长32字节const_symbol_table_len equ (header_end-symbol_table)/max_api_str_lenapp_len dd app_end ;程序长度,需要确认加载N个扇区header_len dd header_end ;头部长度, 用于确定界限值,动态建立描述符;头部段选择子需要此段选择子来定位符号表header_selector dd section.header.startstack_selector dd 0 ;stack段选择子,加载器来动态分配stack_len dd 1 ;stack长度,粒度4Kentry dd start; aop 入口偏移地址;用于计算基址,被计算后将修改成段选择子code_selector dd section.user_code.start ;代码段长度 , 用于确定界限值code_len dd user_code_end;数据区段选择子, 被计算后会修改成段选择子data_selector dd section.user_data.start;长度data_len dd user_data_end;有几个符号需要解析symbol_table_len dd const_symbol_table_len; 获取符号地址表的偏移地址symbol_addr_fetch dd symbol_addr;符号表, 为了方便循环,每个符号最长32字节symbol_table:printf db 'printf'times max_api_str_len-($-printf) db 0exit_process db 'exit_process'times max_api_str_len-($-exit_process) db 0;符号地址表symbol_addr:times const_symbol_table_len*6 db 0header_end:

app_mbr.asm

CORE_ADDR equ 0x40000CORE_SECTOR EQU 0x1DEFAULT_GDT_OFFSET equ 0X28mov eax,csmov ds,eax;获取gdt 地址mov eax,[gdt_addr + 0x7c00]xor edx,edxmov ebx, 16div ebxmov ds,eaxmov esi,edx;设置描述符 index:0mov dword [esi],0mov dword [esi+4],0;1, 数据段,基址:0, 界限:fffff, G=1mov dword [esi+8],0x0000ffffmov dword [esi+12],0x00cf9200;2,代码段(MBR),基址:0x7c00, 界限:1ff , G=0 mov dword [esi + 16],0x7c0001ffmov dword [esi+20] , 0x00409800;3, stack, 基址:0x7c00,界限: ffffe, G=1mov dword [esi+24],0x7c00fffemov dword [esi + 28],0x00cf9600;4,显存, 基址:0xb8000, 界限:7fff,G=0mov dword [esi+32],0x80007fffmov dword [esi +36],0x0040920bmov word [cs:gdt_limit + 0x7c00],39lgdt [cs:gdt_limit +0x7c00]in al,0x92or al,10bout 0x92,alclimov eax,cr0or eax,1mov cr0,eax;16位下使用32位操作数 , 添加 0x66前缀jmp dword 0x10:init[bits 32]init:;初始化ds , 数据段,索引1xor eax,eaxmov eax,1000bmov ds,eax;stackxor eax,eaxmov eax,11_000bmov ss,eaxxor esp,esp;把内核代码加载到CORE_ADDR , 从CORE_SECTOR扇区开始读取mov esi,CORE_ADDRmov ebx,esimov eax,CORE_SECTORcall read_sector;内核代码首4字节为程序长度 ;读取程序多大mov eax,[esi]xor edx,edxmov ecx,512div ecxor edx,edxjnz checkdec eaxcheck:or eax,eaxjz read_done;从下个扇区开始读mov ecx,eaxmov eax,CORE_SECTORinc eax;一直读完process_left:call read_sectorinc eaxloop process_leftread_done:;初始化内核代码描述符;0x4 , sys_func 段;0x8 , core_data 段;0xc, core_code 段;0x10, app_entry 入口;获取gdt表 , 增加内核代码描述符mov esi,[0x7c00+gdt_addr];sys_func 代码段mov edi,CORE_ADDRmov eax,[edi+4];sys_func偏移地址mov edx,[edi+8];core_data 偏移 ;sys_func段界限sub edx,eaxdec edxmov ebx,edx;sys_func 基址add eax,edimov ecx,0x00409800;sys_func 段属性, 可执行, G=0;创建sys_func 描述符call mk_gdt_dmov [esi+DEFAULT_GDT_OFFSET],eaxmov [esi+DEFAULT_GDT_OFFSET+4],edx; core_data 数据段mov eax,[edi+8]; core_data 偏移mov ebx,[edi+12]; core_code 偏移sub ebx,eaxdec ebx ; core_data 段界限add eax,edi; core_data 基址mov ecx,0x00409200; core_data 段属性, G=0;安装core_data 数据段描述符call mk_gdt_dmov [esi + DEFAULT_GDT_OFFSET + 8],eaxmov [esi + DEFAULT_GDT_OFFSET + 12],edx;core_code 代码段mov eax,[edi+ 12] ; core_code 偏移mov ebx,[edi] ; 程序总长度sub ebx,eaxdec ebx ; core_code 段界限add eax,edi; core_code 基址mov ecx,0x00409800; core_code 段属性,G=0call mk_gdt_d;安装core_code 代码段描述符mov [esi+DEFAULT_GDT_OFFSET + 16],eaxmov [esi+DEFAULT_GDT_OFFSET + 20],edx;修改GDT界限,重新加载gdtmov word [0x7c00 + gdt_limit],8*8-1lgdt [0x7c00 + gdt_limit]; 从mbr转入内核代码, edi:基址, 偏移:0x10 为代码入口; mbr 主要功能加载内核代码jmp far [edi+0x10] ; jmp 0x38:[edi+0x10] ; cs:0x38 , eip:[edi+0x10];结束done:hlt;构建一个描述符;eax gdt基址;ebx gdt界限;ecx gdt段属性;返回 edx 高32 , eax 低32位mk_gdt_d:mov edx, eaxand edx,0xffff0000; 基址低16位放入 eax 高16位shl eax,16; eax 低16位 放入 段界限低16位or ax,bx; 基址高8位 放入低8位,然后交换位置,构建出高16位段基址rol edx,8;低8位跟高8位交换, 中间交换(中间都是0 无所谓)bswap edx;处理高32位中 还有高4位的段界限xor bx,bxor edx,ebx;最后加上段属性or edx,ecxret;eax : 扇区号, ds:ebx:输出缓冲区地址read_sector: push eax push ecxpush edxpush eaxmov dx,0x1f2mov al,1out dx,al ;读取的扇区数inc dx;0x1f3pop eaxout dx,al ;LBA地址7~0inc dx;0x1f4mov cl,8shr eax,clout dx,al ;LBA地址15~8inc dx;0x1f5shr eax,clout dx,al ;LBA地址23~16inc dx;0x1f6shr eax,clor al,0xe0 ;第一硬盘 LBA地址27~24out dx,alinc dx;0x1f7mov al,0x20 ;读命令out dx,al.waits:in al,dxand al,0x88cmp al,0x08jnz .waits ;不忙,且硬盘已准备好数据传输 mov ecx,256 ;总共要读取的字数mov dx,0x1f0.readw:in ax,dxmov [ebx],axadd ebx,2loop .readwpop edxpop ecxpop eaxretgdt_limit dw 0gdt_addr dd 0x7e00times 510-($-$$) db 0dw 0xaa55

app_core.asm

;段选择子,固定值sys_func_selector equ 0x28;系统调用core_data_seletor equ 0x30;数据段core_code_seletor equ 0x38;代码段screen_selector equ 0x20 ;显存core_stack_selector equ 0x18 ;stackmem_selector equ 0x8 ;4G内存空间max_api_str_len equ 32 ; api定长32字节;用户程序起始扇区号user_app_sector equ 50;0x00 , 代码长度core_len dd core_length ;下面3个段地址只在被加载时用来计算基址;0x04 sys_func_seg_addr dd section.sys_func.start;0x08core_data_seg_addr dd section.core_data.start;0x0ccore_code_seg_addr dd section.core_code.start;0x10 , 代码入口点entry dd startdw core_code_seletor[bits 32]section sys_func vstart=0;eax 内核符号索引 ;es:edx 用户符号地址表;ebx 用户符号索引fill_user_symbol:push dspush esipush eaxpush ecxpush edi;ds:esi -> 内核符号地址表;es:edi -> 用户符号地址表xor esi,esimov esi, core_data_seletorxor edi,edimov edi,edxmov ds,esimov esi,symbol_addr;获取内核系统函数偏移地址xor edx,edxmov ecx,6mul ecxpush eax;保存当前地址mov ecx,[ds:esi+eax];填充用户表mov eax,ebxxor edx,edxmov ebx,6mul ebxpush eax;保存当前地址mov [es:edi+eax],ecx;段选择子pop ebx ;用户地址pop eax ;内核地址mov ax,[ds:esi+eax+2]mov [es:edi+ebx+2],axpop edipop ecxpop eaxpop esipop dsret;eax :用户符号表段选择子, ebx:符号表偏移 ecx N个符号需要被解析 edx 被填充偏移地址realloc_user_symbol:push espush dspush edi;es:edi 指向用户符号表mov es,eaxmov edi,ebxmov eax,core_data_seletormov ds,eaxxor ebx,ebx ;ebx作为用户符号表的索引xor eax,eaxbegin_realloc: ;入参: es:edi call match_symbolcmp byte [bool_matched_symbol],0jz loop_next;eax 返回内核索引,ebx 用户符号索引,es:edx 用户符号地址表call fill_user_symbolloop_next:inc ebxadd edi,max_api_str_lenloop begin_reallocmov byte [bool_matched_symbol],0pop edipop dspop esretf;内部使用,符号表匹配一次cmp_symbol_once:push ecxcldxor eax,eaxmov ecx,8repe cmpsdjnz not_matchor eax,1not_match:pop ecxret;入参 es:edi, 用户程序的符号表;ds:esi 指向内核符号表; eax 返回内核符号索引match_symbol:push dspush esipush ecxpush ebx;开始比较字符串mov ebx,core_data_seletormov ds,ebxmov ecx,symbol_table_lenmov esi,symbol_tablemov byte [bool_matched_symbol],0xor ebx,ebxbegin_match_symbol:call cmp_symbol_onceor eax,0jnz matchedadd esi,max_api_str_leninc ebxloop begin_match_symboljmp match_symbol_donematched:mov byte [bool_matched_symbol],1mov eax,ebxmatch_symbol_done:pop ebxpop ecxpop esipop dsret;增加一个描述符;描述符 edx:eax 入参 ,;返回 eax : 此描述符的段选择子( 更新后的界限值 / 8)add_gdt_d:push dspush espush ebxpush edx;ds 指向内核数据段选择子mov ebx,core_data_seletormov ds,ebx;es 0-4G选择子mov ebx,mem_selectormov es,ebx;把GDTR里的数据 放到指定内存地址,6个字节sgdt [ds:gdt_limit];计算新增的gdt地址: gdt表地址 + gdt界限 +1xor ebx,ebxmov bx,[ds:gdt_limit]inc bxadd ebx,[ds:gdt_addr];放入新增描述符mov [es:ebx],eaxmov [es:ebx+4],edx;修改gdt界限add word [ds:gdt_limit],8;计算当前描述符的段选择子xor eax,eaxxor edx,edxmov ax,[ds:gdt_limit]mov bx,8div bxshl ax,3 ; ti,rpl 3位均0;重新加载gdtrlgdt [ds:gdt_limit]pop edxpop ebxpop espop dsretf;eax 基址;ebx 界限;ecx 属性;返回 高32位edx:低32位eax 描述符mk_gdt_d:push edx;构建低32位 ,eax == [16位基址][16段界限]mov edx,eaxand edx,0xffff0000shl eax,16or ax,bx;构建高32位rol edx,8bswap edxxor bx,bxor edx,ebxor edx,ecxpop edxretf;ecx 需要分配的字节数calc_mem_addr:push ebxpush edxpush eaxpush dsmov eax, core_data_seletormov ds,eax;eax :下个可分配地址mov eax,[ds:mem_alloc_addr]add eax,ecxmov ecx,[ds:mem_alloc_addr];为下个地址对齐;ebx 必是一个4字节对齐的地址mov ebx,eaxand ebx,0xfffffffc add ebx,4;如果eax 最后2位有1位是1,则不是能被4整除的数test eax,0x03;根据条件赋值cmovnz eax,ebxmov [ds:mem_alloc_addr],eaxpop dspop eaxpop edxpop ebxretf;eax 扇区号;ds:ebx 写入地址read_sector: push eax push ecxpush edxpush eaxmov dx,0x1f2mov al,1out dx,al;读取的扇区数inc dx ;0x1f3pop eaxout dx,al;LBA地址7~0inc dx ;0x1f4mov cl,8shr eax,clout dx,al;LBA地址15~8inc dx ;0x1f5shr eax,clout dx,al;LBA地址23~16inc dx ;0x1f6shr eax,clor al,0xe0;第一硬盘 LBA地址27~24out dx,alinc dx ;0x1f7mov al,0x20 ;读命令out dx,al.waits:in al,dxand al,0x88cmp al,0x08jnz .waits;不忙,且硬盘已准备好数据传输 mov ecx,256 ;总共要读取的字数mov dx,0x1f0.readw:in ax,dxmov [ebx],axadd ebx,2loop .readwpop edxpop ecxpop eaxretfprintf: ;DS:EBX=字符串地址push ecx.getc:mov cl,[ebx]or cl,cljz .exitcall put_charinc ebxjmp .getc.exit:pop ecxretf ;段间返回put_char:pushad;以下取当前光标位置mov dx,0x3d4mov al,0x0eout dx,alinc dx in al,dxmov ah,aldec dx mov al,0x0fout dx,alinc dx in al,dx mov bx,axcmp cl,0x0d jnz .put_0amov ax,bxmov bl,80div blmul blmov bx,axjmp .set_cursor.put_0a:cmp cl,0x0a jnz .put_otheradd bx,80jmp .roll_screen.put_other: push esmov eax,screen_selector;0xb8000段的选择子mov es,eaxshl bx,1mov [es:bx],clpop esshr bx,1inc bx.roll_screen:cmp bx,2000 jl .set_cursorpush dspush esmov eax,screen_selectormov ds,eaxmov es,eaxcldmov esi,0xa0 mov edi,0x00 mov ecx,1920rep movsdmov bx,3840 mov ecx,80.cls:mov word[es:bx],0x0720add bx,2loop .clspop espop dsmov bx,1920.set_cursor:mov dx,0x3d4mov al,0x0eout dx,alinc dx mov al,bhout dx,aldec dx mov al,0x0fout dx,alinc dx mov al,blout dx,alpopadret section core_data vstart=0;分配用户程序内存物理起始地址mem_alloc_addr dd 0x100000 ;用来修改gdt, 指令sgdt把GDTR寄存器的数据放到指定内存gdt_limitdw 0gdt_addrdd 0welcome_msg db 'welcome to mini core code',0x0d,0x0a,0load_user_app_msg db 'loading user app...',0x0d,0x0a,0load_user_app_done db 'user app loaded!',0x0d,0x0a,0exit_msg db 'user app exit',0x0d,0x0a,0cpu_info_begin db 0x0d,0x0a,0cpu_info times 128 db 0cpu_info_end db 0x0d,0x0a,0; 首个扇区缓存first_sector_buf times 512 db 0;是否匹配bool_matched_symbol db 0;内核栈顶stack_esp dd 0;用户程序段选择子 (头)user_app_header_selector dw 0;内核符号表;用于匹配用户程序调用的函数,并填充对应的 段选择子:偏移symbol_table:__printfdb 'printf'times max_api_str_len-($-__printf) db 0__read_sector db 'read_sector'times max_api_str_len-($-__read_sector) db 0__exit_process db 'exit_process'times max_api_str_len-($-__exit_process) db 0symbol_table_len equ ($-symbol_table)/max_api_str_len;符号地址表symbol_addr:__printf_addr dd printfdw sys_func_selector__read_sector_addr dd read_sectordw sys_func_selector__exit_process_addr dd exit_processdw core_code_seletorcore_data_end:section core_code vstart=0start:;进入内核代码;ds数据区使用自己的core_data;ss使用mbr中已经分配的4Kmov eax,core_data_seletormov ds,eax;输出欢迎信息, ebx:参数 , ds:数据段mov ebx, welcome_msg; call 0x28:printf , push cs push eip call sys_func_selector:printfmov eax,0;cpuid 返回cpu 信息,特性 ... ; eax 参数; 参数0 返回最大功能号, ebx, ecx,edx 返回其他信息cpuidmov [cpu_info + 0], ebxmov [cpu_info + 4],ecxmov [cpu_info + 8],edx;输出CPU 信息mov ebx,cpu_info_begincall sys_func_selector:printfmov ebx,cpu_infocall sys_func_selector:printfmov ebx,cpu_info_endcall sys_func_selector:printf;输出加载用户程序信息mov ebx,load_user_app_msgcall sys_func_selector:printf;分配内存地址;加载用户程序;建立描述符;匹配符号表,把匹配到的api符号 填入用户程序中mov esi,user_app_sectorcall load_user_appmov ebx,load_user_app_donecall sys_func_selector:printf;保留当前内核栈顶,用户程序将切换成自己的栈mov [ds:stack_esp],espxor eax,eaxmov eax,[ds:user_app_header_selector]mov ds,eaxjmp far [ds:0x14] ; cs:用户代码段选择子, eip:startexit_process:;恢复内核数据段,stackmov eax,core_data_seletormov ds,eaxmov eax,core_stack_selectormov ss,eaxmov esp,[ds:stack_esp]mov ebx,exit_msgcall sys_func_selector:printfhlt;参数 : esi 起始扇区号load_user_app:pushadpush dspush esmov eax , core_data_seletormov ds, eax;读用户APP首个扇区mov eax,esimov ebx,first_sector_bufcall sys_func_selector:read_sector;计算用户程序扇区数量;加载用户程序的起始地址由内核代码决定,这里由mem_alloc_addr 作为起始地址;这里起始地址以4字节对齐, 程序总字节以512对齐(读取扇区以512字节为单位);由于读取扇区是512字节对齐的,因此分配地址至少也是要512字节对齐的;calc_mem_addr 传入已经对齐的字节数, 同时计算下个可分配的地址mov eax,[ds:first_sector_buf]mov ecx,512xor edx,edxdiv ecxor edx,edxjz begin_calc_mem_addrinc eax;计算分配内存地址begin_calc_mem_addr:mov ebx,eax ; ebx :扇区数mov ecx,512mul ecxmov ecx,eax ;以512对齐的字节数call sys_func_selector:calc_mem_addr;计算好用户加载的起始地址后, 开始加载用户程序mov edi,ecx ;edi 用户程序加载首地址xchg ebx,ecx ; 交换后, ecx 扇区数 , ebx 起始地址mov eax,mem_selector ; 0~4G的数据描述符mov ds,eaxmov eax,esi; eax 扇区号, ecx 扇区数 , ebx 分配的内存起始地址read_user_app:call sys_func_selector:read_sectorinc eaxloop read_user_app;构建头描述符;当前 ds : 0~4G 的段选择子;eax 基址, ebx 段界限 , ecx 属性mov eax,edimov ebx,[edi+0x04]dec ebx ;段界限mov ecx, 0x00409200 ;属性,读写;建立描述符call sys_func_selector:mk_gdt_d;增加描述符,eax作为返回值call sys_func_selector:add_gdt_d;段选择子写回应用程序mov [edi+0x08],eax;代码段描述符mov eax,ediadd eax,[edi+0x18] ;基址mov ebx,[edi+0x1c] ;代码段长度dec ebx ;界限mov ecx,0x00409800 ; 属性,执行call sys_func_selector:mk_gdt_dcall sys_func_selector:add_gdt_dmov [edi+0x18],eax;数据段mov eax,ediadd eax,[edi+0x20] ;基址mov ebx,[edi+0x24] ;长度dec ebx ;界限mov ecx,0x00409200; 读写call sys_func_selector:mk_gdt_dcall sys_func_selector:add_gdt_dmov [edi+0x20],eax;stack;stack段界限从高地址往低,因此从0xFFFFF开始减;当前stack段属性的G=1mov eax,[edi+0x10] ;获取长度单位xor ebx,ebxmov ebx,0xfffffsub ebx,eax;stack段界限;给stack计算分配的地址,ecx 入参(字节数)mov eax,0x1000xor edx,edxmul dword [edi+0x10]mov ecx,eaxcall sys_func_selector:calc_mem_addr;返回的地址为栈底;设置eax基址(栈顶)add eax,ecxmov ecx,0x00c09600 ;stack属性call sys_func_selector:mk_gdt_dcall sys_func_selector:add_gdt_dmov [edi+0x0c],eax;开始匹配系统函数,重定位用户程序符号;eax 用户符号表段选择子;ebx 偏移地址;ecx N个符号需要解析;edx 用户符号表被填充偏移地址mov eax,[edi+0x08]mov ebx,0x30mov ecx,[edi+0x28]mov edx,[edi+0x2c]call sys_func_selector:realloc_user_symbol;在core_data中填入用户程序(头)段选择子,用于jmp进入入口mov eax,core_data_seletormov es,eaxmov eax,[edi+0x08]mov [es:user_app_header_selector],eaxpop espop dspopadretsection core_endcore_length:

user_app.asm

max_api_str_len equ 32 ;api字符串定长32字节const_symbol_table_len equ (header_end-symbol_table)/max_api_str_len;模拟连接器生成文件头section header vstart=0;程序长度app_len dd app_end;头部长度;0x04header_len dd header_end;头部段选择子,会被修改;0x08header_selector dd section.header.start;偏移0xcstack_selector dd 0 ;stack段选择子,由内核代码来分配;0x10 ;stack长度由用户程序指定,由内核代码分配;stack_len == 1, 以4K为粒度stack_len dd 1 ;stack长度;偏移0x14entry dd start; aop;用于计算基址,被计算后将修改成段选择子;偏移0x18code_selector dd section.user_code.start ;代码段长度;偏移0x1ccode_len dd user_code_end;数据区,0x20data_selector dd section.user_data.start;长度 ,0x24 data_len dd user_data_end;有几个符号需要解析;0x28symbol_table_len dd const_symbol_table_len;0x2csymbol_addr_fetch dd symbol_addr;符号表;0x30symbol_table:printf db 'printf'times max_api_str_len-($-printf) db 0exit_process db 'exit_process'times max_api_str_len-($-exit_process) db 0;符号地址表symbol_addr:times const_symbol_table_len*6 db 0header_end:[bits 32]section user_data vstart=0welcome_msg db '!!!! user app is running , welcome!',0x0d,0x0a,0user_data_end:section user_code vstart=0start:;当前ds是头段选择子mov eax,dsmov es,eax;切换用户程序自己的数据段 ,stackmov eax,[ds:data_selector]mov ds,eaxmov eax,[es:stack_selector]mov ss,eaxxor esp,esp;call printfmov ebx,welcome_msgcall far [es:symbol_addr];跳回内核jmp far [es:symbol_addr + 6]user_code_end:section app_tailapp_end:

如果觉得《模拟链接器 重定位符号表》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。