llvm / llvm/llvm-project

LLDB does not account for the difference between Windows PE Section VirtualSize and RawDataSize...

Open
#220,618 8 comments 0 reactions 0 assignees View on GitHub
lldb platform:windows
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

...when looking up global variables that are zero initialised.

We found this on a downstream Windows on Arm (arm64) buildbot. I don't know why it wasn't hit on Linaro's bot. Probably some compiler/linker/toolchain difference. Either way I don't think it's a bug in the tools.

Downstream we have these tests failing:
```
Failed Tests (12):
lldb-api :: commands/expression/pr52257/TestExprCrash.py
lldb-api :: commands/target/dump/TestTargetDumpTypeSystem.py
lldb-api :: functionalities/breakpoint/breakpoint_on_lambda_capture/TestBreakOnLambdaCapture.py
lldb-api :: lang/cpp/class-loading-via-member-typedef/TestClassLoadingViaMemberTypedef.py
lldb-api :: lang/cpp/class-template-non-type-parameter-pack/TestClassTemplateNonTypeParameterPack.py
lldb-api :: lang/cpp/class-template-type-parameter-pack/TestClassTemplateTypeParameterPack.py
lldb-api :: lang/cpp/complete-type-check/TestCppIsTypeComplete.py
lldb-api :: lang/cpp/crtp/TestCppCRTP.py
lldb-api :: lang/cpp/non-type-template-param/TestCppNonTypeTemplateParam.py
lldb-api :: lang/cpp/template-arguments/TestCppTemplateArguments.py
lldb-api :: lang/cpp/typedef-to-outer-fwd/TestTypedefToOuterFwd.py
lldb-api :: windows/msvcrt/TestMSVCRTCException.py
```
All of the lang/cpp tests load a program file but do not actually run it. What they should be able to do is print an expression just using the memory available from the program file.

I'll take `lldb/test/API/lang/cpp/class-loading-via-member-typedef/TestClassLoadingViaMemberTypedef.py` as the example. It failed with:
```
AssertionError: 'error: Couldn't materialize: couldn't get the value of variable pull_in_classes: read memory from 0x140063c00 failed
error: errored out in virtual lldb_private::LLVMUserExpression::DoExecute, couldn't PrepareToExecuteJITExpression
' is not success
```
Let's look at the sections of the program file:
```
Section {
Number: 3
Name: .data (2E 64 61 74 61 00 00 00)
VirtualSize: 0x2478
VirtualAddress: 0x63000
RawDataSize: 3072
PointerToRawData: 0x61E00
PointerToRelocations: 0x0
PointerToLineNumbers: 0x0
RelocationCount: 0
LineNumberCount: 0
Characteristics [ (0xC0000040)
IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
IMAGE_SCN_MEM_READ (0x40000000)
IMAGE_SCN_MEM_WRITE (0x80000000)
]
}
Section {
<...>
VirtualAddress: 0x66000
<...>
```
Assume the base address is `0x14...`. The failing address is supposed to be in section 3. `0x63000 + 0x2478 = 0x65478`. It should be reading from the 0 part of the section, which is not in the file literally but is in the zero pad ending of the section, indicated by the `VirtualSize` being larger than the `RawDataSize`.

Except lldb is not accounting for that. `0x63000 + 3072 (RawDataSize) = 0x63c00` which is the exact failed address.

There is a test case that covers this difference but not in this exact way, lldb/test/Shell/SymbolFile/NativePDB/globals-bss.cpp.

To reproduce the issue with that test, delete the `llvm-readobj` line and add a global variable of sufficient size to be bigger than the smallest unit the linker will allocate for the non-zero init data:
```
volatile char padding[4096] = {1};
```
(if you just add an int, it'll set RawDataSize to a minimum 512 bytes and that avoids the crash)

Now we can see using llvm-readobj that we have the same situation:
```
# | 43: VirtualSize: 0x1004 (aka 4096 + 4, aka the size of the init'd array, plus the size of the int)
# | 44: VirtualAddress: 0x3000
# | 45: RawDataSize: 4096 (the size of the init'd array - only!)
```
And it fails in the same way:
```
# | 8: (lldb) target variable GlobalVariable
# | 9: (int) GlobalVariable =
# | check:36'2 ? possible intended match
```

Contributor guide

Open the contributing guide

Research direction

Start with lldb/test/Shell/SymbolFile/NativePDB/globals-bss.cpp and reproduce the failure after adding the 4096-byte initialized padding described in the issue. Compare the PE section's VirtualSize and RawDataSize with the address LLDB reads, then run that shell test and the listed Windows and C++ API tests. Done means zero-initialized variables in the virtual-only section area can be inspected successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
devtools, operating-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.