Advertisement Jump to content
Sign in to follow this  
fir

code -> .data & data -> .code

This topic is 1785 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I heard that sometimes (often?) compilers put the data to code section

and vice versa (code to data section) though it is generally not good

and 'can even degrade performance'  - but why they do that was not explained

 

does maybe someone know something about that?

Edited by fir

Share this post


Link to post
Share on other sites
Advertisement

Other than hearing this, have you also read it somewhere on the interwebs? Perhaps you can post a link to such a claim, so we can know exactly what you are talking about?

Share this post


Link to post
Share on other sites

I'm not super knowledgeable in this area, perhaps someone can illuminate me -- aren't the code and data sections mostly throwbacks to the 16bit segmented memory days?

 

I recall that when I learned to program in a compilable version of QuickBASIC (v4.5 for those who remember) they actually had instructions for setting the code and data segments that were active. One of the major optimizations I discovered in one of my programs once, was that the structure of my map rendering was causing me to jump all over memory and even across data segments -- which was obviously horrible. When I fixed it, I went from 5fps to 50fps, in a tight rendering loop where blitting should have been the bottleneck.

Share this post


Link to post
Share on other sites

Other than hearing this, have you also read it somewhere on the interwebs? Perhaps you can post a link to such a claim, so we can know exactly what you are talking about?

alright, the first source is in agner fog objconv instruction

 

"11.27 How does the disassembler distinguish between code and data?
The first assumption is that code segments contain code and data segments contain data.
Unfortunately, some compilers put jump tables and other data into the code segment, even
though this gives inferior performance."
 
the second
 
"The problem wouldn't be as difficult if data were limited to the .data section (segment) of an executable (explained in a later chapter) and if executable code were limited to the .code section of an executable, but this is often not the case. Data may be inserted directly into the code section (e.g. jump address tables, constant strings), and executable code may be stored in the data section (although new systems are working to prevent this for security reasons)."
 
from
 
but this is not further explained and i wonder what is a reason of 
this "violations"

Share this post


Link to post
Share on other sites

You can really put anything you want in sections of the executable, it's all just bytes anyway. The sections can be called anything also. The only difference is the flags that the OS uses to allocate the memory. Pure data sections could for example be allocated as non-executable or read-only.

This is the function that Windows uses to allocate memory when it loads executables, it might give you some hints about this.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx

I'm not sure what he's on about when he says that data in the code section could give you performance issues. The data needs to be somewhere, and I don't see why loading it from one arbitrary memory address would be worse than doing it from another arbitrary address. It might even be beneficial to have it near the code, because the jump table data would likely end up in the same memory page as the code that uses it... But I'm not sure, so don't qote me on that. But the compiler writers are usually not morons, so it would be pretty safe to bet that they know what they are doing.

 

not always sometimes compilers do a really weak stuff...

i also sont know what he mean about this performance issues,

though i must say that year ot two years ago i was calling the data - i mean i wrote the procedures right in machine code in my prog like

 

char asmDot_SSE[] =
{
 
 0xC8, 0x00, 0x00, 0x00,
 0x8B, 0x45, 0x08,
 0x8B, 0x5D, 0x0C,
 0x8B, 0x4D, 0x10,
 0x0F, 0x10, 0x00,
 0x0F, 0x10, 0x0B,
 0x0F, 0x59, 0xC1,
 0x0F, 0x12, 0xC8,
 0x0F, 0x58, 0xC8,
 0x0F, 0x28, 0xC1,
 0x0F, 0xC6, 0xC9, 0x01,
 0xF3, 0x0F, 0x58, 0xC1,
 0xF3, 0x0F, 0x11, 0x01,
 0xC9,
 0xC3
 
};
 
and when counting the execution times it was much slower than 
executing it in normal code section - i dont know what was a reason of that but it worked but had a speed penalty sadly
 
mayve some guarding mechanism is involved ? maybe it works also in opossite way, i mean accessing data from .code pages.. dont know
Edited by fir

Share this post


Link to post
Share on other sites

I'm not super knowledgeable in this area, perhaps someone can illuminate me -- aren't the code and data sections mostly throwbacks to the 16bit segmented memory days?

 

I recall that when I learned to program in a compilable version of QuickBASIC (v4.5 for those who remember) they actually had instructions for setting the code and data segments that were active. One of the major optimizations I discovered in one of my programs once, was that the structure of my map rendering was causing me to jump all over memory and even across data segments -- which was obviously horrible. When I fixed it, I went from 5fps to 50fps, in a tight rendering loop where blitting should have been the bottleneck.

 

no, you mixing old 640K ds: cs: stuff here... 

i am moving about .data and .code sections in program image,

which are later mapped into a ram memory where ram pages are also mapped as "executable" "data" "readolny data" etc - and i wonder on the details of this here

Share this post


Link to post
Share on other sites

and when counting the execution times it was much slower than 
executing it in normal code section - i dont know what was a reason of that but it worked but had a speed penalty sadly


I'm willing to bet you'd see a lot of overhead in the function that calls into your code array; jumps and address dereferences and so on. There's also a difference between the data L1 cache and the code L1 cache, and pulling data in to be executed likely incurs additional cache misses and requests in order to run. This is also a possible reason for data in the code section being slower. One would have to look at the disassembly and run under a suitably capable profiler (vtune, cachegrind, etc.) to verify that assumption. This is part of what locality of reference in code is all about and why a profile-guided linker will lay out the compiled code differently (so functions that call each other end up close together in the machine code image). Naively written SSE code can also incur a lot of overhead due to various modes and flags; compiler intrinsics are almost always the right way to go to avoid these kinds of issues thanks to the compiler being able to set things up properly when you're not hiding your use of SSE from the compiler.

In any case, this works on Windows because you have to opt in to the NX bit on Windows for backwards-compatibility reasons. It's usually on by default in Linux and most other major OSes that support the feature. The flag is set on by default with newer versions of Visual Studio, I think, but imported project files or projects built with older VS versions probably don't have the bit set in the executable image to enable the NX behavior. This is similar to how a 32-bit application has to opt in to being LAA (large address aware) to access more than 2GB of memory even on a 64-bit kernel.

Share this post


Link to post
Share on other sites
Jump tables for switch statements are technically data which are typically stored in the code section after the end of the function which contains the switch statement.
 
Microsoft compilers output files organized like so:
 
 
Headers
Code Section (the headers designate this section as Execute+Read)
    Function 0
        Instructions
        Optional: NOP or INT3 padding
        Optional: Case mapping table (and optional padding to the next pointer-sized boundary)
        Optional: Case block pointers (and optional padding to the next 16 byte boundary)
    Function 1
        Instructions
        ...ETC...
Initialized Data Section(s) (the headers designate these section(s) as Read+Write or just Read)
    Data!
Import Section (readonly after the loader finishes with it)
    Data!
Export Section (readonly after the loader finishes with it)
    Data!
Relocation Section (discarded after the loader finishes with it)
    Data!
Resource Section (readonly)
    Data!
(etc...)
Other compilers (or binary compressors such as UPX) are free to rearrange anything besides the main header that they want to. Edited by Nypyren

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!