《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
定位一个map相关的coredump来熟悉一下:
Core was generated by `./xuzhina_dump_c07_s3_ex 5 / 6'. Program terminated with signal 11, Segmentation fault. #0 0x00000000 in ?? () Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6_6.4.i686 libgcc-4.4.7-11.el6.i686 libstdc++-4.4.7-11.el6.i686 (gdb) bt #0 0x00000000 in ?? () #1 0x08048bd0 in main () (gdb) i r eax 0x5 5 ecx 0x0 0 edx 0x0 0 ebx 0x6 6 esp 0xbfd3de7c 0xbfd3de7c ebp 0xbfd3dee8 0xbfd3dee8 esi 0x0 0 edi 0x0 0 eip 0x0 0 eflags 0x210296 [ PF AF SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
由于栈顶的指令地址为0x0,而eip的值也是0x0,可以知道是调用了函数指针,且函数指针的值为空.而这个函数指针是由main函数调用
看一下main函数的汇编:
(gdb) disassemble Dump of assembler code for function main: 0x0804898f <+0>: push %ebp 0x08048990 <+1>: mov %esp,%ebp 0x08048992 <+3>: and $0xfffffff0,%esp 0x08048995 <+6>: push %esi 0x08048996 <+7>: push %ebx 0x08048997 <+8>: sub $0x58,%esp 0x0804899a <+11>: cmpl $0x3,0x8(%ebp) 0x0804899e <+15>: jg 0x80489b6 <main+39> 0x080489a0 <+17>: movl $0x8049ce4,(%esp) 0x080489a7 <+24>: call 0x804883c <puts@plt> 0x080489ac <+29>: mov $0xffffffff,%ebx 0x080489b1 <+34>: jmp 0x8048c42 <main+691> 0x080489b6 <+39>: lea 0x18(%esp),%eax 0x080489ba <+43>: mov %eax,(%esp) 0x080489bd <+46>: call 0x8048c6e <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEC2Ev> 0x080489c2 <+51>: lea 0x37(%esp),%eax 0x080489c6 <+55>: mov %eax,(%esp) 0x080489c9 <+58>: call 0x804887c <_ZNSaIcEC1Ev@plt> 0x080489ce <+63>: lea 0x37(%esp),%eax 0x080489d2 <+67>: mov %eax,0x8(%esp) 0x080489d6 <+71>: movl $0x8049cfa,0x4(%esp) ---Type <return> to continue, or q <return> to quit--- 0x080489de <+79>: lea 0x30(%esp),%eax 0x080489e2 <+83>: mov %eax,(%esp) 0x080489e5 <+86>: call 0x80487ec <_ZNSsC1EPKcRKSaIcE@plt> 0x080489ea <+91>: lea 0x30(%esp),%eax 0x080489ee <+95>: mov %eax,0x4(%esp) 0x080489f2 <+99>: lea 0x18(%esp),%eax 0x080489f6 <+103>: mov %eax,(%esp) 0x080489f9 <+106>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x080489fe <+111>: movl $0x8048964,(%eax) 0x08048a04 <+117>: lea 0x30(%esp),%eax 0x08048a08 <+121>: mov %eax,(%esp) 0x08048a0b <+124>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048a10 <+129>: jmp 0x8048a41 <main+178> 0x08048a12 <+131>: mov %edx,%ebx 0x08048a14 <+133>: mov %eax,%esi 0x08048a16 <+135>: lea 0x30(%esp),%eax 0x08048a1a <+139>: mov %eax,(%esp) 0x08048a1d <+142>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048a22 <+147>: mov %esi,%eax 0x08048a24 <+149>: mov %ebx,%edx 0x08048a26 <+151>: jmp 0x8048a28 <main+153> 0x08048a28 <+153>: mov %edx,%ebx ---Type <return> to continue, or q <return> to quit--- 0x08048a2a <+155>: mov %eax,%esi 0x08048a2c <+157>: lea 0x37(%esp),%eax 0x08048a30 <+161>: mov %eax,(%esp) 0x08048a33 <+164>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048a38 <+169>: mov %esi,%eax 0x08048a3a <+171>: mov %ebx,%edx 0x08048a3c <+173>: jmp 0x8048c26 <main+663> 0x08048a41 <+178>: lea 0x37(%esp),%eax 0x08048a45 <+182>: mov %eax,(%esp) 0x08048a48 <+185>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048a4d <+190>: lea 0x3f(%esp),%eax 0x08048a51 <+194>: mov %eax,(%esp) 0x08048a54 <+197>: call 0x804887c <_ZNSaIcEC1Ev@plt> 0x08048a59 <+202>: lea 0x3f(%esp),%eax 0x08048a5d <+206>: mov %eax,0x8(%esp) 0x08048a61 <+210>: movl $0x8049cfc,0x4(%esp) 0x08048a69 <+218>: lea 0x38(%esp),%eax 0x08048a6d <+222>: mov %eax,(%esp) 0x08048a70 <+225>: call 0x80487ec <_ZNSsC1EPKcRKSaIcE@plt> 0x08048a75 <+230>: lea 0x38(%esp),%eax 0x08048a79 <+234>: mov %eax,0x4(%esp) 0x08048a7d <+238>: lea 0x18(%esp),%eax 0x08048a81 <+242>: mov %eax,(%esp) ---Type <return> to continue, or q <return> to quit--- 0x08048a84 <+245>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048a89 <+250>: movl $0x8048972,(%eax) 0x08048a8f <+256>: lea 0x38(%esp),%eax 0x08048a93 <+260>: mov %eax,(%esp) 0x08048a96 <+263>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048a9b <+268>: jmp 0x8048acc <main+317> 0x08048a9d <+270>: mov %edx,%ebx 0x08048a9f <+272>: mov %eax,%esi 0x08048aa1 <+274>: lea 0x38(%esp),%eax 0x08048aa5 <+278>: mov %eax,(%esp) 0x08048aa8 <+281>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048aad <+286>: mov %esi,%eax 0x08048aaf <+288>: mov %ebx,%edx 0x08048ab1 <+290>: jmp 0x8048ab3 <main+292> 0x08048ab3 <+292>: mov %edx,%ebx 0x08048ab5 <+294>: mov %eax,%esi 0x08048ab7 <+296>: lea 0x3f(%esp),%eax 0x08048abb <+300>: mov %eax,(%esp) 0x08048abe <+303>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048ac3 <+308>: mov %esi,%eax 0x08048ac5 <+310>: mov %ebx,%edx 0x08048ac7 <+312>: jmp 0x8048c26 <main+663> ---Type <return> to continue, or q <return> to quit--- 0x08048acc <+317>: lea 0x3f(%esp),%eax 0x08048ad0 <+321>: mov %eax,(%esp) 0x08048ad3 <+324>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048ad8 <+329>: lea 0x47(%esp),%eax 0x08048adc <+333>: mov %eax,(%esp) 0x08048adf <+336>: call 0x804887c <_ZNSaIcEC1Ev@plt> 0x08048ae4 <+341>: lea 0x47(%esp),%eax 0x08048ae8 <+345>: mov %eax,0x8(%esp) 0x08048aec <+349>: movl $0x8049cfe,0x4(%esp) 0x08048af4 <+357>: lea 0x40(%esp),%eax 0x08048af8 <+361>: mov %eax,(%esp) 0x08048afb <+364>: call 0x80487ec <_ZNSsC1EPKcRKSaIcE@plt> 0x08048b00 <+369>: lea 0x40(%esp),%eax 0x08048b04 <+373>: mov %eax,0x4(%esp) 0x08048b08 <+377>: lea 0x18(%esp),%eax 0x08048b0c <+381>: mov %eax,(%esp) 0x08048b0f <+384>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048b14 <+389>: movl $0x8048983,(%eax) 0x08048b1a <+395>: lea 0x40(%esp),%eax 0x08048b1e <+399>: mov %eax,(%esp) 0x08048b21 <+402>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048b26 <+407>: jmp 0x8048b57 <main+456> ---Type <return> to continue, or q <return> to quit--- 0x08048b28 <+409>: mov %edx,%ebx 0x08048b2a <+411>: mov %eax,%esi 0x08048b2c <+413>: lea 0x40(%esp),%eax 0x08048b30 <+417>: mov %eax,(%esp) 0x08048b33 <+420>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048b38 <+425>: mov %esi,%eax 0x08048b3a <+427>: mov %ebx,%edx 0x08048b3c <+429>: jmp 0x8048b3e <main+431> 0x08048b3e <+431>: mov %edx,%ebx 0x08048b40 <+433>: mov %eax,%esi 0x08048b42 <+435>: lea 0x47(%esp),%eax 0x08048b46 <+439>: mov %eax,(%esp) 0x08048b49 <+442>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048b4e <+447>: mov %esi,%eax 0x08048b50 <+449>: mov %ebx,%edx 0x08048b52 <+451>: jmp 0x8048c26 <main+663> 0x08048b57 <+456>: lea 0x47(%esp),%eax 0x08048b5b <+460>: mov %eax,(%esp) 0x08048b5e <+463>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048b63 <+468>: lea 0x4f(%esp),%eax 0x08048b67 <+472>: mov %eax,(%esp) 0x08048b6a <+475>: call 0x804887c <_ZNSaIcEC1Ev@plt> 0x08048b6f <+480>: mov 0xc(%ebp),%eax ---Type <return> to continue, or q <return> to quit--- 0x08048b72 <+483>: add $0x8,%eax 0x08048b75 <+486>: mov (%eax),%eax 0x08048b77 <+488>: lea 0x4f(%esp),%edx 0x08048b7b <+492>: mov %edx,0x8(%esp) 0x08048b7f <+496>: mov %eax,0x4(%esp) 0x08048b83 <+500>: lea 0x48(%esp),%eax 0x08048b87 <+504>: mov %eax,(%esp) 0x08048b8a <+507>: call 0x80487ec <_ZNSsC1EPKcRKSaIcE@plt> 0x08048b8f <+512>: lea 0x48(%esp),%eax 0x08048b93 <+516>: mov %eax,0x4(%esp) 0x08048b97 <+520>: lea 0x18(%esp),%eax 0x08048b9b <+524>: mov %eax,(%esp) 0x08048b9e <+527>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048ba3 <+532>: mov (%eax),%esi 0x08048ba5 <+534>: mov 0xc(%ebp),%eax 0x08048ba8 <+537>: add $0xc,%eax 0x08048bab <+540>: mov (%eax),%eax 0x08048bad <+542>: mov %eax,(%esp) 0x08048bb0 <+545>: call 0x80487fc <atoi@plt> 0x08048bb5 <+550>: mov %eax,%ebx 0x08048bb7 <+552>: mov 0xc(%ebp),%eax 0x08048bba <+555>: add $0x4,%eax ---Type <return> to continue, or q <return> to quit--- 0x08048bbd <+558>: mov (%eax),%eax 0x08048bbf <+560>: mov %eax,(%esp) 0x08048bc2 <+563>: call 0x80487fc <atoi@plt> 0x08048bc7 <+568>: mov %ebx,0x4(%esp) 0x08048bcb <+572>: mov %eax,(%esp) 0x08048bce <+575>: call *%esi => 0x08048bd0 <+577>: mov %eax,%ebx 0x08048bd2 <+579>: lea 0x48(%esp),%eax 0x08048bd6 <+583>: mov %eax,(%esp) 0x08048bd9 <+586>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048bde <+591>: jmp 0x8048c0c <main+637> 0x08048be0 <+593>: mov %edx,%ebx 0x08048be2 <+595>: mov %eax,%esi 0x08048be4 <+597>: lea 0x48(%esp),%eax 0x08048be8 <+601>: mov %eax,(%esp) 0x08048beb <+604>: call 0x80487cc <_ZNSsD1Ev@plt> 0x08048bf0 <+609>: mov %esi,%eax 0x08048bf2 <+611>: mov %ebx,%edx 0x08048bf4 <+613>: jmp 0x8048bf6 <main+615> 0x08048bf6 <+615>: mov %edx,%ebx 0x08048bf8 <+617>: mov %eax,%esi 0x08048bfa <+619>: lea 0x4f(%esp),%eax 0x08048bfe <+623>: mov %eax,(%esp) 0x08048c01 <+626>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048c06 <+631>: mov %esi,%eax 0x08048c08 <+633>: mov %ebx,%edx 0x08048c0a <+635>: jmp 0x8048c26 <main+663> 0x08048c0c <+637>: lea 0x4f(%esp),%eax 0x08048c10 <+641>: mov %eax,(%esp) 0x08048c13 <+644>: call 0x804882c <_ZNSaIcED1Ev@plt> 0x08048c18 <+649>: lea 0x18(%esp),%eax 0x08048c1c <+653>: mov %eax,(%esp) 0x08048c1f <+656>: call 0x8048c5a <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEED2Ev> 0x08048c24 <+661>: jmp 0x8048c42 <main+691> 0x08048c26 <+663>: mov %edx,%ebx 0x08048c28 <+665>: mov %eax,%esi 0x08048c2a <+667>: lea 0x18(%esp),%eax 0x08048c2e <+671>: mov %eax,(%esp) 0x08048c31 <+674>: call 0x8048c5a <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEED2Ev> 0x08048c36 <+679>: mov %esi,%eax 0x08048c38 <+681>: mov %ebx,%edx 0x08048c3a <+683>: mov %eax,(%esp) 0x08048c3d <+686>: call 0x804889c <_Unwind_Resume@plt> 0x08048c42 <+691>: mov %ebx,%eax 0x08048c44 <+693>: add $0x58,%esp 0x08048c47 <+696>: pop %ebx 0x08048c48 <+697>: pop %esi 0x08048c49 <+698>: mov %ebp,%esp 0x08048c4b <+700>: pop %ebp 0x08048c4c <+701>: ret End of assembler dump.
出现coredump可能是因为这一条指令
0x08048bce <+575>: call *%esi
看一下esi的值:
(gdb) i r esi esi 0x0 0
可见esi为0,确实是由于那一条指令引起的.
那么为什么esi的值是从哪里来的?
0x08048b8f <+512>: lea 0x48(%esp),%eax 0x08048b93 <+516>: mov %eax,0x4(%esp) 0x08048b97 <+520>: lea 0x18(%esp),%eax 0x08048b9b <+524>: mov %eax,(%esp) 0x08048b9e <+527>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048ba3 <+532>: mov (%eax),%esi
可见esi是0x08048b9e处所调用的函数,
_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_的返回值
而_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_的实际名称:
[xuzhina@localhost s3_ex]$ c++filt _ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_ std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int (*)(int, int), std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int (*)(int, int)> > >::operator[](std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
由于map的operater[]有一个参数,由上面看,可知map对象的地址是esp+0x18,那个参数是放在esp+0x48,而这个刚好是一个string对象的this指针.见0x08048b8a调用了构造函数
0x08048b6f <+480>: mov 0xc(%ebp),%eax 0x08048b72 <+483>: add $0x8,%eax 0x08048b75 <+486>: mov (%eax),%eax 0x08048b77 <+488>: lea 0x4f(%esp),%edx 0x08048b7b <+492>: mov %edx,0x8(%esp) 0x08048b7f <+496>: mov %eax,0x4(%esp) 0x08048b83 <+500>: lea 0x48(%esp),%eax 0x08048b87 <+504>: mov %eax,(%esp) 0x08048b8a <+507>: call 0x80487ec <_ZNSsC1EPKcRKSaIcE@plt> [xuzhina@localhost s3_ex]$ c++filt _ZNSsC1EPKcRKSaIcE std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
可知,这个string的构造函数只是接收了一个参数,而这个参数是由ebp+c所指向内存地址偏移+8所得来的.
而ebp+0xc是main函数的第二个参数,由main函数的原型
int main( int argc, char* argv[] );
可知
这个参数应该是argv[2],而argv[2]是字符串。它的值是多少呢?
(gdb) x /wx $ebp+0xc 0xbfd3def4: 0xbfd3df94 (gdb) x /8wx 0xbfd3df94 0xbfd3df94: 0xbfd3f543 0xbfd3f55c 0xbfd3f55e 0xbfd3f560 0xbfd3dfa4: 0x00000000 0xbfd3f562 0xbfd3f585 0xbfd3f5a4 (gdb) x /s 0xbfd3f55e 0xbfd3f55e: "/"
再看一下map的内容有哪些。由上面已经知道map对象的地址是esp+0x18。且由于上面参数的类型可以知道,这个map对象的key是string类型,而val是函数指针
(gdb) x /8wx $esp+0x18 0xbfd3de98: 0x00000001 0x00000000 0x089a7020 0x089a7090 0xbfd3dea8: 0x089a70c8 0x00000004 0x089a7014 0x0804bb84 (gdb) x /8wx 0x089a7020 0x89a7020: 0x00000001 0xbfd3de9c 0x089a7090 0x089a7058 0x89a7030: 0x089a7014 0x08048964 0x00000000 0x00000019 (gdb) x /8wx 0x089a7014 0x89a7014: 0x0000002b 0x00000000 0x00000021 0x00000001 0x89a7024: 0xbfd3de9c 0x089a7090 0x089a7058 0x089a7014 (gdb) x /s 0x089a7014 0x89a7014: "+" (gdb) info symbol 0x08048964 add(int, int) in section .text of /home/xuzhina/code/s3_ex/xuzhina_dump_c07_s3_ex (gdb) x /8wx 0x089a7090 0x89a7090: 0x00000001 0x089a7020 0x00000000 0x00000000 0x89a70a0: 0x089a7084 0x08048983 0x00000000 0x00000019 (gdb) x /8wx 0x089a7084 0x89a7084: 0x0000002a 0x00000000 0x00000021 0x00000001 0x89a7094: 0x089a7020 0x00000000 0x00000000 0x089a7084 (gdb) x /s 0x089a7084 0x89a7084: "*" (gdb) info symbol 0x08048983 mul(int, int) in section .text of /home/xuzhina/code/s3_ex/xuzhina_dump_c07_s3_ex (gdb) x /8wx 0x089a7058 0x89a7058: 0x00000001 0x089a7020 0x00000000 0x089a70c8 0x89a7068: 0x089a704c 0x08048972 0x00000000 0x00000019 (gdb) x /8wx 0x089a704c 0x89a704c: 0x0000002d 0x00000000 0x00000021 0x00000001 0x89a705c: 0x089a7020 0x00000000 0x089a70c8 0x089a704c (gdb) x /s 0x089a704c 0x89a704c: "-" (gdb) info symbol 0x08048972 sub(int, int) in section .text of /home/xuzhina/code/s3_ex/xuzhina_dump_c07_s3_ex (gdb) x /8wx 0x089a70c8 0x89a70c8: 0x00000000 0x089a7058 0x00000000 0x00000000 0x89a70d8: 0x089a70bc 0x00000000 0x00000000 0x00020f21 (gdb) x /8wx 0x089a70bc 0x89a70bc: 0x0000002f 0x00000000 0x00000021 0x00000000 0x89a70cc: 0x089a7058 0x00000000 0x00000000 0x089a70bc (gdb) x /s 0x089a70bc 0x89a70bc: "/"
而main函数调用_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_除了coredump的附近之外,还有三处调用了。
0x080489ea <+91>: lea 0x30(%esp),%eax 0x080489ee <+95>: mov %eax,0x4(%esp) 0x080489f2 <+99>: lea 0x18(%esp),%eax 0x080489f6 <+103>: mov %eax,(%esp) 0x080489f9 <+106>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x080489fe <+111>: movl $0x8048964,(%eax) 0x08048a75 <+230>: lea 0x38(%esp),%eax 0x08048a79 <+234>: mov %eax,0x4(%esp) 0x08048a7d <+238>: lea 0x18(%esp),%eax 0x08048a81 <+242>: mov %eax,(%esp) ---Type <return> to continue, or q <return> to quit--- 0x08048a84 <+245>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048a89 <+250>: movl $0x8048972,(%eax) 0x08048b00 <+369>: lea 0x40(%esp),%eax 0x08048b04 <+373>: mov %eax,0x4(%esp) 0x08048b08 <+377>: lea 0x18(%esp),%eax 0x08048b0c <+381>: mov %eax,(%esp) 0x08048b0f <+384>: call 0x8048cfc <_ZNSt3mapISsPFiiiESt4lessISsESaISt4pairIKSsS1_EEEixERS5_> 0x08048b14 <+389>: movl $0x8048983,(%eax)
其中0x8048964,0x8048972,0x8048983正好是那三个函数指针。可见,main函数并没有往map对象里面放入”/”的val。
看一下程序代码,
1 #include <map> 2 #include <string> 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 typedef int (*oper)(int a, int b ); 7 8 int add( int a, int b ) 9 { 10 return a + b; 11 } 12 13 int sub( int a, int b ) 14 { 15 return a - b; 16 } 17 18 int mul( int a, int b ) 19 { 20 return a * b; 21 } 22 23 int main( int argc, char* argv[] ) 24 { 25 if ( argc < 4 ) 26 { 27 printf( "parameter less than 4\n" ); 28 return -1; 29 } 30 31 std::map< std::string, oper> operMap; 32 operMap["+"] = &add; 33 operMap["-"] = ? 34 operMap["*"] = &mul; 35 36 return operMap[argv[2]]( atoi( argv[1] ), atoi( argv[3] ) ); 37 }
可知,确实如上面所分析那样.所以,对于获取map里面的元素,用operator[]要慎重.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。