Why does my __FILE__ macro produce an invalid address, which mysteriously becomes valid a few moments later?

A customer couldn't understand what the debugger was showing them. They had a function that used the __FILE__ macro, but the address of the resulting string was reported by the debugger as invalid:

// source code:
char *fileName = __FILE__;

// In the debugger
0: kd> ?? fileName
char * 0x00796d60
 "--- memory read error at address 0x00796d60 ---"
0: kd> db 0x00796d60
00796d60  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796d70  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796d80  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796d90  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796da0  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796db0  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796dc0  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00796dd0  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

The __FILE__ macro expanded to an invalid address. How can this be?

But wait, it gets even stranger: After executing a few lines of code, the pointer suddenly becomes good!

0: kd> ?? fileName
char * 0x00796d60

What's going on here?

The thing to note is the prompt. The customer is using kd, the kernel debugger, rather than a user-mode debugger.¹ The kernel debugger shows what is in memory right now. If something is paged out, the kernel debugger won't try to page it in, because that would require the kernel to run, and that would kind of interfere with kernel debugging. (Besides, the kernel debugger doesn't know whether it's safe to page in memory right now. The current IRQL may be one that disallows page faults.)

Executing a few lines of code did something that caused the memory be paged in. The most likely thing that happened is that the file name was printed to a log file, and that means reading the file name, which means paging it in.

But that's not the only thing that could've caused the memory to be paged in. Another possibility is that the program accessed a variable that happens to reside on the same page. The unit of paging is the (um) page, so any variables that share the same page will be paged in and out together.

But the root cause for the confusion is that the customer is using the kernel debugger to debug user-mode code. It is possible to debug user mode with the kernel debugger, but it's quite cumbersome because you are debugging at a very, very low level, and lots of things you take for granted are no longer available. If you're going to be debugging user-mode code, you probably want to use a user-mode debugger.

¹ The customer disputed this assertion that they were using the kernel debugger. "We are using WinDbg, not kd." Explain.

Comments (7)
  1. Darran Rowe says:

    I would imagine that the customer could have gotten confused, kd.exe is installed as part of Debugging Tools for Windows in the Windows SDK.
    Windbg.exe also supports the -k and -kl options, so that could have an effect on windbg, although to be honest I have never really needed to use the kernel debugger.

  2. The MAZZTer says:

    A quick Google reveals WinDbg can, in fact, do kernel debugging.

    it’s like saying you proudly don’t own a gasoline car, and thus, don’t pollute the atmosphere. But you drive a gas motorcycle.

  3. David Haim says:

    the title is a bit clickbaitish, it doesn’t relates to __FILE__ macro directly and it but to any string literal viewed under kernel debugger.

    non the less, it provides useful anecdote for kernel mode debugging.

    1. Darran Rowe says:

      First of all, click bait implies that you would be more tempted to click it due to this title . But since __FILE__ macro implies C style string literals, I don’t see why it would be any more tempting. In fact, if you weren’t well versed with the pre-processor, then the title would be more confusing.
      Secondly, since the pre-processor replaces __FILE__ with a C style string literal then it is directly related. In fact the customer really noticed this because they were looking at code which took the string literal that came from the __FILE__ macro as a parameter. So either way, it is still directly related.

  4. Piotr says:

    yeah, explain :)
    I didn’t know you can unknowingly run LIVE KERNEL DEBUGGING. Opening up a dump file is one thing, but live kernel debugging? Wouldn’t that interfere with more than just your application? And how do you even run that undistinguishably from user mode debugging? You don’t choose a process to debug in kernel mode, right? You debug “the computer”.

    1. Tilmann Krueger says:

      Yeah, that puzzles me, too! And when you debug a NT installation, don’t you have to boot with a special flag? And connect another Machine via USB or something? And connect the debugger on that second machine to the debug target, (because it will wait for the debugger to connect)?

      How can one do all these things and not be aware of them?

    2. Karellen says:

      If you run a program without knowing what it does, or run a program with command line options without knowing what those options do, or run a program that runs another program in a way you weren’t expecting, you can unknowingly do… anything the computer lets you do. Including call the kernel debugging APIs.

      Yes, running a kernel debugger will interfere with more than just your application. That’s kind of an inevitable consequence of the fact that it’s a kernel debugger.

      If you’re not debugging a “process” (or “task” or “thread”) in kernel-mode debugging, what are you debugging? The kernel may not have a PID (or it may?), but it has a thread (or threads) of execution, each with a stack, a stack pointer, an open file table, a set of memory mapped pages (with TLB entries), a set of synchronisation objects, and pretty much everything else a user-mode process has. If you want to step through “the computer”, you’re stepping through something that looks exactly like a process. And, as you would want to switch between threads in a user-mode debugger, you’ll want to switch between user-mode and kernel-mode “processes” in a kernel-mode debugger. (With others paused, and maybe others running in the background on other CPUs?)

      If you didn’t want that, you shouldn’t have run a kernel-mode debugger. On the other hand, if you don’t really know what programs you’re running, you probably shouldn’t be running them as Admin, or as any other user with kernel-debugging privileges.

Comments are closed.

Skip to main content