VC 2019安排代码位置
1. 问题的出现
这个问题是我在开发32位引导程序是出现的,原来是用VC2005来编译和链接的,一直工作很正常,但是,有一天,我突发奇想,换成VC2019来编译,好吧,引导程序就罢工了……
这是我的makefile
OBJS = $(OBJDIR)\startup.obj $(OBJDIR)\rbxldr32.obj $(OBJDIR)\io.obj \
$(OBJDIR)\nvsprintf.obj $(OBJDIR)\bvd.obj $(OBJDIR)\pe.obj
#
#
..\..\bin\RBXLDR32: $(OBJS) ..\..\bin\build.exe
$(LINK) $(LFLAGS) $(OBJS) libc.lib msvcrt.lib
del ..\..\bin\RBXLDR32
..\..\bin\build -s..\..\bin\rbxldr32.exe -d..\..\bin\RBXLDR32 -a18000
我把mainCRTStartup放在startup.obj中,为了保证mainCRTStartup位于0x00401000的位置,我就把startup.obj放在了第一个文件中。这个makefile在VC2005中,没有问题。
换到了VC2019后,就不行了
折腾了半天,搞了各种编译和链接的参数,毛用都没有
2. 发现问题
然后就看MAP文件,一看,简直日了狗了,原来是入口点变了。我是设计将引导程序的入口点放在RVA+0x00001000处,经过重定位后,是在0x00019000处,在MAP文件中,会显示在0x00401000处。
这个安排在VC2005中并没有问题,到了VC2019,就出问题了,微软还真能搞事,
这个可以看看,先放VC2005的MAP文件
rbxldr32
Timestamp is 5e736e4f (Thu Mar 19 21:06:23 2020)
Preferred load address is 00400000
Start Length Name Class
0001:00000000 00002e8cH .text CODE
0002:00000000 00000790H .data DATA
0002:000007a0 00000360H .bss DATA
Address Publics by Value Rva+Base Lib:Object
0000:00000000 ___safe_se_handler_count 00000000 <absolute>
0000:00000000 ___safe_se_handler_table 00000000 <absolute>
0001:00000000 _mainCRTStartup 00401000 f startup.obj
0001:00000010 _CheckLdrParamList 00401010 f rbxldr32.obj
0001:00000120 _LPL_FindFirst 00401120 f rbxldr32.obj
0001:00000150 _LDR_GetSysImgOffset 00401150 f rbxldr32.obj
entry point at 0001:00000000
这个可以看出,mainCRTStartup就位于0x00401000处, 没有问题。
然后看看VC2019的MAP文件
rbxldr32
Timestamp is 5e736f05 (Thu Mar 19 21:09:25 2020)
Preferred load address is 00400000
Start Length Name Class
0001:00000000 00000690H .text CODE
0001:00000690 000027a5H .text$mn CODE
0002:00000000 0000001cH .rdata DATA
0002:0000001c 00000000H .edata DATA
0002:0000001c 00000070H .rdata$zzzdbg DATA
0003:00000000 000007a0H .data DATA
0003:000007a0 00000374H .bss DATA
;
Address Publics by Value Rva+Base Lib:Object
;
0000:00000000 ___guard_longjmp_table 00000000 <absolute>
0000:00000000 ___guard_iat_table 00000000 <absolute>
0001:00000000 _strcmp 00401000 f libc:strcmp.obj
0001:00000090 _memset 00401090 f libc:memset.obj
0001:000000f0 _strstr 004010f0 f libc:strstr.obj
0001:00000170 _strlen 00401170 f libc:strlen.obj
0001:000001f0 __aulldiv 004011f0 f libc:ulldiv.obj
0001:00000260 __allmul 00401260 f libc:llmul.obj
0001:000002a0 __aullrem 004012a0 f libc:ullrem.obj
0001:00000320 _memmove 00401320 f libc:memmove.obj
0001:00000470 _memcpy 00401470 f libc:memcpy.obj
0001:000005d0 _strchr 004015d0 f libc:strchr.obj
0001:000005d6 ___from_strstr_to_strchr 004015d6 libc:strchr.obj
0001:00000690 _mainCRTStartup 00401690 f startup.obj
0001:000006a0 _CheckLdrParamList 004016a0 f rbxldr32.obj
0001:000007b0 _LPL_FindFirst 004017b0 f rbxldr32.obj
0001:000007e0 _LDR_GetSysImgOffset 004017e0 f rbxldr32.obj
0001:000008c0 _BuildSystMemLayout 004018c0 f rbxldr32.obj
entry point at 0001:00000690
0x00401000的位置居然变成了strcmp,那当然会出错了
3. 问题在哪里
一开始,还以为是链接器改变了按照目标文件和库文件顺序来安排代码位置的方式,但是,仔细一看,还是按照命令行指定的顺序来安排代码位置的。
找了很久,找不到,都要死心滚回VC2005了,在上下翻MAP文件的时候,发现了问题在哪里,居然是在死心后发现问题的,发现VC2019编译出的MAP存在特别多的节(SECTION),对比一下VC2005的MAP文件,VC2005编译出来只有3个节,而VC2019,居然编译出了7个节,而且居然还有两个代码节,日
; 这是VC2005的节信息
Start Length Name Class
0001:00000000 00002e8cH .text CODE
0002:00000000 00000790H .data DATA
0002:000007a0 00000360H .bss DATA
;
; 这是VC2019的节信息
Start Length Name Class
0001:00000000 00000690H .text CODE
0001:00000690 000027a5H .text$mn CODE
0002:00000000 0000001cH .rdata DATA
0002:0000001c 00000000H .edata DATA
0002:0000001c 00000070H .rdata$zzzdbg DATA
0003:00000000 000007a0H .data DATA
0003:000007a0 00000374H .bss DATA
然后还发现,VC2019居然把mainCRTStartup放在了第二个代码节的开头
0001:00000690 _mainCRTStartup 00401690 f startup.obj
到这里才发现,这是两个不同名称的代码节,什么鬼?编译器居然给代码节改名了……所以链接器就按节名称的顺序来安排代码位置,好吧,大写的服。
4. 解决问题
为了解决这个问题,我首先想到的是给代码节改名,改成.text应该就可以了,在CL.EXE的帮助里没有找到可以改节名的地方,是想在编译参数里面设置的话,可以映像到整个程序,没找到…
算了,还是老老实实用#pragma吧
就是在代码里增加一行 #pragma code_seg(".text")
修改后的代码是这样的
#include "rbxldr32.h"
void main(void);
//
#pragma code_seg(".text")
//
void mainCRTStartup(void)
{
main();
for(;;)
;
}
然后来看看编译出来的MAP
rbxldr32
;
Timestamp is 5e7375d3 (Thu Mar 19 21:38:27 2020)
;
Preferred load address is 00400000
;
Start Length Name Class
0001:00000000 000006a0H .text CODE
0001:000006a0 00002795H .text$mn CODE
0002:00000000 0000001cH .rdata DATA
0002:0000001c 00000000H .edata DATA
0002:0000001c 00000070H .rdata$zzzdbg DATA
0003:00000000 000007a0H .data DATA
0003:000007a0 00000374H .bss DATA
;
Address Publics by Value Rva+Base Lib:Object
;
0000:00000000 ___guard_longjmp_table 00000000 <absolute>
0000:00000000 ___guard_iat_table 00000000 <absolute>
0001:00000000 _mainCRTStartup 00401000 f startup.obj
0001:00000010 _strcmp 00401010 f libc:strcmp.obj
0001:000000a0 _memset 004010a0 f libc:memset.obj
0001:00000100 _strstr 00401100 f libc:strstr.obj
0001:00000180 _strlen 00401180 f libc:strlen.obj
0001:00000200 __aulldiv 00401200 f libc:ulldiv.obj
0001:00000270 __allmul 00401270 f libc:llmul.obj
0001:000002b0 __aullrem 004012b0 f libc:ullrem.obj
0001:00000330 _memmove 00401330 f libc:memmove.obj
0001:00000480 _memcpy 00401480 f libc:memcpy.obj
0001:000005e0 _strchr 004015e0 f libc:strchr.obj
0001:000005e6 ___from_strstr_to_strchr 004015e6 libc:strchr.obj
0001:000006a0 _CheckLdrParamList 004016a0 f rbxldr32.obj
0001:000007b0 _LPL_FindFirst 004017b0 f rbxldr32.obj
0001:000007e0 _LDR_GetSysImgOffset 004017e0 f rbxldr32.obj
0001:000008c0 _BuildSystMemLayout 004018c0 f rbxldr32.obj
0001:00000b40 _IA32_BuildPageMap 00401b40 f rbxldr32.obj
0001:00000d60 _LoadCoreFile 00401d60 f rbxldr32.obj
entry point at 0001:00000000
这样mainCRTStartup又回到0x00401000处了,