Skip to content

QEMU: Make PDB path/name deterministic#58

Merged
vineelko merged 1 commit into
mainfrom
users/vineelko/fix_pdb_alt_path_1029
Oct 30, 2025
Merged

QEMU: Make PDB path/name deterministic#58
vineelko merged 1 commit into
mainfrom
users/vineelko/fix_pdb_alt_path_1029

Conversation

@vineelko
Copy link
Copy Markdown
Contributor

Description

Explicitly specifying the PDB name via PDBALTPATH improves debugger usability for several reasons:

  1. Cargo copies the final .efi and its .pdb to target\<target-triple>\[debug|release] as qemu_sbsa_dxe_core.efi(no hash appended.
  2. Full paths embedded in the efi generated on WSL make it harder for the debugger to map the .efi to the .pdb on Windows.
  3. Without this PR, the PDB file name is non-deterministic (it includes a hash), even though it contains the full path, making debugging/scripting tedious.
  4. As long as the .efi and .pdb files are in the same directory, WinDbg can easily pick up the PDB file without any manual fixes.
  5. Even if they are not in the same directory, .sympath+ can be used to point WinDbg to the directory containing the pdb files.

Since WinDbg checks the current directory for the PDB by default, based on the path embedded in the .efi, providing just the file name (not the full path) for PDBALTPATH allows the debugger to locate it easily without requiring any file renames.

Option 1: Without the PDBALTPATH option
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           3f      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core-b3e9a890bb6be3ce.pdb

Option 2: -C link-arg=/PDBALTPATH:%_PDB% # This is the default behaviour by rustc.
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           3f      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core-f3080c9eaed3fe01.pdb

Option 3: -C link-arg=/PDBALTPATH: # only useful on Windows, that too, only if the path exists(not useful for remote/offline debugging)
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           83      1b1074   1afa74    Format: RSDS, guid, 1, E:\repos\patina-dxe-core-qemu\target\aarch64-unknown-uefi\debug\deps\qemu_sbsa_dxe_core-765fdbf691f03726.pdb

Option 4: -C link-arg=/PDBALTPATH:qemu_sbsa_dxe_core.pdb
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           2e      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core.pdb

Fixes #445

  • Impacts functionality?
  • Impacts security?
  • Breaking change?
  • Includes tests?
  • Includes documentation?

How This Was Tested

Validated on QEMU

Integration Instructions

Other platform specific rust dxe binaries need to incorporate a similar change to achieve a better debugging experience!

Explicitly specifying the PDB name via `PDBALTPATH` improves debugger usability
for several reasons:

1. Cargo copies the final `.efi` and its `.pdb` to
   `target\<target-triple>\[debug|release]` as `qemu_sbsa_dxe_core.efi`(no hash
   appended.
2. Full paths embedded in the PDB generated on WSL make it harder for the
   debugger to map the `.efi` to the `.pdb` on Windows.
3. Without this PR, the PDB file name is non-deterministic (it includes a hash),
   even though it contains the full path, making debugging/scripting tedious.
4. As long as the `.efi` and `.pdb` files are in the same directory, WinDbg can
   easily pick up the PDB file without any manual fixes.
5. Even if they are not in the same directory, `.sympath+` can be used to point
   WinDbg to the directory containing the pdb files.

Since WinDbg checks the current directory for the PDB by default, based on the
path embedded in the .efi, providing just the file name (not the full path) for
PDBALTPATH allows the debugger to locate it easily without requiring any file
renames.

```
Option 1: Without the PDBALTPATH option
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           3f      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core-b3e9a890bb6be3ce.pdb

Option 2: -C link-arg=/PDBALTPATH:%_PDB% # This is the default behaviour by rustc.
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           3f      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core-f3080c9eaed3fe01.pdb

Option 3: -C link-arg=/PDBALTPATH: # only useful on Windows, that too, only if the path exists(not useful for remote/offline debugging)
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           83      1b1074   1afa74    Format: RSDS, guid, 1, E:\repos\patina-dxe-core-qemu\target\aarch64-unknown-uefi\debug\deps\qemu_sbsa_dxe_core-765fdbf691f03726.pdb

Option 4: -C link-arg=/PDBALTPATH:qemu_sbsa_dxe_core.pdb
Debug Directories(1)
    Type       Size     Address  Pointer
    cv           2e      1b1074   1afa74    Format: RSDS, guid, 1, qemu_sbsa_dxe_core.pdb
```

Signed-off-by: Vineel Kovvuri[MSFT] <vineelko@microsoft.com>
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@os-d os-d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like getting rid of the hash, but I also think the full path is nice for automatic symbol resolution. Today the problem is that the pdbaltpath doesn’t have the right value, so you have to manually add a symbol path in the debugger.

My preference would be: use the full path and don’t create the binary/pdb with a hash in the name.

The full path allows for automatic symbol resolution, but it doesn’t prevent manual symbol inclusion either, e.g. the case where you built in WSL and then copy the pdb to Windows (or point directly into WSL which sometimes works).

I’m not following how WinDbg is automatically picking up the pdb in the scenario you are describing, you are saying WinDbg knows the path to the efi binary from somewhere and so searches that directory? If so, that works, too, I just want automatic symbol resolution, don’t care the path.

@makubacki
Copy link
Copy Markdown
Collaborator

Given that this is only affecting the QEMU DXE Core build, I'm also primarily interested in automatic symbol resolution since this will commonly be built and run on the same system as QEMU and the source debugger.

However, I think somewhere in our platform integration docs, the tradeoffs should be called out for different approaches and they could be used with the Patina debugger. In particular, it's common on real platforms to debug a build from a CI agent or another host system so the symbols need to be mapped. If that's not already documented somewhere walking through that kind of scenario would probably be helpful for others.

@cfernald
Copy link
Copy Markdown
Contributor

I agree that option 3 is preferable uniquely to QEMU, but most platforms would likely want to use option 4.

That said, symbol paths are persistent (usually) so it shouldn't be that inconvenient for folks to add their path.

@vineelko
Copy link
Copy Markdown
Contributor Author

vineelko commented Oct 29, 2025

We all want the hash to get removed! But removing the hash is not a trivial option, atleast in terms of logistics, as this requires a change similar to the following for UEFI targets in the Cargo repository(we can definitely pursue that):

rust-lang/cargo#7358
rust-lang/cargo#7400

Putting that aside, I assume by “automatic symbol resolution” you mean that whenever you launch the .efi file in WinDbg it will automatically load the PDB, right? In fact, that is exactly what this PR is striving for. Not sure if I made it clear: the symbols get automatically resolved by WinDbg because the .pdb is sitting next to the .efi.

ModLoad: 00000100`00000000 00000100`00125000   C:\temp\stacktrace\qemu_sbsa_dxe_core.efi
qemu_sbsa_dxe_core!qemu_sbsa_dxe_core::_start:    <--- symbols automatically resolved.
00000100`0000741c a9bb53f3 stp         x19,x20,[sp,#-0x50]!
0:000> lm
start             end                 module name
00000100`00000000 00000100`00125000   qemu_sbsa_dxe_core   (private pdb symbols)  .\qemu_sbsa_dxe_core.pdb

The reason you have to manually add the symbol path today is because you are launching the .efi from the debug or release directory, which happens to contain a “-hash.pdb” embedded in it, and the .pdb sitting next to that .efi does not have the “-hash”, so WinDbg thinks it is not a match(default search behavior fails). Upon giving the path to directory containing the pdb(with the hash in the filename) it is able to locate the .pdb. This is the exact manual step I want to get rid off (and also to benefit other offline/remote scenarios).

Having a full path(even without the hash) to the PDB is not desirable because it only makes the debugger happy in the one case where the path to the PDB actually exists on the filesystem. In all other cases you still need to manually teach the debugger which path to use. On top of that, if the file names don't match (because of the hash) then we need to either rename the PDB sitting next to the binary to include the hash or point to the directory containing the PDB with the hash in its filename.

By not embedding the path and embedding only the file name, we solve two problems:

  1. Binary names match PDB names, this solves the debugger not being able to pick the PDB even if it is in the search path.
  2. In 99% of cases, the PDB sits next to the binary and, because of the debugger's default search behavior, the debugger can pick up the PDB. No additional manual steps would be required.

This is what Windows binaries also do:

Debug Directories(4)
	Type       Size     Address  Pointer
	cv           22      1a30fc   1a30fc	Format: RSDS, guid, 1, ntdll.pdb

Lastly, this does not break any existing automatic symbol resolution workflows, even on QEMU. I have tested it.

@os-d
Copy link
Copy Markdown
Contributor

os-d commented Oct 29, 2025

Lastly, this does not break any existing automatic symbol resolution workflows, even on QEMU. I have tested it.

I think we are talking about two different scenarios. I believe you are talking about the scenario where you are loading the efi binary directory in WinDbg to resolve stack trace symbols.

I am talking about the debugger scenario, where you are connected to a live system and are trying to load symbols there. That's where this automatic resolution doesn't work; you need the full path:

e.g. when I pull this branch

image

That's where I'm saying with the full path automatic symbol resolution works. Today, it doesn't work with the full path, because we don't have the hash in the full path, so you have to add a separate symbol directory to get that to find the pdb with the hash.

WinDbg doesn't know where the EFI binary is from when you are doing live debugging. That's where I would like automatic resolution to work.

@vineelko
Copy link
Copy Markdown
Contributor Author

Had an offline chat with Oliver.

Live Debugging Scenario:
Currently, we don’t need any additional steps to resolve symbols as long as the live debugging target is running on the same host that built the sources. Automatic symbol resolution work, because the full path (including the hash) is already baked into the .efi file(because of Option 3). Closed the issue as it is working as expected. However, with my current PR, this setup would break the QEMU live debugging scenario unless an additional sympath+ command is used.

Offline Binary Inspection Scenario:
Below is the script I use to load .efi binary to inspect unwind-related information in the binary built in WSL. Each time the binary is rebuilt with new changes, the hash changes, and I have to update the script accordingly.

@echo off
taskkill /T /F /IM DbgX.Shell.exe
del *.efi
del *.pdb

busybox64 cp "\\wsl.localhost\Ubuntu-24.04\home\vineel\repos\patina-dxe-core-qemu\target\aarch64-unknown-uefi\release\qemu_sbsa_dxe_core.efi" qemu_sbsa_dxe_core-6fabfb0368cf38f2.efi
busybox64 cp "\\wsl.localhost\Ubuntu-24.04\home\vineel\repos\patina-dxe-core-qemu\target\aarch64-unknown-uefi\release\qemu_sbsa_dxe_core.pdb" qemu_sbsa_dxe_core-6fabfb0368cf38f2.pdb

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\editbin.exe" /rebase:base=0x10000000000 qemu_sbsa_dxe_core-6fabfb0368cf38f2.efi

windbgx -y . -z qemu_sbsa_dxe_core-6fabfb0368cf38f2.efi

Anyways, it seems there’s no perfect common ground without compromising one of our experiences. That said, I’d like to point out that by relying on full pdb path, we’re deviating from the typical workflow used when debugging physical platforms, or Windows in general, where the first step after connecting to the target is usually to run .sympath+ to set the symbol path. For the sake of convenience (not having to set sympath explicitly during live debugging, which today only applies to QEMU), I feel like we’re sacrificing other use cases(Like working with binaries built in WSL, as building aarch64 on Windows is not working at the moment) especially because updating the above script on every rebuild is not cool as it is hard to find the new hash😄

How about adding the QEMU build path to _NT_SYMBOL_PATH, which persist across debugging sessions and .symfix and .sympath+ would also be not needed and should solve the live debugging QEMU use case?

E:\repos\patina-dxe-core-qemu\target\x86_64-unknown-uefi\debug;E:\repos\patina-dxe-core-qemu\target\aarch64-unknown-uefi\debug;srv*c:\symbols.pri*https://symweb.azurefd.net;srv*C:\Symbols*https://msdl.microsoft.com/download/symbols

@os-d
Copy link
Copy Markdown
Contributor

os-d commented Oct 29, 2025

(not having to set sympath explicitly during live debugging, which today only applies to QEMU)

This applies to physical platforms, too. If you run WinDbg from the machine you built the code for, the full path will also resolve. Nothing is QEMU specific here, except for this repo :)

@vineelko
Copy link
Copy Markdown
Contributor Author

Yaa my bad, agreed it applies to physical platforms build from the same host. But _NT_SYMBOL_PATH should still work because every binary is uniquely tied to its PDB(using CodeView signature) and the debugger knows which pdb to pick😉

@os-d
Copy link
Copy Markdown
Contributor

os-d commented Oct 29, 2025

Yaa my bad, agreed it applies to physical platforms build from the same host. But _NT_SYMBOL_PATH should still work because every binary is uniquely tied to its PDB(using CodeView signature) and the debugger knows which pdb to pick😉

I'm okay if we lose automatic symbol resolution. It's not a major pain to do the .sympath+. My main point is that consumers of Patina have liked automatic symbol resolution and we attempt to have this repo be a good representation of what platforms should do/would like to do. I think your current use case is pretty niche. Outside of the stacktrace debugging I'm assuming you are doing, I don't think that case would occur very often. So I'm more weighing is it worth losing the automatic symbol resolution for a small use case (which don't get me wrong, I believe is annoying :).

I'm not overly concerned. I prefer automatic symbol resolution because it has made it easier for consumers of the debugger to pick up and go quickly without extra steps. But it's only one extra step.

@cfernald
Copy link
Copy Markdown
Contributor

I'm find to take this either way. I do think we should document this in patina to allow platforms to make their own choices.

@makubacki
Copy link
Copy Markdown
Collaborator

I'm going to go ahead and approve because I think the conversation has reached an end. I also don't think .sympath+ is a big obstacle for someone already committed to debugging in WinDbg. In the documentation for platforms, a concise summary of tradoffs would be nice.

@vineelko
Copy link
Copy Markdown
Contributor Author

vineelko commented Oct 30, 2025

Thanks folks, The platform docs already talks about .sympath+ (step 2). But I can also add about _NT_SYMBOL_PATH and a brief note about the tradeoffs.

.sympath+ <path to pdb dir> ; usually <cloned dir>\target\x86_64-unknown-uefi\debug\deps
.srcpath+ <path to src dir> ; usually <cloned dir>\src

@vineelko vineelko merged commit 3637f9a into main Oct 30, 2025
5 checks passed
@vineelko vineelko deleted the users/vineelko/fix_pdb_alt_path_1029 branch November 5, 2025 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: PDB Path (or AltPath) Does Not Match Actual PDB Location

6 participants