在这种情况下,几张照片值几千字,所以我将首先描述我观察的内容。这是我的第一个展览,我将其称为"堆叠框架1":
这里我传入一个类型为FLValue
的参数(它是指向定义所调用函数的DLL内部的C ++类型的指针的typedef)到一个名为{{1}的函数}。此时参数的值为0x0486d10e。
现在请注意我的第二个展览,我将打电话给#34;堆叠第2帧":
此堆栈帧位于堆栈帧1的正上方,是输入函数FLValue_AsString
的结果。据我所知,调试器立即执行并且没有采用任何内联的路由(这是在所有禁用的优化运行的情况下运行的)。但是,奇怪的是,请注意FLValue_AsString
(上面的v
)的值现在是0xedcf6fc2。这显然会导致内存访问无效导致崩溃。刚刚发生了什么事?
其他一些信息:
FLValue
__cdecl
块extern "C"
的DLL)不直接编译文件,而是一种将三个静态库和另外两个dll链接在一起的集合体,并定义了暴露符号的定义文件和所以函数实际上是在其中一个静态库里面定义的。 编辑屏幕截图中的文字形式的一些信息:
Trace @ stack frame 1(从main()开始):
FLValue_AsString
传递给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++
的参数值:
foo 0x0486d10e {_byte = 0x0486d10e&#34; FRem ...} const fleece :: Value *
Trace @ stack frame 2:
FLValue_AsString()
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++
收到的参数值:
v 0xedcf6fc2 {_byte = 0xedcf6fc2} const fleece :: Value *
编辑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:exact / D&#34; WIN32&#34; / D&#34; _WINDOWS&#34; / D&#34; _DEBUG&#34; / D&#34; DEBUG&#34; / D&#34; C4DB_THREADSAFE&#34; / D&#34; SQLITE_OMIT_LOAD_EXTENSION&#34; / D&#34; C4_TESTS&#34; / D&#34; CMAKE_INTDIR = \&#34; Debug \&#34;&#34; / D&#34; _MBCS&#34; / errorReport:prompt / WX- / Zc:forScope / RTC1 / GR / Gd / Oy- / MDd / Fa&#34; Debug /&#34; / EHsc / nologo /Fo&#34; C4Tests.dir\Debug;&#34; /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:exact / D&#34; WIN32&#34; / D&#34; _WINDOWS&#34; / D&#34; _DEBUG&#34; / D&#34; DEBUG&#34; / D&#34; C4DB_THREADSAFE&#34; / D&#34; SQLITE_OMIT_LOAD_EXTENSION&#34; / D&#34; CMAKE_INTDIR = \&#34; Debug \&#34;&#34; / D&#34; LiteCore_EXPORTS&#34; / D&#34; _WINDLL&#34; / D&#34; _MBCS&#34; / errorReport:prompt / WX- / Zc:forScope / RTC1 / GR / Gd / Oy- / MDd / Fa&#34; Debug /&#34; / EHsc / nologo /Fo&#34; LiteCore.dir\Debug;&#34; /Fp"LiteCore.dir\Debug\LiteCore.pch"
FleeceStatic:
/ GS / TP / analyze- / W3 / Zc:wchar_t / Zi / Gm- / Od / Ob0 /Fd"FleeceStatic.dir\Debug\FleeceStatic.pdb" / Zc:inline / fp:exact / D&#34; WIN32&#34; / D&#34; _WINDOWS&#34; / D&#34; _DEBUG&#34; / D&#34; DEBUG&#34; / D&#34; CMAKE_INTDIR = \&#34; Debug \&#34;&#34; / D&#34; _MBCS&#34; / errorReport:prompt / WX- / Zc:forScope / RTC1 / GR / Gd / Oy- / MDd / Fa&#34; Debug /&#34; / EHsc / nologo /Fo&#34; .FleeceStatic.dir\Debug;&#34; /Fp"FleeceStatic.dir\Debug\FleeceStatic.pch"
链接选项:
C4Tests.exe:
/ OUT:&#34; d:\开发\ couchbase-精简版芯\ build_cmake \ C \测试\调试\ C4Tests.exe&#34; / MANIFEST / NXCOMPAT /PDB:" D:/Development/couchbase-lite-core/build_cmake/C/tests/Debug/C4Tests.pdb" / DYNAMICBASE&#34; kernel32.lib&#34; &#34; USER32.LIB&#34; &#34; GDI32.LIB&#34; &#34; winspool.lib&#34; &#34; SHELL32.LIB&#34; &#34; ole32.lib&#34; &#34; oleaut32.lib&#34; &#34; UUID.LIB&#34; &#34; comdlg32.lib&#34; &#34; advapi32.lib&#34; &#34; .... \调试\ LiteCore.lib&#34; &#34; .... \厂商\ SQLiteCpp \ sqlite3的\调试\ sqlite3.lib&#34; &#34; .... \厂商\ forestdb \调试\ forestdb.lib&#34; &#34; ...... \厂商\ OpenSSL的\库\ Windows \ 86 \ libeay32.lib&#34; /IMPLIB:"D:/Development/couchbase-lite-core/build_cmake/C/tests/Debug/C4Tests.lib" / DEBUG / MACHINE:X86 / SAFESEH / INCREMENTAL /PGD:" D:\Development\couchbase-lite-core\build_cmake\C\tests\Debug\C4Tests.pgd" / SUBSYSTEM:CONSOLE / MANIFESTUAC:&#34; level =&#39; asInvoker&#39; uiAccess =&#39;假&#39;&#34; /ManifestFile:"C4Tests.dir\Debug\C4Tests.exe.intermediate.manifest" / ERRORREPORT:PROMPT / NOLOGO / TLBID:1
LiteCore.dll:
/ OUT:&#34; d:\开发\ couchbase-精简版芯\ build_cmake \调试\ LiteCore.dll&#34; / MANIFEST / NXCOMPAT /PDB:" D:/Development/couchbase-lite-core/build_cmake/Debug/LiteCore.pdb" / DYNAMICBASE&#34; kernel32.lib&#34; &#34; USER32.LIB&#34; &#34; GDI32.LIB&#34; &#34; winspool.lib&#34; &#34; SHELL32.LIB&#34; &#34; ole32.lib&#34; &#34; oleaut32.lib&#34; &#34; UUID.LIB&#34; &#34; comdlg32.lib&#34; &#34; advapi32.lib&#34; &#34;调试\ LiteCoreStatic.lib&#34; &#34;供应商\羊毛\调试\ FleeceStatic.lib&#34; &#34;供应商\ sqlite3的-unicodesn \调试\ SQLite3_UnicodeSN.lib&#34; &#34;供应商\ SQLiteCpp \ sqlite3的\调试\ sqlite3.lib&#34; &#34;供应商\ forestdb \调试\ forestdb.lib&#34; &#34; WS2_32.LIB&#34; &#34; .. \厂商\ OpenSSL的\库\ Windows \ 86 \ libeay32.lib&#34; /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:&#34; level =&#39; asInvoker&#39; uiAccess =&#39;假&#39;&#34; /ManifestFile:"LiteCore.dir\Debug\LiteCore.dll.intermediate.manifest" / ERRORREPORT:PROMPT / NOLOGO / TLBID:1
FleeceStatic:
/ OUT:&#34; d:\开发\ couchbase-精简版芯\ build_cmake \厂商\羊毛\调试\ FleeceStatic.lib&#34; / NOLOGO
编辑4 :让我们进一步深入到发射的组件中。在致电FLValue_AsString()
:
FLValue_ToString()
所以我猜这看起来好像搞砸了约会呢?这似乎是滥用ecx而不是eax?
回复关于FLSLICE的问题: 在外部,它的定义如下:
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...
在静态库内部,它是一个具有相同成员变量的结构的typedef,并添加了一些用于内部操作的方法,如here所示。
答案 0 :(得分:2)
我没有关于Visual C ++调用约定的专家,但我强烈怀疑两个模块之间FLSlice
声明的差异导致调用者期望返回值通过寄存器传递并且被调用者期望它在内存中传递。这导致对哪个参数代表有问题的指针存在分歧。
来自维基百科上的X86 calling conventions:
cdecl的解释有一些变化,特别是如何返回值。 [...]某些编译器在寄存器对EAX:EDX中返回长度为2个寄存器或更少的简单数据结构,以及需要异常处理程序进行特殊处理的较大结构和类对象(例如,定义的构造函数,析构函数或赋值)在内存中返回。要在内存中传递&#34;&#34;,调用者分配内存并将指针作为隐藏的第一个参数传递给它;被调用者填充内存并返回指针,返回时弹出隐藏的指针。
特别注意构造函数的存在会影响调用约定。