October 29th, 2024

Reverse-engineering what a “short” section is

There is a linker error LNK2004 that says that the “short” section is too long. What does this mean? What’s a “short” section? What makes it shorter than a “long” section?

You actually have enough information to figure this out.

Look closely at the error message, and you’ll see a big clue:

gp relative fixup overflow to 'target'; short section 'section' is too large or out of range.

The error message says “gp relative fixup”, and “gp” is the traditional name of a dedicated register on Itanium and PowerPC that points to a module’s global variables.

The reach of the gp register is 4MB on Itanium and 64KB on PowerPC, and the limited reach is consistent with the “relative fixup” part of the error message. It’s saying that the linker needs to fix up an indexed indirect instruction, but the offset is too large for the instruction’s reach.

Recall that on Itanium and PowerPC, global variables are divided into two classes: “small” and “large”. All the “small” variables are placed in a block of memory that the gp register points to. All the “large” variables are placed elsewhere in the process, and a pointer to the “large” variable is added to the list of “small” variables.

gp → Small A
    • → Large B
    Small C  
    Small D
    • → Large E
       
       

In the above example, we have the gp register pointing to a block of memory (known in PowerPC as the Table of Contents). This block of memory contains some small variables (Small A, Small C, and Small D), and it also has pointers to large variables (Large B and Large E).

Loading a small variable can be done by indirecting from the gp register.

// Itanium
    addl    r30 = 16, gp ;; // Calculate address of Small C variable
    ld4     r30 = [r30]     // load 32-bit value from Small C

// PowerPC
    lwz     r3, 16(r2)      // load 32-bit value from Small C (relative to r2)

But loading a large variable requires two steps, one to load the pointer as a small variable, and then another to dereference that pointer to get to the real data.

// Itanium
    addl    r30 = 8, gp ;;  // Calculate address of Large B pointer (in small data)
    ld8     r30 = [r30] ;;  // Load the pointer to Large B
    ld4     r30 = [r30] ;;  // Load 32-bit value from Large B

// PowerPC
    lwz     r3, 16(r2)      // load pointer to Large B
    lwz     r3, 0(r3)       // Load 32-bit value from Large B

Variables placed in the “small variables” block are faster to access, but you have space for only a limited number of them, and large variables each consume a pointer in the small variables block as well.

Now the error message makes sense: You tried to pack too much data into the “small variables” block. The linker apparently uses the terms “short” and “long” to refer to what we have been informally calling “small” and “large”.

Windows dropped support for both Itanium and PowerPC quite some time ago, so you’re not going to see these errors coming from any version of the linker less than a decade old. The error message is vestigial. Nevertheless, the error message number has already been assigned, so you’ll see it in a list of error codes, even though there is no longer any way to cause it to be generated.

Related reading: What is the thread reaper?

Topics
History

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.