2012-12-04 25 views
8

我忙于一个项目,我必须在bash或ssh中自动化一些进程,所以我决定使用libexpect.so库。如果你不知道libexpect是什么,它提供了一个我可以在C++程序中使用的期望扩展,期望只是一个可以为ssh之类的东西运行自动化脚本的程序。所以我可以执行一个脚本,试图在某处ssh ...当密码提示被发现,期望我可能已经给予期待密码发送。为什么我在使用libexpect.so的简单C++程序中出现分段错误?

我的问题是,当我运行一个程序,甚至是一个非常简单的程序时,我得到一个分段错误,我用gdb缩小了libexpect.so中一个名为exp_spawnv的函数。

我知道我已经把库链接起来了,它编译得很好,事实上,当我在ubuntu中编译和运行时,并不存在整个问题,但是在我的arch linux安装中,我得到了细节错误,后来。我之所以在拱门上架设它,是因为我希望最终可以在大多数发行版上构建项目。

我的想法是,在我的拱安装有权限,当exp_spawnv函数被调用,可能是一个管道,叉子或任何其他权限。

为了证明我没有做一些简单的事情,下面是一个简单的main.cpp用于说明目的。

#include <tcl8.5/expect.h> 

int main() 
{ 
    FILE* file = exp_popen("bash"); 
} 

所以它只是有史以来最简单的期望计划。这里是我编译和链接它。

$克++ -ggdb -c main.cpp中

main.cpp中:在函数 '诠释主()':

main.cpp中:5:32:警告:弃用转换从字符串常量 '字符*'[-Wwrite串]

$克++ main.o -lexpect -o mainprog

所以我得到我的可执行mainprog ...只是运行,这将给我一个分段错误,没有别的。

如果我在gdb中运行mainprog,它会告诉我exp_spawnv中有seg故障。这是我在最后的backtrace中在gdb中做的。

(GDB)运行

启动程序:/ home/user中/ testlibexpect/mainprog

警告:无法加载Linux的vdso.so.1共享库的符号。

你需要“设置solib-search-path”或“set sysroot”吗?

程序接收到的信号SIGSEGV,分段错误。

0x00007ffff7bc8836 in exp_spawnv()from/usr/lib/libexpect。所以

(GDB)从/usr/lib/libexpect.so

1 0x00007ffff7bc8cb4回溯

0 exp_spawnv 0x00007ffff7bc8836()在exp_spawnl()从/ usr/LIB/libexpect。所以

2 0x00007ffff7bc8d01在exp_popen()从/usr/lib/libexpect.so

3在主在main.cpp中0x000000000040069e():5

两件事关注我。

  1. 看着libexpect的手册页,我知道exp_spawnv分叉一个新的进程,我可以通过FILE *进行通信。所以我猜SIGSEGV信号是因为叉子发生故障而收到的?

  2. 该行(警告:无法为linux-vdso.so.1加载共享库符号)在回溯中看起来很腥吗?

因此,总之,我的问题是我应该看看解决这个问题?我曾尝试从源代码构建期望的库,并通过arch包管理器pacman获取它...问题依然存在,因此如果知道我的意思,我认为库构建不会损坏。

编辑:根据我所做的研究,我的担忧点2不是问题,只是美容。

Eclipse中的拆卸低于:

