为什么我在使用libexpect.so的简单c ++程序中出现分段错误? [英] Why do I get a segmentation fault in my simple c++ program using libexpect.so?

查看:104
本文介绍了为什么我在使用libexpect.so的简单c ++程序中出现分段错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正忙于一个项目,在该项目中我必须自动化bash或ssh中的某些进程,因此我决定使用libexpect.so库.如果您不知道libexpect是什么,它将提供一个我可以在c ++程序中使用的Expect扩展,而Expect只是一个可以在其中运行诸如ssh之类的自动化脚本的程序.因此,我可以执行一个尝试在某个地方ssh的脚本...当按期望找到密码提示时,我本可以已经给了期望发送的密码.

I am busy with a project where I have to automate some processes in bash or ssh so I decided to use the libexpect.so library. If you don't know what libexpect is, it provides an expect extension that I can use in a c++ program, and expect is just a program where you can run automated scripts for things like ssh. So I can execute a script which attempts to ssh somewhere...when the password prompt is found by expect I could have already given expect a password to send.

我的问题是,当我运行一个程序(甚至是一个非常简单的程序)时,我遇到了段错误,我使用gdb将其缩小为libexpect.so中称为exp_spawnv的函数.

My problem is that when I run a program, even a really simple one, I get a segmentation fault which I narrowed down, with gdb, to a function in libexpect.so called exp_spawnv.

我知道我已经正确链接了该库,它可以正常编译,而实际上在ubuntu中编译和运行时不存在整个问题,但是在我的arch linux安装程序中,我遇到了分段错误,稍后将详细介绍.之所以在arch上构建它,是因为我最终希望使该项目在大多数发行版中都可以构建.

I know I've linked the library right, it compiles fine and infact the whole problem doesn't exist when I compile and run in ubuntu, but in my arch linux install I get the segmentation fault which I'll detail later. The reason why I'm building it on arch is because I want to eventually make the project buildable on most distros.

我的想法是,在我的arch安装中,权限会在调用exp_spawnv函数时失败,可能是管道,派生之类的东西.

My thoughts are that in my arch installation there are permissions which fail when the exp_spawnv function is called, perhaps a pipe, fork or whatever.

为了证明我没有做些时髦的事情,这里有一个简单的main.cpp,用于说明目的.

To prove that I'm not doing something funky, here is a simple main.cpp for illustration purposes.

#include <tcl8.5/expect.h>

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

因此,这几乎是有史以来最简单的期望程序.这是我进行编译和链接的地方.

So it is just about the most simple expect program ever made. Here is me compiling and linking it.

$ g ++ -ggdb -c main.cpp

$ g++ -ggdb -c main.cpp

main.cpp:在函数"int main()"中:

main.cpp: In function ‘int main()’:

main.cpp:5:32:警告:不建议将字符串常量转换为"char *" [-Wwrite-strings]

main.cpp:5:32: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]

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

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

所以我得到了我的可执行文件mainprog ...只要运行就会给我一个分段错误,而没有其他任何事情.

So I got my executable mainprog...just running that will give me a segmentation fault and nothing else.

如果我在gdb中运行mainprog,它会告诉我exp_spawnv中存在段错误.这是我在gdb中做的,最后回溯了.

If I run mainprog in gdb it tells me that there is a seg fault in exp_spawnv. Here is what I did in gdb with the backtrace at the end.

(gdb)运行

(gdb) run

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

Starting program: /home/user/testlibexpect/mainprog

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

warning: Could not load shared library symbols for linux-vdso.so.1.

您是否需要设置solib-search-path"或设置sysroot"?

Do you need "set solib-search-path" or "set sysroot"?

程序收到信号SIGSEGV,分段错误.

Program received signal SIGSEGV, Segmentation fault.

0x00007ffff7bc8836

0x00007ffff7bc8836 in exp_spawnv () from /usr/lib/libexpect.so

(gdb)回溯

/usr/lib/libexpect.so中的exp_spawnv()中的0 0x00007ffff7bc8836

0 0x00007ffff7bc8836 in exp_spawnv () from /usr/lib/libexpect.so

/usr/lib/libexpect.so中的exp_spawnl()中的1 0x00007ffff7bc8cb4

1 0x00007ffff7bc8cb4 in exp_spawnl () from /usr/lib/libexpect.so

/usr/lib/libexpect.so中的exp_popen()中的2 0x00007ffff7bc8d01

2 0x00007ffff7bc8d01 in exp_popen () from /usr/lib/libexpect.so

main()中main.cpp:5的3 0x000000000040069e

3 0x000000000040069e in main () at main.cpp:5

有两件事与我有关.

  1. 查看libexpect的联机帮助页,我知道exp_spawnv派生了一个新进程,并且我将能够通过FILE *进行通信.所以我想收到SIGSEGV信号是因为叉子发生了不好的事情?

  1. looking at the manpage for libexpect, I know exp_spawnv forks a new process and I'll be able to communicate through the FILE*. So I guess that SIGSEGV signal is received because something bad has happened with the fork?

