I was making a small change to a function:  adding to it a couple UINTN auto variables, a new auto EFI_GUID variable, and a handful of changed lines.

Suddenly, the project would no longer compile.  I got this error message from the Microsoft linker:

TSEHooks.obj : error LNK2019: unresolved external symbol __chkstk referenced in function Foo

Build\TSE.dll : fatal error LNK1120: 1 unresolved externals

NMAKE : fatal error U1077: 'C:\WinDDK\7600.16385.1\bin\x86\amd64\LINK.EXE' : return code '0x460'
Stop.

======================
Build Error!!
======================

This surprised me—why is the linker complaining?  “unresolved external symbol”—I didn’t add a new function call, and neither did I add an extern reference.  Are my linker paths messed up somehow?  After burning lots of time trying various wild goose chases I started searching more for this “__chkstk”—what is that?

I started searching Google for help, and found a forum posting with the following comment:

The "chkstk" unresolved external is caused by the compiler checking to see if you've occupied more than (I think 4K on an x86 system) stack space for local variables…
Could I have pushed the function over the maximum stack space?  As I mentioned, I only added two UNITNs (8B each) and an EFI_GUID (16B) for 32B total.

Looking further I noticed that one of the already existing auto variables in this function was a SETUP_DATA structure variable—the variable type that holds all the BIOS Setup program settings information.  This was the problem—there are over 1200 variables contained in this one structure!

After further investigation, I found the following from Microsoft:

__chkstk Routine

Called by the compiler when you have more than one page of local variables in your function.

__chkstk Routine is a helper routine for the C compiler.  For x86 compilers, __chkstk Routine is called when the local variables exceed 4K bytes; for x64 compilers it is 8K.

My solution was going to be to move the SETUP_DATA variable to file scope with internal linkage, but to my surprise I found someone had already done that!  So, there was a file-scope SETUP_DATA variable, and then someone created another automatic SETUP_DATA variable within the scope of one of the functions.  Messy!  Anyway, it made my job easier—I simply removed the auto copy of SETUP_DATA and the linker error went away.

Two Takeaways

1) Microsoft, couldn’t there by a better message for communicating that the function has violated its stack space?  Something like:

Stackoverflow in function Foo:  Requested X bytes, maximum limit is 8192 bytes

rather than:

LNK2019: unresolved external symbol __chkstk referenced in function Foo

2) Developers, be on the lookout for usages of the BIOS Setup data structure.  I’m guessing it’s probably the largest of all the UEFI variables, and by a good margin.

Post a Comment

  1. An unresolved external is precisely that. The compiler cannot find stack overflows at compile time, it inserts calls to __chkstk so that the checks can be performed at runtime. You're getting the unresolved external link error because you are not linking with a library that contains __chkstk.

    ReplyDelete
  2. You can use the "/Gs999999" option in your project to get rid of the __chkstk linker error unless you need more than 999999 bytes on the stack :) (IMHO, 999999 is the maximum value you can pass to the linker.)

    ReplyDelete
    Replies
    1. IMHO? I meant AFAIK. All those abbreviations...

      Delete

Be sure to select an account profile (e.g. Google, OpenID, etc.) before typing your comment!