00007ffff7bc87c6: mov 0x20c68b(%rip),%rax  # 0x7ffff7dd4e58 
00007ffff7bc87cd: mov (%rax),%rax 
00007ffff7bc87d0: test %rax,%rax 
00007ffff7bc87d3: je 0x7ffff7bc87d7 <exp_spawnv+935> 
00007ffff7bc87d5: callq *%rax 
00007ffff7bc87d7: mov %r12,%rsi 
00007ffff7bc87da: mov %rbp,%rdi 
00007ffff7bc87dd: callq 0x7ffff7bb2330 <[email protected]> 
00007ffff7bc87e2: callq 0x7ffff7bb1720 <[email protected]> 
00007ffff7bc87e7: mov 0x24(%rsp),%edi 
00007ffff7bc87eb: mov %rax,%rsi 
00007ffff7bc87ee: mov $0x4,%edx 
00007ffff7bc87f3: xor %eax,%eax 
00007ffff7bc87f5: callq 0x7ffff7bb1910 <[email protected]> 
00007ffff7bc87fa: mov $0xffffffff,%edi 
00007ffff7bc87ff: callq 0x7ffff7bb23d0 <[email protected]> 
00007ffff7bc8804: nopl 0x0(%rax) 
00007ffff7bc8808: xor %eax,%eax 
00007ffff7bc880a: movl $0x0,0x20dd3c(%rip)  # 0x7ffff7dd6550 
00007ffff7bc8814: callq 0x7ffff7bb1700 <[email protected]> 
00007ffff7bc8819: xor %eax,%eax 
00007ffff7bc881b: callq 0x7ffff7bb2460 <[email protected]> 
00007ffff7bc8820: lea -0x1c97(%rip),%rdi  # 0x7ffff7bc6b90 
00007ffff7bc8827: callq 0x7ffff7bb2540 <[email protected]> 
00007ffff7bc882c: mov 0x20c555(%rip),%rax  # 0x7ffff7dd4d88 
00007ffff7bc8833: mov (%rax),%rax 
00007ffff7bc8836: mov 0x410(%rax),%rdi 

答案,我想出了

下面是我最终想出了解决方案,我已经接受了SZX的答案,因为它导致我失望一旦我知道我在找什么,这条路很平凡。

//do not use TCL stubs as this is a main 
#undef USE_TCL_STUBS 


#include <iostream> 
using std::cout; 
using std::endl; 

//headers that must be included when using expectTcl as an extension to c++ program 
#include <stdio.h> 
#include <stdlib.h> 
#include <expectTcl/tcl.h> 
#include <expectTcl/expect_tcl.h> 
#include <expectTcl/expect.h> 

//enums representing cases of what expect found in loop 
enum{FOUNDSEARCH, PROMPT}; 

int main() 
{ 
    /* initialise expect and tcl */ 
    Tcl_Interp *interp = Tcl_CreateInterp(); 

    if(Tcl_Init(interp) == TCL_ERROR) 
    { 
     cout << "TCL failed to initialize." << endl; 
    } 
    if(Expect_Init(interp) == TCL_ERROR) 
    { 
     cout << "Expect failed to initialize." << endl; 
    } 

    /* end of intialisation procedure */ 

    //open a shell with a pipe 
    char shellType[] = "sh"; 
    FILE* fp = exp_popen(shellType); 

    //should we exit from the loop which is studying sh output 
    bool shouldBreak = false; 
    //did we find the pwd 
    bool foundSearch = false; 
    //does it look like expect is working 
    bool expectWorking = false; 
    //did we receive a prompt...therefore we should send a command 
    bool receivedPrompt = false; 

    while(shouldBreak == false) 
    { 
     switch(exp_fexpectl(fp, 
       exp_glob, "/tools/test*", FOUNDSEARCH, //different 
       exp_glob,"# ", PROMPT, //cases are shown here 
       exp_end)) //that the expect loop could encounter 
    { 
    case FOUNDSEARCH: 
     foundSearch = true; 
     break; 
    case PROMPT: 
     if (receivedPrompt) 
     { 
      shouldBreak = true; 
      expectWorking = true; 
     } 
     else 
     { 
      receivedPrompt = true; 
      fprintf(fp, "%s\r", "pwd"); 
     } 
     break; 
    case EXP_TIMEOUT: 
     shouldBreak = true; 
     break; 
    case EXP_EOF: 
     shouldBreak = true; 
     break; 
    } 

     //cout << "exp_match : " << exp_match << endl; 
    } 

    cout << endl; 
    if (foundSearch) 
    { 
     cout << "Expect found output of pwd" << endl; 
    } 
    else 
    { 
     cout << "Expect failed to find output of pwd" << endl; 
    } 
    if(expectWorking) 
    { 
     cout << "The expect interface is working" << endl; 
    } 
    else 
    { 
     cout << "The expect interface is not working" << endl; 
    } 


    cout << "The test program successfully reached the end" << endl; 
} 