回溯中的这一行(警告:无法加载linux-vdso.so.1.的共享库符号)看起来很腥?

That line (warning: Could not load shared library symbols for linux-vdso.so.1.) in the backtrace looks fishy?

因此,总而言之,我的问题是我应该寻找什么来解决此问题?我尝试过从源代码构建期望库,并通过arch软件包管理器pacman来获取它.问题仍然存在,所以如果您了解我的意思,我认为库的构建不会损坏.

So in summary, my question is what should I look into to fix this problem? I have tried building the expect library from source and by acquiring it with the arch package manager pacman...the problem persists so I don't think the library build is corrupt if you know what I mean.

根据我所做的研究,我所关注的第二点不是问题,而仅仅是表面上的.

point 2 of my concerns is not a problem according to research I've done, just cosmetic.

从eclipse的反汇编如下:

The disassembly from eclipse is below:

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 <execvp@plt>
00007ffff7bc87e2:   callq 0x7ffff7bb1720 <__errno_location@plt>
00007ffff7bc87e7:   mov 0x24(%rsp),%edi
00007ffff7bc87eb:   mov %rax,%rsi
00007ffff7bc87ee:   mov $0x4,%edx
00007ffff7bc87f3:   xor %eax,%eax
00007ffff7bc87f5:   callq 0x7ffff7bb1910 <write@plt>
00007ffff7bc87fa:   mov $0xffffffff,%edi
00007ffff7bc87ff:   callq 0x7ffff7bb23d0 <exit@plt>
00007ffff7bc8804:   nopl 0x0(%rax)
00007ffff7bc8808:   xor %eax,%eax
00007ffff7bc880a:   movl $0x0,0x20dd3c(%rip)        # 0x7ffff7dd6550
00007ffff7bc8814:   callq 0x7ffff7bb1700 <exp_init_pty@plt>
00007ffff7bc8819:   xor %eax,%eax
00007ffff7bc881b:   callq 0x7ffff7bb2460 <exp_init_tty@plt>
00007ffff7bc8820:   lea -0x1c97(%rip),%rdi        # 0x7ffff7bc6b90
00007ffff7bc8827:   callq 0x7ffff7bb2540 <expDiagLogPtrSet@plt>
00007ffff7bc882c:   mov 0x20c555(%rip),%rax        # 0x7ffff7dd4d88
00007ffff7bc8833:   mov (%rax),%rax
00007ffff7bc8836:   mov 0x410(%rax),%rdi

我参与的答案

这是我最终想出的解决方案,我接受了szx的回答,因为它使我走上了这条路,一旦我知道了我要寻找的东西,这就变得微不足道了.

Here is the solution I eventually came up with, I have accepted szx's answer because it lead me down this path which was trivial once I knew what I was looking for.

//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提示您输入信息,请发送pwd.然后,如果它给您当前目录,则期望它正在工作.这种结构对于诸如ssh之类的东西可能非常有用.假设您要在某处自动执行sshing,执行某项操作然后离开该位置.尤其是如果您想做几百次,并且不想确认每个主机的真实性,并且每次都键入密码.

All I've done here is shown how to initialize expect/tcl to prevent the problem I had that szx was talking about. Then I just did a typical expect like problem where I pretty much said if the shell prompts you for input send it pwd. Then, if it gives you the current directory expect is working. This kind of structure can be extremely useful for something like ssh. Say if you want to automate sshing somewhere, doing something and then getting out of there. Especially if you want to do it a couple hundred times and you don't want to confirm the authenticity of every host and type a password in every time.

请注意,出于某些原因,我不必在 ubuntu 上执行此操作……可能是因为我不是从源代码构建它,而只是使用了apt-get.但是,我的项目需要我从源代码构建,因此我在 http://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.html

Note that I never had to do this on ubuntu for some reason...possibly because I did not build it from source and just used apt-get. However, my project requires me to build from source so I found a really good, neat way to do it on http://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.html and http://www.linuxfromscratch.org/lfs/view/development/chapter05/expect.html... infact that whole website looks really useful.

再次感谢szx

推荐答案

exp_spawnv尝试访问Tcl_ErrnoMsg时,在Tcl中定义的全局变量TclStubs *tclStubsPtr恰好是NULL.结构(请参见tcl.h):

The global variable TclStubs *tclStubsPtr defined inside Tcl happens to be NULL when exp_spawnv tries to accessTcl_ErrnoMsg which is defined as a member of that structure (see tcl.h):

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

我对Expect和Tcl都不熟悉,但是上面的建议建议您应该调用一些初始化子例程(如果存在)或手动进行设置.

I'm not familiar with neither expect nor Tcl but the above suggests that you probably should call some initialization subroutine (if there exists one) or set it manually.

这篇关于为什么我在使用libexpect.so的简单c ++程序中出现分段错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