Warum erhalte ich mit libexpect.so einen Segmentierungsfehler in meinem einfachen C ++ - Programm?

Ich bin mit einem Projekt beschäftigt, bei dem ich einige Prozesse in bash oder ssh automatisieren muss, also habe ich mich für die libexpect.so-Bibliothek entschieden. Wenn Sie nicht wissen, was libexpect ist, bietet es eine Expect-Erweiterung, die ich in einem C ++ - Programm verwenden kann, und Expect ist nur ein Programm, mit dem Sie automatisierte Skripts für Dinge wie ssh ausführen können. So kann ich ein Skript ausführen, das irgendwo versucht, ssh auszuführen ... Wenn die Passwortabfrage gefunden wird, hätte ich erwarten können, dass bereits ein Passwort gesendet wird.

Mein Problem ist, dass ich beim Ausführen eines Programms, auch eines sehr einfachen, einen Segmentierungsfehler erhalte, den ich mit gdb auf eine Funktion in libexpect.so eingegrenzt habe, die exp_spawnv heißt.

Ich weiß, dass ich die Bibliothek richtig verlinkt habe. Sie wird einwandfrei kompiliert und infact. Das ganze Problem besteht nicht, wenn ich in Ubuntu kompiliere und laufe, aber in meiner Arch Linux-Installation erhalte ich den Segmentierungsfehler, auf den ich später näher eingehen werde. Der Grund, warum ich es auf Arch aufbaue, ist, dass ich das Projekt schließlich auf den meisten Distributionen aufbaubar machen möchte.

Ich bin der Meinung, dass es in meiner Arch-Installation Berechtigungen gibt, die fehlschlagen, wenn die Funktion exp_spawnv aufgerufen wird, vielleicht eine Pipe, eine Gabel oder was auch immer.

Hier ist eine einfache main.cpp zur Veranschaulichung, um zu beweisen, dass ich nichts Ungewöhnliches mache.

#include <tcl8.5/expect.h>

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

Es handelt sich also um das einfachste Erwartungsprogramm, das jemals erstellt wurde. Hier kompiliere und verlinke ich es.

$ g ++ -ggdb -c main.cpp

main.cpp: In der Funktion 'int main ()':

main.cpp: 5: 32: Warnung: Veraltete Konvertierung von String-Konstante zu "char *" [-Wwrite-strings]

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

Also habe ich mein ausführbares Hauptprogramm ... nur ausgeführt, was zu einem Segmentierungsfehler und sonst nichts führt.

Wenn ich mainprog in gdb ausführe, gibt es einen Seg-Fehler in exp_spawnv. Hier ist, was ich in gdb mit dem Backtrace am Ende gemacht habe.

(gdb) ausführen

Startprogramm: / home / user / testlibexpect / mainprog

Warnung: Shared Library-Symbole für linux-vdso.so.1 konnten nicht geladen werden.

Benötigen Sie "set solib-search-path" oder "set sysroot"?

Programm empfangenes Signal SIGSEGV, Segmentierungsfehler.

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

(gdb) backtrace

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

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

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

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

Zwei Dinge betreffen mich.

Wenn ich mir die Manpage für libexpect ansehe, weiß ich, dass exp_spawnv einen neuen Prozess auslöst und ich in der Lage bin, über die DATEI * zu kommunizieren. Ich vermute also, dass SIGSEGV-Signal empfangen wird, weil mit der Gabel etwas Schlimmes passiert ist?

Diese Zeile (Warnung: Shared Library-Symbole für linux-vdso.so.1 konnten nicht geladen werden) im Backtrace sieht faul aus?

Zusammenfassend ist meine Frage, worauf ich achten sollte, um dieses Problem zu beheben? Ich habe versucht, die Expect-Bibliothek aus dem Quellcode zu erstellen und sie mit dem Arch Package Manager Pacman zu erwerben. Das Problem besteht also weiterhin. Ich glaube nicht, dass die Bibliothekserstellung beschädigt ist, wenn Sie wissen, was ich meine.

BEARBEITEN: Punkt 2 meiner Bedenken ist nach meinen Recherchen kein Problem, sondern nur ein kosmetisches.

Die Demontage von Eclipse ist unten:

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

DIE ANTWORT, MIT DER ICH AUFKAM

Hier ist die Lösung, die ich schließlich gefunden habe. Ich habe die Antwort von szx akzeptiert, weil sie mich auf diesen Pfad geführt hat, der trivial war, als ich wusste, wonach ich suchte.

//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;
}

Alles, was ich hier gemacht habe, ist gezeigt, wie man expect / tcl initialisiert, um das Problem zu vermeiden, von dem ich in szx gesprochen habe. Dann habe ich gerade ein typisches Expect Like-Problem gemacht, bei dem ich ziemlich genau gesagt habe, wenn die Shell Sie zur Eingabe auffordert, senden Sie es pwd. Dann, wenn es Ihnen das aktuelle Verzeichnis gibt, erwartet, funktioniert. Diese Art von Struktur kann für so etwas wie ssh äußerst nützlich sein. Sagen Sie, wenn Sie irgendwo sshing automatisieren möchten, etwas tun und dann rauskommen möchten. Vor allem, wenn Sie es ein paar hundert Mal tun möchten und nicht jedes Mal die Echtheit jedes Hosts bestätigen und ein Kennwort eingeben möchten.

Beachten Sie, dass ich das nie machen mussteUbuntu Aus irgendeinem Grund ... möglicherweise, weil ich es nicht aus dem Quellcode erstellt und nur verwendet habeapt-get. Mein Projekt erfordert jedoch, dass ich von der Quelle aus baue, sodass ich einen wirklich guten, ordentlichen Weg gefunden habe, es weiter zu machenhttp://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.html undhttp://www.linuxfromscratch.org/lfs/view/development/chapter05/expect.html... tatsächlich sieht die ganze Website sehr nützlich aus.

Nochmals vielen Dank an szx

Antworten auf die Frage(2)

Ihre Antwort auf die Frage