What is ILT (incremental Link Table )?
I have studied the DLL import/export principles over the past two days. After reading some documents, I accidentally found an article on the Internet with errors. This article is still widely circulated, I am afraid I have missed many children. I think it is necessary to talk about it :)
Which search engine can be used to search for "C ++ virtual function ILT"? At the top of the list, there is an article titled "disassembly and resolution of C ++ virtual function calls". This article is transferred and transferred, I don't know where the original source is. The author of the original article has personally studied the compilation code produced by VC. The research spirit is commendable, but he has a wrong understanding of some concepts.
As mentioned in this article:
004010dc call @ ILT + 5 (test) (0040100a) // call the test function;
// Here @ ILT + 5 is the address of the JMP command to jump to the test function, all
// Function call will be like this @ ILT + 5 * n, n indicates the nth function in this module, and ILT means
// It is the import lookup table. When a program calls a function, it uses this table to jump to the corresponding function and runs the // line of code.
The author apparently only tested the debug version, because only debug will show @ ILT, and ILT does not mean import lookup table here. Import lookup table is a concept used when DLL is used, it has nothing to do with the implementation of C ++ virtual functions. Here the ILT is the meaning of incremental link table, and the abbreviation conflict is really annoying :)
What is incremental link table?
Think about it. If we want to compile the compiler and linker ourselves, we certainly hope that the faster the compilation connection runs, the better. At the same time, we also hope that the generated binary code will be fast and small, god is fair, and the fish and the bear's paw cannot have both sides. Therefore, we naturally think of two build methods, one is release, and the compilation is slower, but the generated binary code is compact and cool, it is a debug program that is fast to compile and run, and it doesn't matter if the generated code is bloated. The debug version expects programmers to build it repeatedly during development. In order not to waste programmers, try your best to make the compilation connection faster.
Assume that a program has two consecutive Foo and bar (the so-called continuity means that the function body is stored continuously after the compilation and connection). The Foo entry is located at 0x0400 and the length is 0 x bytes, the bar entry should be 0x0600 = 0x0400 + 0x0200. During development, programmers often modify code and build code. If the programmer adds some content to Foo, the foo function occupies 0 x bytes, the bar entry has to move 0x100 back to 0x0700, so there is a problem. If foo is called N times in the program, so linker has to modify the N function call points. Although linker is not too tired, it takes a long time to link and programmers will feel uncomfortable. So msvc build in the debug version won't make every function body so compact, and every function body has padding (all assembly code INT 3 is used to cause interruption, in this way, exceptions may occur when the padding part is not running due to odd reasons.) with these padding, the above mentioned problems can be mitigated to a certain extent, but when the function adds more content than padding, the problem persists. What should I do? Msvc uses incremental Link Table in debug build. ILT is actually a string of JMP statements. Each JMP statement corresponds to a function. The JMP destination is the function entry point, the difference with the absence of ILT is that the call to a function is not a direct call to the function entry point, but a call to the corresponding location in the ILT, and nothing is done at this location, directly add JMP to the function. The advantage is that when the address of a function entry changes, you only need to modify the corresponding value in the ILT and do not need to modify each call location, it is worthwhile to use a redundant ITL to reduce the time complexity from O (n) to O (1). Of course, the binary files of the debug version will be slightly larger and slower, and the release version will not use ILT.
Back to the article on the Internet, the original author made some explorations. Instead, I first called the function using ILT as the stepping stone JMP. I felt very interesting. Unfortunately, I did not test the release version, it is a pity that ILT has not been further explored. If you look at the release version of the assembly code, you will see a very compact and concise assembly code, it is truly pure vtable implementation.