_ Rtc_checkesp and other C rumtime Problems

Source: Internet
Author: User

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:

 

Printf ("% d", 1 );

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.