【发布时间】:2016-11-22 00:28:30
【问题描述】:
在这种情况下,几张照片值几千字,所以我将从描述我所观察到的内容开始。这是我的第一个展览,我将其称为“堆栈框架 1”:
在这里,我将FLValue 类型的参数(它是指向C++ 类型的指针的typedef,该指针位于定义被调用函数的DLL 的内部)到一个名为FLValue_AsString 的函数。此时参数的值为0x0486d10e。
现在请注意我的第二个展览,我将其称为“堆栈框架 2”:
此堆栈帧位于堆栈帧 1 的正上方,是输入函数 FLValue_AsString 的结果。据我所知,调试器立即介入并没有采用任何内联的旁路(这是在所有优化都禁用的情况下运行的)。然而,奇怪的是,注意v(上面的FLValue)的值现在是0xedcf6fc2。由于无效的内存访问,这显然会导致崩溃。到底发生了什么?
其他一些信息:
- 编译器设置为将解决方案中所有项目中的函数编译为
__cdecl - 有问题的函数在
extern "C"块内定义 - LiteCore.dll(暴露
FLValue_AsString的 DLL)不直接编译文件,而是将三个静态库和其他两个 dll 链接在一起,并定义暴露符号的定义文件等函数实际上是在其中一个静态库中定义的。 - 这不是我观察到这种行为的唯一地方,尽管大多数调用都正常运行
- 如果我手动将调试器中的内存地址更改为堆栈帧 1 中的地址,那么我可以在没有无效内存访问的情况下继续操作
- 在 OS X 上使用 Apple Clang 或在 Ubuntu 上使用 GCC 编译时,没有观察到这些问题
编辑屏幕截图中的一些文本形式的信息:
跟踪@堆栈帧 1(从 main() 开始):
C4Tests.exe!PerfTest::insertDocs(const _FLArray * docs) Line 141 C++
C4Tests.exe!`anonymous namespace'::____C_A_T_C_H____T_E_S_T____491::test() Line 501 C++
C4Tests.exe!Catch::NWayMethodTestCase<`anonymous namespace'::____C_A_T_C_H____T_E_S_T____491>::invoke() Line 29 C++
C4Tests.exe!Catch::TestCase::invoke() Line 7519 C++
C4Tests.exe!Catch::RunContext::invokeActiveTestCase() Line 6159 C++
C4Tests.exe!Catch::RunContext::runCurrentTest(std::basic_string<char,std::char_traits<char>,std::allocator<char> > & redirectedCout, std::basic_string<char,std::char_traits<char>,std::allocator<char> > & redirectedCerr) Line 6131 C++
C4Tests.exe!Catch::RunContext::runTest(const Catch::TestCase & testCase) Line 5951 C++
C4Tests.exe!Catch::runTests(const Catch::Ptr<Catch::Config> & config) Line 6297 C++
C4Tests.exe!Catch::Session::run() Line 6405 C++
C4Tests.exe!Catch::Session::run(int argc, const char * const * const argv) Line 6384 C++
C4Tests.exe!main(int argc, char * * argv) Line 10333 C++
传入FLValue_AsString()的参数值:
foo 0x0486d10e {_byte=0x0486d10e "FRem...} const 羊毛::Value *
跟踪@堆栈帧 2:
LiteCore.dll!FLValue_AsString(const fleece::Value * v) Line 56 C++
C4Tests.exe!PerfTest::insertDocs(const _FLArray * docs) Line 141 C++
C4Tests.exe!`anonymous namespace'::____C_A_T_C_H____T_E_S_T____491::test() Line 501 C++
C4Tests.exe!Catch::NWayMethodTestCase<`anonymous namespace'::____C_A_T_C_H____T_E_S_T____491>::invoke() Line 29 C++
C4Tests.exe!Catch::TestCase::invoke() Line 7519 C++
C4Tests.exe!Catch::RunContext::invokeActiveTestCase() Line 6159 C++
C4Tests.exe!Catch::RunContext::runCurrentTest(std::basic_string<char,std::char_traits<char>,std::allocator<char> > & redirectedCout, std::basic_string<char,std::char_traits<char>,std::allocator<char> > & redirectedCerr) Line 6131 C++
C4Tests.exe!Catch::RunContext::runTest(const Catch::TestCase & testCase) Line 5951 C++
C4Tests.exe!Catch::runTests(const Catch::Ptr<Catch::Config> & config) Line 6297 C++
C4Tests.exe!Catch::Session::run() Line 6405 C++
C4Tests.exe!Catch::Session::run(int argc, const char * const * const argv) Line 6384 C++
C4Tests.exe!main(int argc, char * * argv) Line 10333 C++
C4Tests.exe!invoke_main() Line 64 C++
FLValue_AsString()接收到的参数值:
v 0xedcf6fc2 {_byte=0xedcf6fc2 } 常量羊毛::值 *
编辑 2:另一个奇怪的信息是,在 64 位构建中,调试器将内存显示为 0xcccccccccccccccc,而实际上内存很好并且程序继续没有问题。
编辑 3:编译器选项:
C4Tests.exe:
/GS /TP /analyze- /W3 /Zc:wchar_t /Zi /Gm- /Od /Ob0 /Fd"C4Tests.dir\Debug\vc140.pdb" /Zc:inline /fp:precise /D "WIN32 " /D "_WINDOWS" /D "_DEBUG" /D "DEBUG" /D "C4DB_THREADSAFE" /D "SQLITE_OMIT_LOAD_EXTENSION" /D "C4_TESTS" /D "CMAKE_INTDIR=\"Debug\"" /D "_MBCS" /errorReport:提示 /WX- /Zc:forScope /RTC1 /GR /Gd /Oy- /MDd /Fa"Debug/" /EHsc /nologo /Fo"C4Tests.dir\Debug\" /Fp"C4Tests.dir\Debug\C4Tests. pch"
LiteCore.dll:
/GS /TP /analyze- /W3 /Zc:wchar_t /Zi /Gm- /Od /Ob0 /Fd"LiteCore.dir\Debug\vc140.pdb" /Zc:inline /fp:precise /D"WIN32 " /D "_WINDOWS" /D "_DEBUG" /D "DEBUG" /D "C4DB_THREADSAFE" /D "SQLITE_OMIT_LOAD_EXTENSION" /D "CMAKE_INTDIR=\"Debug\"" /D "LiteCore_EXPORTS" /D "_WINDLL" /D " _MBCS" /errorReport:prompt /WX- /Zc:forScope /RTC1 /GR /Gd /Oy- /MDd /Fa"Debug/" /EHsc /nologo /Fo"LiteCore.dir\Debug\" /Fp"LiteCore.dir \调试\LiteCore.pch"
羊毛静电:
/GS /TP /analyze- /W3 /Zc:wchar_t /Zi /Gm- /Od /Ob0 /Fd"FleeceStatic.dir\Debug\FleeceStatic.pdb" /Zc:inline /fp:precise /D"WIN32 " /D "_WINDOWS" /D "_DEBUG" /D "DEBUG" /D "CMAKE_INTDIR=\"Debug\"" /D "_MBCS" /errorReport:prompt /WX- /Zc:forScope /RTC1 /GR /Gd / Oy- /MDd /Fa"Debug/" /EHsc /nologo /Fo"FleeceStatic.dir\Debug\" /Fp"FleeceStatic.dir\Debug\FleeceStatic.pch"
链接选项:
C4Tests.exe:
/OUT:"D:\Development\couchbase-lite-core\build_cmake\C\tests\Debug\C4Tests.exe" /MANIFEST /NXCOMPAT /PDB:"D:/Development/couchbase-lite-core/build_cmake /C/tests/Debug/C4Tests.pdb”/DYNAMICBASE“kernel32.lib”“user32.lib”“gdi32.lib”“winspool.lib”“shell32.lib”“ole32.lib”“oleaut32.lib”“uuid .lib" "comdlg32.lib" "advapi32.lib" "....\Debug\LiteCore.lib" "....\vendor\SQLiteCpp\sqlite3\Debug\sqlite3.lib" "..\vendor \forestdb\Debug\forestdb.lib" "......\vendor\openssl\libs\windows\x86\libeay32.lib" /IMPLIB:"D:/Development/couchbase-lite-core/build_cmake/C/测试/调试/C4Tests.lib" /DEBUG /MACHINE:X86 /SAFESEH /INCREMENTAL /PGD:"D:\Development\couchbase-lite-core\build_cmake\C\tests\Debug\C4Tests.pgd" /SUBSYSTEM:CONSOLE / MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"C4Tests.dir\Debug\C4Tests.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
LiteCore.dll:
/OUT:"D:\Development\couchbase-lite-core\build_cmake\Debug\LiteCore.dll" /MANIFEST /NXCOMPAT /PDB:"D:/Development/couchbase-lite-core/build_cmake/Debug/LiteCore .pdb” /DYNAMICBASE “kernel32.lib” “user32.lib” “gdi32.lib” “winspool.lib” “shell32.lib” “ole32.lib” “oleaut32.lib” “uuid.lib” “comdlg32.lib” “advapi32.lib” “Debug\LiteCoreStatic.lib” “供应商\fleece\Debug\FleeceStatic.lib” “供应商\sqlite3-unicodesn\Debug\SQLite3_UnicodeSN.lib” “供应商\SQLiteCpp\sqlite3\Debug\sqlite3.lib” “ vendor\forestdb\Debug\forestdb.lib" "Ws2_32.lib" "..\vendor\openssl\libs\windows\x86\libeay32.lib" /IMPLIB:"D:/Development/couchbase-lite-core/build_cmake/ Debug/LiteCore.lib" /DEBUG /DLL /MACHINE:X86 /SAFESEH /INCREMENTAL /PGD:"D:\Development\couchbase-lite-core\build_cmake\Debug\LiteCore.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level ='asInvoker' uiAccess='false'" /ManifestFile:"LiteCore.dir\Debug\LiteCore.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
羊毛静电:
/OUT:"D:\Development\couchbase-lite-core\build_cmake\vendor\fleece\Debug\FleeceStatic.lib" /NOLOGO
编辑 4:让我们进一步深入研究发出的程序集。在通话前开始FLValue_ToString():
mov eax,dword ptr [ebp-1B4h] // move foo's address into eax
push eax
call _FLValue_AsString (0144AC9Eh)
jmp dword ptr [__imp__FLValue_AsString (014BD208h)]
jmp FLValue_AsString (0187DE00h)
push ebp
mov ebp,esp
sub esp,18h
mov eax,0CCCCCCCCh
mov dword ptr [ebp-18h],eax
mov dword ptr [ebp-14h],eax
mov dword ptr [ebp-10h],eax
mov dword ptr [ebp-0Ch],eax // this should be v (the passed value ?)
mov dword ptr [ebp-8],eax
mov dword ptr [ebp-4],eax
cmp dword ptr [v],0 // v == ebp+0Ch
je FLValue_AsString+34h (0187DE34h) // no jump here
lea eax,[ebp-0Ch] // eax is overwritten with a pointer to 0xcccccccc
push eax
mov ecx,dword ptr [v] // at this point v is garbage
call fleece::Value::asString (016E4DE5h)
jmp fleece::Value::asString (01882DB0h)
...
mov dword ptr [this],eax
mov dword ptr [this],ecx // Why the double move here? Both are invalid anyway...
所以我想这看起来毕竟是混乱的调用约定?这似乎是滥用 ecx 而不是 eax?
回复关于 FLSLICE 的问题: 在外部它是这样定义的:
typedef struct {
const void *buf;
size_t size;
} FLSlice;
在静态库的内部,它被定义为具有相同成员变量的结构,并添加了一些用于内部操作的方法,如 here 所示。
【问题讨论】:
-
Itty bitty 截图没有帮助。请创建一个Minimal, Complete, and Verifiable example。我希望您代表的人现在知道这一点....
-
您是在移动设备上查看此内容吗?截图远非一点点(如果你点击它们,它们是 1080p)。不过我会处理第二部分。
-
对不起,背后有一个邪恶的工作代理。无法点击它们。
-
所以无法搜索/索引屏幕截图。请使用文字。
-
您可以复制/粘贴源代码和堆栈跟踪。这比将图像剪辑到相关部分要快。您使用什么编译器选项?
标签: c++ windows pointers visual-c++ dll