我在这里所做的所有显示如何初始化expect/tcl来防止我曾经说过的szx问题。然后,我只是做了一个典型的期望问题,我几乎说,如果shell提示你输入发送密码。然后,如果它给你当前目录期望正在工作。这种结构对于类似ssh的应用程序非常有用。假如你想在某处自动化sshing,做一些事情然后离开。特别是如果你想做几次,你不想确认每个主机的真实性,并在每次输入密码。

注意,我从来没有出于某种原因做这个Ubuntu的 ...可能是因为我没有从源代码编译它,只是使用apt-get。但是,我的项目需要我从源代码构建,所以我发现了一个非常好的,整洁的方式来做到这一点http://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.htmlhttp://www.linuxfromscratch.org/lfs/view/development/chapter05/expect.html ...事实上,整个网站看起来非常有用。再次

由于SZX

+0

反汇编说什么? – szx

+0

我把它编辑成我的问题 – benzeno

+0

但是里面没有'0x00007ffff7bc8836',地址是否改变? – szx

回答

4

内的Tcl定义的全局变量TclStubs *tclStubsPtr恰好是NULLexp_spawnv试图访问Tcl_ErrnoMsg其被定义为结构的构件(参照tcl.h):

#ifndef Tcl_ErrnoMsg 
#define Tcl_ErrnoMsg \ 
    (tclStubsPtr->tcl_ErrnoMsg) /* 128 */ 
#endif 

我不熟悉既不希望也不是Tcl,但上面的提示你可能应该调用一些初始化子程序(如果存在的话)或手动设置。

+0

啊,是的,这可能是一个问题......我没有在** tcl.h **中找到它,但它是在** tclDecls.h **中。 到目前为止,我在源目录中使用'grep -r“tclStubsPtr”*'来查找指针的任何提及或初始化,并且我在** tclStubLib.c中找到它**此外,'grep'发现* * libtcl8.5.so **库。你会注意到我没有与该图书馆链接。 所以我的下一步是链接它,看看是否修复了任何问题......如果不是,我会调查是否需要打电话给tcl。 – benzeno

+0

好吧,事实证明你给了我一个辉煌的领导......为什么'tclStubsPtr'指针保持为空可能有很多原因。但是无论出于什么原因,最大的问题是expect/tcl环境没有正确初始化。在做了一些挖掘之后,我终于找到了关于'man libexpect'的答案(我从哪里开始)。**作者Don Libes **在源代码发行版中提供的源代码中包含'exp_main_exp.c'作为原型主体。它涉及到包括tcl头和expect_tcl联合头。 – benzeno

+0

然后我强制expect/tcl全局实体初始化。不幸的是,这有点破解...我不应该初始化tcl或甚至包含tcl头文件。无论如何,我接受你的答案,因为我从来没有想过要查找为什么tcl没有正确初始化。对于任何有兴趣的人来说,我已经编辑了一个样例主要代码,它将这个功能应用于我的问题。 – benzeno

1

我最关心的编译时警告。这个接口显然要求你传递一个可写的字符串,但是你传递一个字符串常量。如果确实写入,则会导致分段错误。所以它看起来很适合你的问题。

,如果你不是试图创建一个读写缓存和传递会发生什么:

char name[] = "bash"; 
FILE* file = exp_popen(name); 

更新:我已经测试你的程序(与上面的变化,以及“返回0;”结尾),它对我来说工作得很好。也许你的系统有问题,比如半装的图书馆?当你与-static链接时,你可以检查它是否也失败。如果你这样做,你可以肯定编译时链接库和运行时使用的库是一样的(因为它将在编译时包含在可执行文件中)。

+0

感谢您的快速响应Bas, 这会处理警告,但同样的问题仍然存在。 – benzeno

+0

对于简单的响应,这是我的第一个关于堆栈溢出的问题,我没有意识到在评论中输入命令发送它。 对于我糟糕的编程实践(我的疏忽),我表示歉意,您确实正确地将此引起我的注意。但是,它并没有解决主要问题,但可以在以后为我节省一些麻烦。 – benzeno

+0

太糟糕了,它不能解决它。至于这里的评论,你可以随时删除或编辑它。 –

相关问题