Почему я получаю ошибку сегментации в моей простой программе на c ++, использующей libexpect.so?

Я занят проектом, в котором мне нужно автоматизировать некоторые процессы в bash или ssh, поэтому я решил использовать библиотеку libexpect.so. Если вы не знаете, что такое libexpect, он предоставляет ожидаемое расширение, которое я могу использовать в программе на c ++, и ожидаем, что это просто программа, в которой вы можете запускать автоматизированные сценарии для таких вещей, как ssh. Таким образом, я могу выполнить скрипт, который пытается выполнить ssh где-нибудь … когда запрос пароля найден ожидаемым, я, возможно, уже дал ожидаемый пароль для отправки.

Моя проблема в том, что когда я запускаю программу, даже очень простую, я получаю ошибку сегментации, которую я сузил, с помощью gdb, до функции в libexpect.so, которая называется exp_spawnv.

Я знаю, что правильно связал библиотеку, она прекрасно компилируется, и на самом деле проблемы не существует, когда я компилирую и запускаю в Ubuntu, но в моей установке arch linux я получаю ошибку сегментации, о которой я расскажу позже. Причина, по которой я строю его на базе arch, заключается в том, что я хочу в конечном итоге сделать проект пригодным для сборки на большинстве дистрибутивов.

Я думаю, что в моей установке arch есть разрешения, которые не выполняются при вызове функции exp_spawnv, возможно, pipe, fork или что-то еще.

Чтобы доказать, что я не делаю что-то напуганное, вот простой main.cpp для иллюстрации.

#include <tcl8.5/expect.h>

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

Так что это почти самая простая ожидаемая программа из когда-либо созданных. Вот я его компилирую и связываю.

$ g ++ -ggdb -c main.cpp

main.cpp: в функции int main ():

main.cpp: 5: 32: предупреждение: не рекомендуется преобразовывать строковую константу в ‘char *’ [-Wwrite-strings]

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

Итак, я получил свой исполняемый файл mainprog … просто запущенный, который даст мне ошибку сегментации и больше ничего.

Если я запускаю mainprog в gdb, он говорит мне, что в exp_spawnv есть ошибка seg. Вот что я сделал в GDB с обратным следом в конце.

(GDB) запустить

Запуск программы: / home / пользователь / testlibexpect / mainprog

предупреждение: не удалось загрузить символы общей библиотеки для linux-vdso.so.1.

Вам нужно «установить solib-search-path» или «установить sysroot»?

Программа получила сигнал SIGSEGV, Ошибка сегментации.

0x00007ffff7bc8836 в exp_spawnv () из /usr/lib/libexpect.so

(GDB) Backtrace

0 0x00007ffff7bc8836 в exp_spawnv () из /usr/lib/libexpect.so

1 0x00007ffff7bc8cb4 в exp_spawnl () из /usr/lib/libexpect.so

2 0x00007ffff7bc8d01 в exp_popen () из /usr/lib/libexpect.so

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

Две вещи касаются меня.

  1. Глядя на справочную страницу для libexpect, я знаю, что exp_spawnv разветвляет новый процесс, и я смогу общаться через FILE *. Итак, я думаю, что сигнал SIGSEGV получен, потому что с вилкой случилось что-то плохое?

  2. Эта строка (предупреждение: не удалось загрузить символы общей библиотеки для linux-vdso.so.1.) В обратном следе выглядит подозрительно?

Итак, в заключение, мой вопрос, что я должен изучить, чтобы решить эту проблему? Я попытался собрать библиотеку ожидаемых данных из исходного кода и получить ее с помощью менеджера пакетов пакета pacman … проблема сохраняется, поэтому я не думаю, что сборка библиотеки повреждена, если вы понимаете, о чем я.

РЕДАКТИРОВАТЬ: пункт 2 моих проблем не проблема в соответствии с исследованиями, которые я сделал, просто косметические.

Разборка от затмения ниже:

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, потому что он привел меня на этот путь, который был тривиален, когда я знал, что искал.

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

Все, что я здесь сделал, — это то, как инициализировать ожидаемый / tcl, чтобы предотвратить проблему, о которой говорил szx. Затем я просто сделал типичную ожидаемую проблему, где я в значительной степени сказал, что если оболочка запрашивает у вас ввод, отправьте ее pwd. Затем, если он дает вам текущий каталог, ожидаемо работает. Такая структура может быть чрезвычайно полезна для чего-то вроде ssh. Скажем, если вы хотите автоматизировать sshing где-нибудь, что-то делать, а затем выбраться оттуда. Особенно, если вы хотите сделать это пару сотен раз и не хотите подтверждать подлинность каждого хоста и каждый раз вводить пароль.

Обратите внимание, что мне никогда не приходилось делать это на убунту по какой-то причине … возможно, потому что я не собирал его из исходного кода и просто использовал apt-get, Тем не менее, мой проект требует от меня сборки из исходного кода, поэтому я нашел действительно хороший, аккуратный способ сделать это на http://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.html а также http://www.linuxfromscratch.org/lfs/view/development/chapter05/expect.html… Фактически весь сайт выглядит действительно полезным.

Еще раз спасибо szx

8

Решение

Глобальная переменная TclStubs *tclStubsPtr определенный внутри Tcl случается NULL когда exp_spawnv пытается получить доступTcl_ErrnoMsg который определяется как член этой структуры (см. tcl.h):

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

Я не знаком ни с ожидаемым, ни с Tcl, но приведенное выше предполагает, что вам, вероятно, следует вызвать некоторую подпрограмму инициализации (если она есть) или установить ее вручную.

4

Другие решения

Я был бы наиболее обеспокоен предупреждением при компиляции. Интерфейс явно требует, чтобы вы передавали записываемую строку, но вы передаете строковую константу. Если он действительно записывает в него, это приведет к ошибке сегментации. Так что, похоже, хороший кандидат на вашу проблему.

Что произойдет, если вместо этого вы попытаетесь создать доступный для записи буфер и передать его:

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

Обновление: я протестировал вашу программу (с указанным выше изменением и «return 0;» в конце), и он отлично работает для меня. Возможно, что-то не так с вашей системой, например, наполовину установленная библиотека? Вы можете проверить, не происходит ли это также, когда вы связываетесь с -static. Если вы это сделаете, вы уверены, что связанная библиотека времени компиляции такая же, как и библиотека времени выполнения (потому что она будет включена в исполняемый файл во время компиляции).

1

По вопросам рекламы [email protected]