none
請問c++中傳入參數與進入到函式內容不同? RRS feed

  • 問題

  • 請教一個在執行期間遇到的問題 : 

    當程式進入到TLM2CSCBridge的建構函式,執行到create_method_process這個函式呼叫並進入此函式中,會發現opt_p的參數與傳入的值不同。在此說明一下sc_core為另一個靜態函式庫的命名空間。以下小弟僅列出大略的代碼,以簡化問題描述。

    TLM2CSCBridge::TLM2CSCBridge(sc_core::sc_module_name name):
    sc_core::sc_module(name),
    libraryName("library", "no")
    {
    sc_core::SC_ENTRY_FUNC sc_e_func = NULL;
    const sc_core::sc_spawn_options op;
    const sc_core::sc_spawn_options* opt_p = (const sc_core::sc_spawn_options*)(void *)(0xdeadbeef);
    ::sc_core::sc_process_handle notification_handle =
    sc_core::sc_get_curr_simcontext()->create_method_process("notification", false, sc_e_func, this, opt_p);
    ...
    }

    調用的靜態函式庫代碼 : 

    namespace sc_core {
    extern sc_simcontext* sc_curr_simcontext;
    extern sc_simcontext* sc_default_global_context;
    inline sc_simcontext*
    sc_get_curr_simcontext()
    {
    if( sc_curr_simcontext == 0 ) {
    sc_default_global_context = new sc_simcontext;
    sc_curr_simcontext = sc_default_global_context;
    }
    return sc_curr_simcontext;
    }
    .....
    }
    typedef void (sc_process_host::*SC_ENTRY_FUNC)();
    sc_process_handle
    sc_simcontext::create_method_process(
    const char* name_p, bool free_host, SC_ENTRY_FUNC method_p,
    sc_process_host* host_p, const sc_spawn_options* opt_p )
    {
    const sc_spawn_options* dd = opt_p;
    sc_method_handle handle =
    new sc_method_process(name_p, free_host, method_p, host_p, opt_p);
    .....
    }

    在進入create_method_process的呼叫後,此時查看堆疊(esp)所指位置的情況,原本的5個參數放在0x0027F028 ~ 0x0027F018的堆疊位置,與呼叫sc_get_curr_simcontext之前一樣。

    進入create_method_process後堆疊的記憶體內容如下 : 

    MEM ADDR    (old ebp) (return addr)
    0x0027F00C  0027f1c8   007c4ab3    0027f128   00c43a60
    0x0027F01C  00000000  00000000    013f9c0c   deadbeef 
    0x0027F02C  f56b675d   0027f3dc     0027f408   0113e000

    0x0027F03C  013f9c0c    cccccccc      cccccccc     cccccccc

    在呼叫 create_method_process進入過程中堆疊多push了兩筆資料,分別為return address及ebp的值。(此時esp為0x0027F00C)

    以下為create_method_process經過反組譯,於00002290指令位置將ebp放入堆疊後,於00002291指令位置將目前esp的值(即為上圖綠色箭頭0x0027F00C)給ebp,而此函式中在指令000022DD的位置試圖從堆疊中取出opt_p,發現ebp+28h是抓在0x0027F034的位置也就是值為0x0027f408而非預期0x0027f028位置的deadbeef。

    想請教大家有沒有遇過類似的問題? 代碼能編譯過也能link,但卻在執行期間出現的錯誤,是否有可能是編譯參數設置問題? 請論壇上的高手不吝指教。 感謝!

    create_method_process@sc_simcontext@sc_core@@QAE?AVsc_process_handle@2@PBD_NP8sc_process_host@2@AEXXZPAV42@PBVsc_spawn_options@2@@Z (public: class sc_core::sc_process_handle __thiscall sc_core::sc_simcontext::create_method_process(char const *,bool,void (__thiscall sc_core::sc_process_host::*)(void),class sc_core::sc_process_host *,class sc_core::sc_spawn_options const *)):
    00002290: 55 push ebp
    00002291: 8B EC mov ebp,esp
    00002293: 6A FF push 0FFFFFFFFh
    00002295: 68 00 00 00 00 push offset __ehhandler$?create_method_process@sc_simcontext@sc_core@@QAE?AVsc_process_handle@2@PBD_NP8sc_process_host@2@AEXXZPAV42@PBVsc_spawn_options@2@@Z
    0000229A: 64 A1 00 00 00 00 mov eax,dword ptr fs:[00000000h]
    000022A0: 50 push eax
    000022A1: 81 EC FC 00 00 00 sub esp,0FCh
    000022A7: 57 push edi
    000022A8: 51 push ecx
    000022A9: 8D BD F8 FE FF FF lea edi,[ebp-108h]
    000022AF: B9 3F 00 00 00 mov ecx,3Fh
    000022B4: B8 CC CC CC CC mov eax,0CCCCCCCCh
    000022B9: F3 AB rep stos dword ptr es:[edi]
    000022BB: 59 pop ecx
    000022BC: A1 00 00 00 00 mov eax,dword ptr [___security_cookie]
    000022C1: 33 C5 xor eax,ebp
    000022C3: 89 45 F0 mov dword ptr [ebp-10h],eax
    000022C6: 50 push eax
    000022C7: 8D 45 F4 lea eax,[ebp-0Ch]
    000022CA: 64 A3 00 00 00 00 mov dword ptr fs:[00000000h],eax
    000022D0: 89 4D EC mov dword ptr [ebp-14h],ecx
    000022D3: C7 85 04 FF FF FF mov dword ptr [ebp-0FCh],0
    00 00 00 00
    000022DD: 8B 45 28 mov eax,dword ptr [ebp+28h]
    000022E0: 89 45 E8 mov dword ptr [ebp-18h],eax
    000022E3: 68 10 01 00 00 push 110h
    ........
    2019年11月30日 下午 01:43

