Yesterday, when I debugged the demo of the board, I found that the compilation was not successful. There was a problem with the Runtime Library such as _ RTC.
It turned out that the demo they gave was written in vs2008, And then it only gave the vc6 project, and not to the database under vc6. It only gave the database under vc9 !!!
However, through this issue, I will be able to understand the issue of C rumtime in the future. The following two articles are reproduced! The setting option of C rumtime was not found in vc6, so I had to debug it on vs2005 and found it quite uncomfortable !!!!!!!
Usage of vc7/vc8 library in vc6 -- reprinted
Currently, some new Microsoft sdks basically use vc7/vc8 (that is,. NET 2003/VS 2005). When we use vc6 to use the debug version of these libraries, a link error occurs, the same problem exists for developing static or dynamic libraries with vc7/vc8, this is mainly because vc7/vc8 uses different debugging information formats and some security detection mechanisms are added.
You can modify the configuration information of the project in vc7/vc8 so that it can be used by vc6. The specific operations are as follows:
1. open the Project Settings page, select the C/C ++ properties page, and change "General> debug information format" to "disabled )".
If you do not modify it here, the following error occurs during the connection of vc6:
Fatal error lnk1103: debugging information processing upt; recompile Module
2. Change "code generation-> basic runtime check" to "default )".
If you do not modify it here, the following error occurs during the connection of vc6:
Error lnk2001: unresolved external symbol _ rtc_shutdown
Error lnk2001: unresolved external symbol _ rtc_initbase
Error lnk2001: unresolved external symbol _ rtc_checkesp
Error lnk2001: unresolved external symbol @ _ rtc_checkstackvars @ 8
Error lnk2001: unresolved external symbol _ rtc_uninituse
3. Change "code generation-> buffer security check" to "no )".
If you do not modify it here, the following error occurs during the connection of vc6:
Error lnk2001: unresolved external symbol ___ security_cookie
Error lnk2001: unresolved external symbol @__ security_check_cookie @ 4
After the above modification, the actually generated debug version does not contain debugging information. Therefore, we can also allow the debug version under vc6 to directly use the release version compiled by vc7/vc8, however, you must modify the release version's runtime database type to make it consistent with vc6.
In my previous article, I introduced a simple C ++ program that briefly explains the correspondence between C ++ code and assembly code, in the subsequent articles, I will carefully introduce more details based on different topics. Although I would like to introduce the correspondence between C ++ and assembly code at the beginning, the VC compiler inserts various checks in the code, Seh, c ++ exceptions and other code, so I think it is necessary to first write something common when reading the compiled code generated by VC, then we can start to analyze the disassembly of the C ++ code. This article will first involve the runtime check (runtime checking)
Runtime checking
Runtime check is a dynamic check of Program Correctness/security provided by the VC compiler at runtime, you can enable the small type check and basic runtime checks in the C ++ option of the project to enable the runtime check.
You can also use the/RTC switch to enable the check. The/RTC is followed by C, U, and s to enable different types of check. Smaller type check corresponds to/RTCC, basic runtime checks corresponds to/RTCs and/rtcu.
/RTCC Switch
The RTCC switch can be used to check whether unwanted truncation occurs during type conversion. The following code is used as an example:
Char CH = 0; Short S = 0x101; Ch = s; |
When VC executes CH = s, the following error is reported:
The reason is that 0x101 has exceeded the char representation range.
Previously, the compilation code corresponding to the error code is as follows:
; 42: Char CH = 0; MoV byte PTR _ Ch $ [EBP], 0 ; 43: short S = 0x101; MoV word PTR _ S $ [EBP], 257; 00000101 H ; 44: CH = s; MoV CX, word PTR _ S $ [EBP] Call @ _ rtc_check_2_to_1 @ 4 MoV byte PTR _ Ch $ [EBP], Al |
As you can see, when assigning values, the VC compiler first places the value of S in the Cx register, and then calls the _ rtc_check_2_to_1 @ 4 function to check whether there is any data truncation problem. The result is placed in Al, finally, put Al in Ch. _ Rtc_check_2_to_1 @ 4 as the name suggests, it is to check that the data of two bytes is converted into one byte (short is two bytes, char is a byte), the Code is as follows:
_ Rtc_check_2_to_1: 00411900 push EBP 00411901 mov EBP, ESP 00411903 push EBX 00411904 mov EBX, ECx 00411906 mov eax, EBX 00411908 and eax, 0ff00h 0041190d je _ rtc_check_2_to_1 + 24 h (411924 H) 0041190f CMP eax, 0ff00h 00411914 je _ rtc_check_2_to_1 + 24 h (411924 H) 00411916 mov eax, dword ptr [EBP + 4] 00411919 Push 1 0041191b push eax 0041191c call _ rtc_failure (411195 H) 00411921 add ESP, 8 00411924 mov Al, BL 00411926 pop EBX 00411927 pop EBP 00411928 RET |
1. 00411904 ~ 00411906: ECx stores the value of S and is transferred to eax.
2. 00411908 ~ 0041190d: Check the eax and 0xff00 phases and check whether the result is 0. If the result is 0, the short value is 0 or a positive number of <128, which does not exceed the range, jump directly to 00411924 to get the result and return
3. 0041190f ~ 00411914: Check whether eax is equal to 0xff00. If it is equal, the short value is negative and> =-128. It is acceptable within the char representation range and jumps to 00411924.
4. If none of the above checks are passed, it indicates that this value has exceeded the range. An error is returned when the _ rtc_failure function is called.
To solve this problem, you can simply change the code to the following:
Char CH = 0; Short S = 0x101; Ch = S & 0xff; |
/Rtcu Switch
The function of this switch is to enable the check for uninitialized variables, which is more useful than static warnings. Consider the following code:
Int; Char ch; Scanf ("% C", & Ch ); If (CH = 'y') A = 10; Printf ("% d", ); |
The compiler does not know whether a is correctly initialized before printf through flow analysis, because the branch a = 10 is determined by external conditions, therefore, only the dynamic monitoring method can know whether the program has any bugs (of course, we can clearly see from here that this program must have bugs ). Obviously, comparing the value of a variable with a specific value does not know whether the variable is initialized. Therefore, the compiler needs to use an additional byte to track whether the variable is initialized:
The start code of the function is as follows:
Push EBP MoV EBP, ESP Sub ESP, 228; 000000e4h Push EBX Push ESI Push EDI Lea EDI, dword ptr [ebp-228] MoV ECx, 57; 00000039 H MoV eax,-858993460; cccccccch Rep stosd MoV byte PTR $ t5147 [EBP], 0 |
The last sentence is critical. Setting the value of $ t5147 to 0 indicates that the variable A is not initialized.
When CH = 'y', the compiler will set $ t5147 to 1 in addition to executing a = 10.
MoV byte PTR $ t5147 [EBP], 1 MoV dword ptr _ A $ [EBP], 10; 0000000ah |
Before printf, the compiler checks the value of $ t5147. If it is 0, it indicates that no Initialization is performed. Execute _ rtc_uninituse to report an error. Otherwise, the compiler jumps to the corresponding code to execute the printf statement:
CMP byte PTR $ t5147 [EBP], 0 JNE short $ ln4 @ wmain Push offset $ ln5 @ wmain Call _ rtc_uninituse Add ESP, 4 $ Ln4 @ wmain: MoV ESI, ESP MoV eax, dword ptr _ A $ [EBP] Push eax Push offset ?? _ C @ _ 02dpkjamef @? $ CFD? $ Aa @ Call dword ptr _ imp _ printf Add ESP, 8 Cmp esi, ESP Call _ rtc_checkesp |
/RTCs Switch
This switch is used to check stack-related problems:
1. initialize the variables on the stack to 0xcc in debug mode, and check for uninitialized problems.
2. Check the overrun of the array variable.
3. Check whether ESP is destroyed
In debug mode, the initialization variable is 0xcc.
Suppose we have the following code:
Void func () { Int; Int B; Int C; } |
The compilation code is as follows:
? Func @ yaxxz proc; func, comdat ; 38 :{ Push EBP MoV EBP, ESP Sub ESP, 228; 000000e4h Push EBX Push ESI Push EDI Lea EDI, dword ptr [ebp-228] MoV ECx, 57; 00000039 H MoV eax,-858993460; cccccccch Rep stosd ; 39: int; ; 40: int B; ; 41: int C; ; 42: ; 43 :} Pop EDI Pop ESI Pop EBX MoV ESP, EBP Pop EBP RET 0 ? Func @ yaxxz endp |
1. The sub ESP, 228: s compiler allocates 228 bytes to the stack.
2. Then the three push commands are used to save the register.
3. lea EDI, dword ptr [ebp-228] until the repstosd command is initialized from the ebp-228 to write 57 0 xcccccccc, that is, 57*4 = 228 0xcc, just to fill the previous sub ESP, 228 space allocated. This code initializes all variables to 0xcc.
There is a reason to select 0xcc:
1. 0xcc is different from the general initialization value. People generally prefer to initialize the variable to a relatively simple value such as 0, 1,-1, while 0xcc is usually large enough and negative, it is easy to pay attention to, and the general variable value may not be 0xcc, which may easily cause errors.
2. 0xcc = INT 3. If it is executed as a code, it will cause a breakpoint exception, which is more likely to cause attention.
Check the overrun of the array variable
Suppose we have the following code:
Void func { Char Buf [104]; Scanf ("% s", Buf ); Return 0; } |
After scanf is called, the following code is executed:
MoV ECx, EBP Push eax Lea edX, dword ptr $ ln5 @ wmain Call @ _ rtc_checkstackvars @ 8 |
This Code calls the _ rtc_checkstackvars @ 8 function to check whether 0xcccccccc is damaged at the beginning and end of the array. If yes, an error is reported. _ Rtc_checkstackvars is not given because the code is too long. This function uses the location and length information saved by the compiler to check the beginning and end of the array:
$ Ln5 @ FUNC: DD 1 Dd $ ln4 @ func $ Ln4 @ FUNC: DD-112; ffffff90h DD 104; 00000068 H Dd $ Ln3 @ func $ Ln3 @ FUNC: DB 98; 00000062 H DB 117; 00000075 H DB 102; 00000066 H Db 0 |
$ Ln5 @ func records the number of arrays, while $ ln4 @ func saves the array offset EBP-112 and the array length 104, $ Ln3 @ func saves the variable name (0x62, 0x75, 0x66, 0 = "Buf ").
Check ESP
ESP errors may be caused by the mistach of the call protocol, or the stack itself is not balanced. The compiler inserts an ESP check when calling other functions and when the functions PROLOG and epilog (start and end code:
1. When calling other external functions:
Suppose we have the following code:
The compilation code is as follows:
MoV ESI, ESP Push 1 Push offset ?? _ C @ _ 02dpkjamef @? $ CFD? $ Aa @ Call dword ptr _ imp _ printf Add ESP, 8 Cmp esi, ESP Call _ rtc_checkesp |
We can see that the checked code is very simple and straightforward. Save ESP in ESI. When printf is called to balance the stack, check whether the ESP and ESI are consistent, and then call _ rtc_checkesp, __rtc_checkesp code is also very simple:
_ Rtc_checkesp: 00412730 JNE esperror (412733 H) 00412732 RET Esperror: ...... 00412744 call _ rtc_failure (411195 H) ...... 00412754 RET |
If they are inconsistent, go to the esperror label to report an error.
2. When the function returns:
The following code is used as an example:
Void func () { _ ASM { Push eax } } |
The func function deliberately pushes eax to disrupt the balance of the stack. The corresponding assembly code is as follows:
? Func @ yaxxz proc; func, comdat ; 38 :{ Push EBP MoV EBP, ESP Sub ESP, 192; 000000c0h Push EBX Push ESI Push EDI Lea EDI, dword ptr [ebp-192] MoV ECx, 48; 00000030 H MoV eax,-858993460; cccccccch Rep stosd ; 39: _ ASM ; 40 :{ ; 41: Push eax Push eax ; 42 :} ; 43 :} Pop EDI Pop ESI Pop EBX Add ESP, 192; 000000c0h Cmp ebp, ESP Call _ rtc_checkesp MoV ESP, EBP Pop EBP RET 0 ? Func @ yaxxz endp |
In the function initialization code, func saves the EBP in the stack and saves the current ESP in the EBP.
? Func @ yaxxz proc; func, comdat Push EBP MoV EBP, ESP |
The key check code is later. When the func function recovers the stack, the stack will be restored to the State where the ESP is saved to EBP. At this time, EBP must be equal to ESP; otherwise, an error occurs.
Cmp ebp, ESP Call _ rtc_checkesp MoV ESP, EBP Pop EBP RET 0 ? Func @ yaxxz endp |
The dialog box displayed when an error occurs is as follows:
OK. This time it is written here. The following articles will be written as follows:
1./GS & Security cookie
2. Calling conventions
3. Name mangling
4. structured exception handling
5. Passing by reference
6. member functions
7. Object Layout
8. Virtual Functions
9. Virtual inheritance
10. c ++ exceptions
11. templates
Stay tuned.
Author: Atfield
Blog: Http://blog.csdn.net/atfield
Reprinted please indicate the source