所有回覆

  • 不明白你的意思。你是说执行完“mov eax,dword ptr [ebp+28h]”这句后,eax的值是0027f408?

    2019年12月1日 上午 09:36
  • 是的,沒錯。

    [ebp + 28h]的值為0x0027F034也就是內存中的值為0x0027f408。而在上一層呼叫順序是先呼叫sc_get_curr_simcontext()接著呼叫create_method_process()。於呼叫sc_get_curr_simcontext()前先編譯器編譯出的指令會將opt_p及它左邊的五個參數與notification_handle的位址push進堆疊(stack),位置從0x0027F028 ~ 0x0027F014。(請參照上篇貼出來的堆疊內容)

    “mov eax,dword ptr [ebp+28h]”這個指令在執行期間反組譯顯示為"mov eax, dword ptr[opt_p]",也就是讀取最右邊的opt_p參數,create_method_process()的反組譯碼一開始先把ebp的值push進堆疊,esp值變成0x0027F00C(因為進入create_method_process()前先存了一個返回位址),下一行"mov ebp,esp"便把ebp設為0x0027F00C,因此執行到"mov eax, dword ptr[opt_p]"變成執行0x0027F00C+28H = 0x0027F034把這位址存放的0x0027f408放到eax(請參照上篇貼出來的堆疊內容),而不是預期在0x0027F028位置的0xdeadbeef,也就是opt_p的值。

    由於這過程都是編譯器編譯出來的結果,小弟實在很納悶到底是哪邊出錯? 堆疊的內容並沒有被非法寫入,而是進到另外一個靜態鏈結的函式庫中呼叫,按照其編譯出來的代碼執行透過ebp抓取堆疊中存放的上一層參數,就會抓取錯誤。

    另外也同樣在思考,是否式調用端(.exe)與被調用端(.lib)對於函式呼叫時對堆疊操作方式的認知不同所導致? 但因為小弟對於這方面VC上的經驗太少,是否有高手能提出些追查方向? 


    • 已編輯 Titipo 2019年12月1日 上午 11:02
    2019年12月1日 上午 10:58
  • create_method_process里面全是省略号,这个你让别人怎么和汇编对照着看啊?在create_method_process里断点看到的opt_p值是0xdeadbeef么?为何你不在源码中断点查看而要跑去看汇编码呢?你给的代码因为只有一部分所以我无法重现你的问题,能不能弄截屏啊?你说运行时出现错误,是什么错误呢?你描述的内容很多,但关键信息都没提到啊。如果你认为create_method_process有问题,那你得把这个函数完整贴上来。另外你应该断点然后逐句执行,看里面的参数何时改变的,数值从多少变成了多少。
    2019年12月1日 上午 11:36
  • Hi, 我貼上create_method_process, 我所以貼上匯編碼是因為在源碼設中斷點沒法理解才回去看匯編碼, 因為我是把中斷點就設在" const sc_spawn_options'*dd = opt_p;"這行,也就是create_method_process的剛一入口,就使用觀看區域變數的視窗去觀察就發現內容不對。
    sc_process_handle
    sc_simcontext::create_method_process(
    const char* name_p, bool free_host, SC_ENTRY_FUNC method_p,
    sc_process_host* host_p, const sc_spawn_options* opt_p )
    {
    const sc_spawn_options* dd = opt_p;
    sc_method_handle handle =
    new sc_method_process(name_p, free_host, method_p, host_p, opt_p);
    if ( m_ready_to_simulate ) { // dynamic process
    if ( !handle->dont_initialize() )
    {
    #ifdef SC_HAS_PHASE_CALLBACKS_
    if( SC_UNLIKELY_( m_simulation_status
    & (SC_END_OF_UPDATE|SC_BEFORE_TIMESTEP) ) )
    {
    std::stringstream msg;
    msg << m_simulation_status
    << ":\n\t immediate method spawning of "
    "`" << handle->name() << "' ignored";
    SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_FORBIDDEN_
    , msg.str().c_str() );
    }
    else
    #endif // SC_HAS_PHASE_CALLBACKS_
    {
    push_runnable_method( handle );
    }
    }
    else if ( handle->m_static_events.size() == 0 )
    {
    SC_REPORT_WARNING( SC_ID_DISABLE_WILL_ORPHAN_PROCESS_,
    handle->name() );
    }
    } else {
    m_process_table->push_front( handle );
    }
    return sc_process_handle(handle);
    }

    執行的截屏由於我剛註冊,貼圖後提交網站一直回應我尚未通過認證,因此無法截屏上來。

    不過源碼設斷點我是這樣觀察的 : 

    1. 在create_method_process上一層的調用處用監看視窗觀察opt_p,其值為opt_p = 0xdeadbeef

    2. 在進入create_method_process後在" const sc_spawn_options'*dd = opt_p;"這行用監看視窗看opt_p = 0x0027f408 (實際上每次執行都會不同值)

    3. 原先的代碼opt_p是NULL的,我為了觀察才把opt_p附值0xdeadbeef;實際上運行是opt_p在非NULL的情況下,在下一行"new sc_method_process"中對於這個參數發生判斷錯誤。

    請教 : 若這樣描述無法給您清楚了解,是否能提供我的專案讓您參考?
    • 已編輯 Titipo 2019年12月1日 下午 05:01
    2019年12月1日 下午 04:56
  • 1. 请在这里回帖验证账户:

    https://social.technet.microsoft.com/Forums/en-US/dc4002e4-e3de-4b1e-9a97-3702387886cc/verify-account-42?forum=reportabug

    2. sc_method_process的构造函数声明是什么?是否会改变传入的参数?

    3. 你是在断点时通过反汇编窗口得到的汇编码吗?你是怎么得到汇编码的?

    4. 你这是什么项目?MFC还是dll,还是其它什么?用的是什么版本的VS?

    5. 从现有的代码我看不出有什么问题。如果你能提供demo,可以上传到任意网盘,然后把链接以文本的方式贴在这。只提供能呈现问题的部分即可。或者你可以改成这样“->create_method_process(nullptr, false, nullptr, nullptr, (const sc_core::sc_spawn_options*)0xdeadbeef)”试一试,看看在create_method_process中第一行断点时的那些参数值对不对。

    2019年12月2日 上午 03:58
  • 我再问一下,你有没有改动过代码?如果有,请先执行一下“重新生成解决方案”(可能你那边有不同叫法),然后再看看是否还有这个问题。

    2019年12月2日 上午 05:13
  • 2. sc_method_process的构造函数声明是什么?是否会改变传入的参数?

    Ans : 以下是宣告,傳入的參數與宣告是一致的。

        sc_process_handle __stdcall create_method_process(
        const char* name_p, bool free_host, SC_ENTRY_FUNC method_p, 
        sc_process_host* host_p, const sc_spawn_options* opt_p );

    3. 你是在断点时通过反汇编窗口得到的汇编码吗?你是怎么得到汇编码的?

    Ans : 是的,匯編碼是設在下面這行源代碼處,此時如您照片上描述開啟反匯編,再以F11 step by step偵錯。

             另外一方面我是用dumpbin直接把create_method_process的.lib反匯編兩相比較。

    ::sc_core::sc_process_handle notification_handle =

    sc_core::sc_get_curr_simcontext()->create_method_process("notification", false, sc_e_func, this, opt_p);

    4. 你这是什么项目?MFC还是dll,还是其它什么?用的是什么版本的VS?

    Ans : 我是一個console的執行檔(.exe)link靜態函式庫(.lib)。使用的是Virtual Studio Community 2019

    5. 从现有的代码我看不出有什么问题。如果你能提供demo,可以上传到任意网盘,然后把链接以文本的方式贴在这。只提供能呈现问题的部分即可。或者你可以改成这样“->create_method_process(nullptr, false, nullptr, nullptr, (const sc_core::sc_spawn_options*)0xdeadbeef)”试一试,看看在create_method_process中第一行断点时的那些参数值对不对。

    Ans : 提供在百度網盤可以? 目前我是使用win32的組態(.lib及.exe都為win32),但後來試了一下,改成x64的組態就正常了,但仍不了解是甚麼原因造成的。實際上在編譯及鏈結過程都是成功的。待我上傳後發布出來也許您能提供更進一步的原因。

    6. 你有没有改动过代码?如果有,请先执行一下“重新生成解决方案”(可能你那边有不同叫法),然后再看看是否还有这个问题。

    Ans : 這部份我能確定修改地都有編譯了,首次編譯成功後執行就出現這問題。

     


    2019年12月4日 下午 05:08
  • 我没有百度账号,无法使用百度网盘,google、onedrive、github等等这些都行。

    create_method_process是你自己写的吗?该不会是在单独的lib中已经编译好的吧。这个lib是不是按64位编译的?所以你用x86时就出错了,不过按说x86调用x64不可能通过编译。如果是64位的话,+28h是在调host_p,而不是opt_p。你贴出来的汇编码应该是dumpbin生成的吧,最好贴vs生成的那个,因为直接反编译lib出来的是已经优化过的。

    如果create_method_process是你自己写的,那就得看过demo才能知道是怎么回事了。

    • 已編輯 [-] 2019年12月5日 上午 03:36
    2019年12月5日 上午 03:27
  • demo的link :

    https://drive.google.com/file/d/1wqP78lHqNA43lhm1mQ4TxfqtQ2bnQ8nj/view?usp=sharing

    除了這個問題,還有另一個dynamic_cast結果為0的情況,我一併都放在helloworld的應用裡面跟您請教。

    在源碼裡,我用_WIN64分隔了這兩個問題,problem 1為傳入參數不符,problem 2為針對sc_interface*做down casting到GenericRouter_if*結果為0,實際上GenericRouter_if虛擬繼承sc_interface的。

    我分別對於problem 1及problem 2於問題點添加assert,分別在 : 

    genericRouter.h : LINE 175
    sc_simcontext.cpp : LINE 1160

    針對這兩個問題,只要在上頭的組態選擇x86(problem 1)或x64(problem 2),再對helloworld專案重新建置即可。

    Thanks

    MSVC 環境

    

    2019年12月5日 下午 06:07
  • 1.3G?你是不是把临时文件也打包进去了?打包之前请先删除解决方案中的.vs文件夹和每个专案中的Debug与X64文件夹,这些文件夹中存放的都是无用的临时文件,请只打包源码。当前这个文件太大,我这网络不稳定,下载到一半的时候就出错了。
    2019年12月6日 上午 03:48
  • 重新上傳 : 

    https://drive.google.com/file/d/1kbdY0g6meKMDDaGQGG0qZCWau5hxsZry/view?usp=sharing

    解開後VS Solution的位置 : 

    demo\demo_1\nvdlav1_cmod\nvdlav1_cmod\msvc2019\nvdlav1_cmod\nvdlav1_cmod.sln

    2019年12月6日 上午 05:12
  • 代码很多啊,需要时间来查看。

    第2个问题很简答,实际上编译的时候vs直接告诉你原因了:

    genericRouter.h(174,1): warning C4436: 构造函数或析构函数中从虚拟基“sc_core::sc_interface”到“gs::gp::GenericRouterRaw<32,TRAITS,DEFAULT_PROTOCOL,255,RESP_TYPE,tlm::TLM_ADDRESS_ERROR_RESPONSE,void tlm::tlm_generic_payload::set_response_status(const tlm::tlm_response_status),gs::gp::SimpleAddressMap<TRAITS,255>>”的 dynamic_cast 在遇到部分构造的对象时可能失败

    第1个问题,原因很简单,因为你那个函数指针是成员函数指针。这个东西问题很大。看这个就知道了:https://stackoverflow.com/questions/16062651/about-sizeof-of-a-class-member-function-pointer

    https://devblogs.microsoft.com/oldnewthing/?p=40713

    https://devblogs.microsoft.com/oldnewthing/20040210-00/?p=40683

    在helloworld中sizeof(sc_core::SC_ENTRY_FUNC)是4,在create_method_process中是16,所以就错位了。x64没有问题是因为x64的传参和x86是完全不同的:

    https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2019

    https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019

    https://www.cnblogs.com/Toring/p/6650043.html

    不过我直接建立新的控制台c++和一个lib静态库项目后发现,在lib中的成员函数指针也是4,也就是说按理应该是一样的,要么都是16要么都是4,但不知道为什么你这个create_method_process和helloworld不匹配。你这个项目代码太多需要时间仔细分析,你也可以自己沿着此线索去找找原因,看看是不是什么地方设置的不对了。

    2019年12月6日 上午 11:45
  • 关于dynamic_cast补充一句,那个warning C4436的意思就是这种转换在构造函数中可能失败,因为此时构造没有全部完成。等构造完成再调用就行了,比如把构造函数中的那些代码放到一个void initialize() {......}里面去,构造完成之后立即调用这个initialize。
    2019年12月6日 上午 11:49
  • 编辑helloworld项目的属性,在【附加选项】后面添加/vmg,重新编译即可。

    原因:

    https://zh.wikipedia.org/zh-tw/%E7%B1%BB%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88

    前面的回复中有一些链接,那里面已经提到了,成员函数指针是极为特殊的一种指针,不同的编译器或不同的设置可以有不同的实现,这个c++标准不做统一规定。因此成员函数指针在实现时可能不是实现为指针,而是实现为一个数据结构。当你指定了/vmg时这个结构的大小是指针大小的4倍。因此你的项目必须统一,要么都使用/vmg,要么都不使用,绝对不能混用,否则函数栈的计算就不正常了。至于x64为什么没问题,前面的回复已经讲过,就不再重复了。

    2019年12月6日 下午 04:06
  • 感謝~

    Problem 2解決了~ 按照您的方式在類別建構以後,將會有down casting行為的代碼放到initialize(),再接著調用initialize()就能成功轉型。

    Problem 1我則是試著加/vmg,但無法成功。不過奇怪的一點是,我在命令行也看不到這個參數被加入,即便在helloworld、SystemC、SystemC-core三個專案裡面都移除/vmg的參數,也無法正確傳入參數。感覺上您那邊是可以的,會是VS版本的差異..?

    2019年12月9日 上午 03:31
  • 和VS版本应该是没有关系的,我的是VS2019。我用的是你提供的那个demo_1.rar,解压缩之后只在helloworld的属性配置里面加上/vmg就行了,其它什么也没做。请再仔细检查一下,是不是哪里没设置对,或者使用你这个demo_1.rar试一试而不是你的完整项目。另外如果你想要全部移除/vmg,那你的那个SystemC-core里面还有一个SystemC.props文件最好也改掉。另外所有改动之后不要忘记全部重新编译。

    2019年12月9日 上午 05:06
  • 感謝~ 

    真的可以了~ 後來發現的確是SystemC.props這個檔案裏面的

    <AdditionalOptions>/vmg %(AdditionalOptions)</AdditionalOptions>

    造成不一致,確定專案內的附加參數沒有設置/vmg,再拿掉/vmg後再移除上述的/vmg就能正常收到參數!

    2019年12月12日 上午 02:06