Segfault происходит из-за одной строки кода в файле C, и вся программа не запускается - PullRequest
5 голосов
/ 19 января 2012

Я создал программу на C для записи в последовательный порт (/ dev / ttyS0) во встроенной системе ARM. Ядром, работающим во встроенной системе ARM, является версия 3.0.4 Linux, построенная с использованием того же кросс-компилятора, что и приведенный ниже.

Мой кросс-компилятор - arm-linux-gcc (Buildroot 2011.08) 4.3.6, работающий на хосте Ubuntu x86_64 (3.0.0-14-generic # 23-Ubuntu SMP). Я использовал утилиту stty для настройки последовательного порта из командной строки.

Таинственным образом кажется, что программа откажется работать во встроенной системе ARM, если присутствует одна строка кода. Если строка будет удалена, программа запустится.

Вот полный список кода, повторяющий проблему:

РЕДАКТИРОВАТЬ: теперь я закрываю файл по ошибке, как это предлагается в комментариях ниже.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <termios.h>

int test();
void run_experiment();

int main()
{
    run_experiment();
return 0;
}

void run_experiment()
{
    printf("Starting program\n");
    test();
} 

int test()
{
    int fd;
    int ret;

    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
    printf("fd = %u\n", fd); 
    if (fd < 0)
    {
        close(fd);
        return 0;
    }

    fcntl(fd, F_SETFL, 0);

    printf("Now writing to serial port\n"); 

    //TODO:
    // segfault occurs due to line of code here
    // removing this line causes the program to run properly
    ret = write( fd, "test\r\n", sizeof("test\r\n") );

    if (ret < 0)
    {
        close(fd);
         return 0;
    }
close(fd);
return 1;
} 

Вывод этой программы в системе ARM следующий:

Segmentation fault

Однако, если я удалю строку, указанную выше, и перекомпилирую программу, проблема исчезнет, ​​и получится следующий вывод:

Starting program
fd = 3
Now writing to serial port

Что может быть не так, и как я могу отладить проблему? Будет ли это проблемой с кодом, с кросс-компиляторным компилятором или с версией ОС?

Я также пробовал различные комбинации O_WRONLY и O_RDWR без O_NOCTTY при открытии файла, но проблема все еще сохраняется.

Как предложено @wildplasser в комментариях ниже, я заменил функцию теста следующим кодом, в значительной степени основанным на коде на другом сайте (http://www.warpspeed.com.au/cgi-bin/inf2html.cmd?..\html\book\Toolkt40\XPG4REF.INF+112).

Однако программа все еще не запускается, и я снова получаю загадочный Segmentation Fault.

Вот код:

int test()
{
   int fh;
   FILE *fp;
   char *cp;

   if (-1 == (fh = open("/dev/ttyS0", O_RDWR))) 
   {
      perror("Unable to open");
      return EXIT_FAILURE;
   }
   if (NULL == (fp = fdopen(fh, "w"))) 
   {
      perror("fdopen failed");
      close(fh);
      return EXIT_FAILURE;
   }

   for (cp = "hello world\r\n"; *cp; cp++) 
   fputc( *cp, fp);

   fclose(fp);
   return 0;
}

Это очень загадочно, поскольку, используя другие программы, которые я написал, я могу использовать функцию write() аналогичным образом для записи в файлы sysfs без каких-либо проблем.

ОДНАКО, если программа точно в той же структуре, то я не могу написать в /dev/null.

НО я могу успешно записать в файл sysfs, используя точно такую ​​же программу!

Если segfault произошел в определенной строке функции, то я бы предположил, что вызов функции будет вызывать segfault. Однако полная программа не запускается!

ОБНОВЛЕНИЕ: Для получения дополнительной информации вот информация о кросс-компиляторе, используемая для сборки в системе ARM:

$ arm-linux-gcc --v Используя встроенные спецификации. Цель: arm-unknown-linux-uclibcgnueabi Настраивается с помощью: /media/RESEARCH/SAS2-version2/device-system/buildroot/buildroot-2011.08/output/toolchain/gcc-4.3.6/configure --prefix = / media / RESEARCH / SAS2-version2 / device-system / buildroot / buildroot-2011.08 / output / host / usr --build = x86_64-unknown-linux-gnu --host = x86_64-unknown-linux-gnu --target = arm-unknown-linux-uclibcgnueabi --enable-languages ​​= c, c ++ --with-sysroot = / media / RESEARCH / SAS2-version2 / система устройств / buildroot / buildroot-2011.08 / output / host / usr / arm-unknown-linux-uclibcgnueabi / sysroot --with-build-time -tools = / media / RESEARCH / SAS2-версия2 / система устройств / buildroot / buildroot-2011.08 / output / host / usr / arm-unknown-linux-uclibcgnueabi / bin --disable -__ cxa_atexit --enable-target-optspace - -disable-libgomp --with-gnu-ld --disable-libssp --disable-multilib --enable-tls --enable-shared --with-gmp = / media / RESEARCH / SAS2-version2 / устройство-система / buildroot / buildroot-2011.08 / output / host / usr --with-mpfr = / media / RESEARCH / SAS2-version2 / система устройств / buildroot / buildroot-2011.08 / output / host / usr --disable-n ls --enable-threads --disable-decimal-float --with-float = soft --with-abi = aapcs-linux --with-arch = armv5te --with-tune = arm926ej-s --disable-largefile --with-pkgversion = 'Buildroot 2011.08' --with-bugurl = http://bugs.buildroot.net/ Модель потока: posix gcc версия 4.3.6 (Buildroot 2011.08)

Вот make-файл, который я использую для компиляции моего кода:

CC=arm-linux-gcc
CFLAGS=-Wall
datacollector: datacollector.o 

clean:
    rm -f datacollector datacollector.o

ОБНОВЛЕНИЕ: Используя предложения по отладке, приведенные в комментариях и ответах ниже, я обнаружил, что ошибка вызвана включением escape-последовательности \r в строку.По какой-то странной причине компилятору не нравится escape-последовательность \r, и она вызовет segfault без запуска кода.

Если escape-последовательность \r будет удалена, то код будет работать так, как ожидается.

Таким образом, ошибочная строка кода должна быть следующей:

ret = write (fd, "test \ n", sizeof ("test \ n"));

Итак, к сведению, полная программа тестирования, которая на самом деле запускается, является следующей (кто-то может прокомментировать?):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <termios.h>

int test();
void run_experiment();

int main()
{
    run_experiment();

return 0;
}

void run_experiment()
{
    printf("Starting program\n");
    fflush(stdout);
    test(); 
}


int test()
{
    int fd;
    int ret;
    char *msg = "test\n";
    // NOTE: This does not work and will cause a segfault!
    // even if the fflush is called after each printf,
    // the program will still refuse to run
    //char *msg = "test\r\n";

    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
    printf("fd = %u\n", fd); 
    fflush(stdout);

    if (fd < 0) 
    {
       close(fd);
       return 0;
    }

    fcntl(fd, F_SETFL, 0);

    printf("Now writing to serial port\n");
    fflush(stdout);

    ret = write( fd, msg, strlen(msg) );

    if (ret < 0)
    {
        close(fd);
        return 0;
    }

    close(fd);

return 1;
} 

РЕДАКТИРОВАТЬ: В дополнение ко всему этому, лучше использовать:

ret = write( fd, msg, sizeof(msg) );

или лучше использовать:

ret = write( fd, msg, strlen(msg) );

Что лучше?Лучше использовать sizeof () или strlen ()?Похоже, что некоторые данные в строке усекаются и не записываются в последовательный порт с использованием функции sizeof ().

Как я понимаю из комментария Павла ниже, лучше использовать strlen() если msg объявлено как char*.

Более того, похоже, что gcc не создает правильный двоичный файл, когда escape-последовательность \r используется для записи в tty.

Что касается последней тестовой программы, приведенной в моем посте выше, следующая строка кода вызывает segfault без запуска программы:

char *msg = "test\r\n";

Как подсказал Игорь в комментариях, я запустил отладчик gdbв двоичном файле с ошибочной строкой кода.Мне пришлось скомпилировать программу с ключом -g. Отладчик gdb изначально запускается в системе ARM, и все двоичные файлы создаются для архитектуры ARM на хосте с использованием одного и того же файла Makefile.Все двоичные файлы создаются с использованием кросс-компилятора arm-linux-gcc.

Вывод gdb (изначально работающий в системе ARM) выглядит следующим образом:

GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-unknown-linux-uclibcgnueabi"...
"/programs/datacollector": not in executable format: File format not recognized
(gdb) run
Starting program:
No executable file specified.
Use the "file" or "exec-file" command.
(gdb) file datacollector
"/programs/datacollector": not in executable format: File format not recognized
(gdb)

Однако, если я изменю одну строку кода на следующую, двоичный файл компилируется и работает правильно.Обратите внимание, что escape-последовательность \r отсутствует:

char *msg = "test\n";

Вот вывод gdb после изменения одной строки кода:

GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-unknown-linux-uclibcgnueabi"...
(gdb) run
Starting program: /programs/datacollector
Starting program
fd = 4
Now writing to serial port
test
Program exited normally.
(gdb)

UPDATE:

Как предложено Заком в ответе ниже, я запустил тестовую программу для встроенной системы Linux.Хотя Зак предоставляет подробный сценарий для запуска во встроенной системе, я не смог запустить сценарий из-за отсутствия инструментов разработки (компилятор и заголовки), установленных в корневой файловой системе.Вместо установки этих инструментов я просто скомпилировал хорошую тестовую программу, которую Зак предоставил в скрипте, и использовал утилиту strace.Утилита strace была запущена во встроенной системе.

Наконец, я думаю, что понимаю, что происходит.

Плохой двоичный файл был передан во встроенную систему через FTP с использованием SPI-мост Ethernet-(KSZ8851SNL).В ядре Linux есть драйвер для KSZ8851SNL.

Похоже, что драйвер ядра Linux, серверное программное обеспечение proftpd, работающее во встроенной системе, или само собственное оборудование (KSZ8851SNL) каким-то образомпортит бинарный файл.Двоичный файл хорошо работает во встроенной системе.

Вот выходные данные strace для двоичного файла testz, переданного во встроенную систему Linux через последовательный канал Ethernet:

Плохие двоичные тесты:

# strace ./testz /dev/null
execve("./testz", ["./testz", "/dev/null"], [/* 17 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40089000
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Segmentation fault

# strace ./testz /dev/ttyS0
execve("./testz", ["./testz", "/dev/ttyS0"], [/* 17 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x400ca000
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Segmentation fault
#

Вот вывод данных strace для двоичного файла testz, переданного на SD-карту во встроенную систему Linux:

Хорошие двоичные тесты:

#  strace ./testz /dev/null
execve("./testz", ["./testz", "/dev/null"], [/* 17 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40058000
open("/lib/libc.so.0", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=298016, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x400b8000
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\240\230\0\0004\0\0\0"..., 4096) = 4096
mmap2(NULL, 348160, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40147000
mmap2(0x40147000, 290576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x40147000
mmap2(0x40196000, 4832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x47) = 0x40196000
mmap2(0x40198000, 14160, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40198000
close(3)                                = 0
munmap(0x400b8000, 4096)                = 0
stat("/lib/ld-uClibc.so.0", {st_mode=S_IFREG|0755, st_size=25296, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x400c4000
set_tls(0x400c4470, 0x400c4470, 0x4007b088, 0x400c4b18, 0x40) = 0
mprotect(0x40196000, 4096, PROT_READ)   = 0
mprotect(0x4007a000, 4096, PROT_READ)   = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B115200 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B115200 opost isig icanon echo ...}) = 0
open("/dev/null", O_RDWR|O_NOCTTY|O_NONBLOCK) = 3
write(3, "1\n", 2)                      = 2
write(3, "12\n", 3)                     = 3
write(3, "123\n", 4)                    = 4
write(3, "1234\n", 5)                   = 5
write(3, "12345\n", 6)                  = 6
write(3, "1\r\n", 3)                    = 3
write(3, "12\r\n", 4)                   = 4
write(3, "123\r\n", 5)                  = 5
write(3, "1234\r\n", 6)                 = 6
close(3)                                = 0
exit_group(0)                           = ?


#  strace ./testz /dev/ttyS0
execve("./testz", ["./testz", "/dev/ttyS0"], [/* 17 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x400ed000
open("/lib/libc.so.0", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=298016, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40176000
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\240\230\0\0004\0\0\0"..., 4096) = 4096
mmap2(NULL, 348160, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40238000
mmap2(0x40238000, 290576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x40238000
mmap2(0x40287000, 4832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x47) = 0x40287000
mmap2(0x40289000, 14160, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40289000
close(3)                                = 0
munmap(0x40176000, 4096)                = 0
stat("/lib/ld-uClibc.so.0", {st_mode=S_IFREG|0755, st_size=25296, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x400d1000
set_tls(0x400d1470, 0x400d1470, 0x40084088, 0x400d1b18, 0x40) = 0
mprotect(0x40287000, 4096, PROT_READ)   = 0
mprotect(0x40083000, 4096, PROT_READ)   = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B115200 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B115200 opost isig icanon echo ...}) = 0
open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 3
write(3, "1\n", 21
)                      = 2
write(3, "12\n", 312
)                     = 3
write(3, "123\n", 4123
)                    = 4
write(3, "1234\n", 51234
)                   = 5
write(3, "12345\n", 612345
)                  = 6
write(3, "1\r\n", 31
)                    = 3
write(3, "12\r\n", 412
)                   = 4
write(3, "123\r\n", 5123
)                  = 5
write(3, "1234\r\n", 61234
)                 = 6
close(3)                                = 0
exit_group(0)                           = ?

Ответы [ 2 ]

9 голосов
/ 21 января 2012

РЕДАКТИРОВАТЬ: Читайте подробности, но быстрый ответ таков: ваш FTP-клиент повреждает вашу программу.Это преднамеренная функция FTP, которую можно отключить, набрав binary в приглашении FTP перед get whatever или put whatever.Если вы используете графический FTP-клиент, у него должен быть флажок с тем же эффектом.Или переключитесь на scp, который не имеет этой неудобной функции.


Во-первых, нет никакой разницы в сгенерированном коде сборки между (одним из) рабочими объектными файлами и поврежденным объектным файлом..

$ objdump -dr dc-good.o > dc-good.s
$ objdump -dr dc-bad.o > dc-bad.s
$ diff -u dc-good.s dc-bad.s
--- dc-good.s   2012-01-21 08:20:05.318518596 -0800
+++ dc-bad.s    2012-01-21 08:20:10.954566852 -0800
@@ -1,5 +1,5 @@

-dc-good.o:     file format elf32-littlearm
+dc-bad.o:     file format elf32-littlearm


 Disassembly of section .text:

На самом деле, есть только два байта , которые отличаются между хорошими и плохими объектными файлами.(Вы неправильно поняли то, что я просил, с "test\r\n" против "testX\n": я хотел, чтобы две строки были одинаковыми длина , чтобы в объектных файлах все было с одинаковым смещением. К счастью, вашекомпилятор дополняет более короткую строку до той же длины, что и более длинная строка, поэтому все равно имеет одинаковое смещение.)

$ hd dc-good.o > dc-good.x
$ hd dc-bad.o > dc-bad.x
$ diff -u1 dc-good.x dc-bad.x
--- dc-good.x   2012-01-21 08:17:28.713174977 -0800
+++ dc-bad.x    2012-01-21 08:17:39.129264489 -0800
@@ -154,3 +154,3 @@
 00000990  53 74 61 72 74 69 6e 67  20 70 72 6f 67 72 61 6d  |Starting program|
-000009a0  00 00 00 00 74 65 73 74  58 0a 00 00 2f 64 65 76  |....testX.../dev|
+000009a0  00 00 00 00 74 65 73 74  58 0d 0a 00 2f 64 65 76  |....testX.../dev|
 000009b0  2f 74 74 79 53 30 00 00  66 64 20 3d 20 25 75 0a  |/ttyS0..fd = %u.|
@@ -223,3 +223,3 @@
 00000de0  61 72 69 65 73 2f 64 61  74 61 63 6f 6c 6c 65 63  |aries/datacollec|
-00000df0  74 6f 72 2d 62 61 64 2d  62 69 6e 61 72 79 2d 32  |tor-bad-binary-2|
+00000df0  74 6f 72 2d 62 61 64 2d  62 69 6e 61 72 79 2d 31  |tor-bad-binary-1|
 00000e00  00 46 49 4c 45 00 5f 5f  73 74 61 74 65 00 5f 5f  |.FILE.__state.__|

Первое отличие - это разница, которая должна быть: 74 65 73 74 58 0a 00 00 - правильная кодировка"test\n" (с одним байтом заполнения), 74 65 73 74 58 0d 0a 00 - правильная кодировка "test\r\n".Другим отличием является информация об отладке: имя каталога, в котором вы скомпилировали программы.Это безвредно.

Объектные файлы такие, какими они должны быть, поэтому на данный момент мы можем исключить ошибку в компиляторе или ассемблере.Теперь давайте посмотрим на исполняемые файлы.

$ hd dc-good > dc-good.xe
$ hd dc-bad > dc-bad.xe
$ diff -u1 dc-good.xe dc-bad.xe
--- dc-good.xe  2012-01-21 08:31:33.456437417 -0800
+++ dc-bad.xe   2012-01-21 08:31:38.388480238 -0800
@@ -120,3 +120,3 @@
 00000770  f0 af 1b e9 53 74 61 72  74 69 6e 67 20 70 72 6f  |....Starting pro|
-00000780  67 72 61 6d 00 00 00 00  74 65 73 74 58 0a 00 00  |gram....testX...|
+00000780  67 72 61 6d 00 00 00 00  74 65 73 74 58 0d 0a 00  |gram....testX...|
 00000790  2f 64 65 76 2f 74 74 79  53 30 00 00 66 64 20 3d  |/dev/ttyS0..fd =|
@@ -373,3 +373,3 @@
 00001750  63 6f 6c 6c 65 63 74 6f  72 2d 62 61 64 2d 62 69  |collector-bad-bi|
-00001760  6e 61 72 79 2d 32 00 46  49 4c 45 00 5f 5f 73 74  |nary-2.FILE.__st|
+00001760  6e 61 72 79 2d 31 00 46  49 4c 45 00 5f 5f 73 74  |nary-1.FILE.__st|
 00001770  61 74 65 00 5f 5f 67 63  73 00 73 74 64 6f 75 74  |ate.__gcs.stdout|

Те же два различия, разные смещения внутри исполняемого файла.Это тоже как и должно быть.Мы также можем исключить ошибку в компоновщике (если он искажает адрес строки, он должен был бы испортить его одинаково в обоих исполняемых файлах, и они оба должны аварийно завершить работу).

На данный момент я думаю, что мы смотрим на ошибку в вашей C-библиотеке или ядре.Чтобы закрепить это дальше, я бы хотел, чтобы вы попробовали этот тестовый скрипт.Запустите его как sh testz.sh на плате ARM и отправьте нам полный вывод.

#! /bin/sh

set -e
cat >testz.c <<\EOF
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define W(f, s) write(f, s, sizeof s - 1)

int
main(int ac, char **av)
{
  int f;
  if (ac != 2) return 2;
  f = open(av[1], O_RDWR|O_NOCTTY|O_NONBLOCK);
  if (f == -1) return 1;

  W(f, "1\n");
  W(f, "12\n");
  W(f, "123\n");
  W(f, "1234\n");
  W(f, "12345\n");

  W(f, "1\r\n");
  W(f, "12\r\n");
  W(f, "123\r\n");
  W(f, "1234\r\n");

  close(f);
  return 0;
}
EOF

arm-linux-gcc -Wall -g testz.c -o testz
set +e
strace ./testz /dev/null
echo ----
strace ./testz /dev/ttyS0
echo ----
exit 0

Я посмотрел на предоставленный вами поврежденный бинарный файл и теперь я знаю, что не так.

$ ls -l testz*
-rwxr-x--- 1 zack zack 7528 Dec 31  1979 testz-bad
-rwxr-x--- 1 zack zack 7532 Jan 21 16:35 testz-good

Игнорировать нечетную метку даты;Посмотрите, как версия -bad на четыре байта меньше, чем версия -good?В исходном коде было ровно четыре \r символа.Давайте посмотрим на различия в шестнадцатеричных дампах.Я вытащил интересный фрагмент из различий и немного перемешал его, чтобы было легче увидеть, что происходит.

 00000620  00 00 00 00 31 32 33 34  0a 00 00 00 31 32 33 34  |....1234....1234|

-00000630  35 0a 00 00 31 0d 0a 00  31 32 0d 0a 00 00 00 00  |5...1...12......|
+00000630  35 0a 00 00 31 0a 00 31  32 0a 00 00 00 00 31 32  |5...1..12.....12|

-00000640  31 32 33 0d 0a 00 00 00  31 32 33 34 0d 0a 00 00  |123.....1234....|
+00000640  33 0a 00 00 00 31 32 33  34 0a 00 00 00 00 00 00  |3....1234.......|

-00000650  00 00 00 00 68 84 00 00  1c 84 00 00 00 00 00 00  |....h...........|
+00000650  68 84 00 00 1c 84 00 00  00 00 00 00 01 00 00 00  |h...............|

Передача файлов заменяет 0d 0a (то есть * 1048).*) последовательности с 0a (просто \n).Это приводит к тому, что все после этой точки в файле будет смещено на четыре байта от того места, где оно должно быть.Код до этой точки, как и все заголовки ELF, на которые смотрит kernel , поэтому вы не получаете

execve("./testz-bad", ["./testz-bad", "/dev/null"], [/* 36 vars */]) = -1 ENOEXEC (Exec format error)

из тестового скрипта;вместо этого вы получаете segfault внутри динамического загрузчика, потому что сегмент DYNAMIC (который сообщает динамическому загрузчику, что делать) равен после начала перемещения.

$ readelf -d testz-bad 2> /dev/null

Dynamic section at offset 0x660 contains 13 entries:
  Tag        Type                         Name/Value
 0x00000035 (<unknown>: 35)              0xc
 0x0000832c (<unknown>: 832c)            0xd
 0x00008604 (<unknown>: 8604)            0x19
 0x00010654 (<unknown>: 10654)           0x1b
 0x00000004 (HASH)                       0x1a
 0x00010658 (<unknown>: 10658)           0x1c
 0x00000004 (HASH)                       0x4
 0x00008108 (<unknown>: 8108)            0x5
 0x0000825c (<unknown>: 825c)            0x6
 0x0000815c (<unknown>: 815c)            0xa
 0x00000098 (<unknown>: 98)              0xb
 0x00000010 (SYMBOLIC)                   0x15
 0x00000000 (NULL)                       0x3

Контраст:

$ readelf -d testz-good

Dynamic section at offset 0x660 contains 18 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.0]
 0x0000000c (INIT)                       0x832c
 0x0000000d (FINI)                       0x8604
 0x00000019 (INIT_ARRAY)                 0x10654
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x10658
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x8108
 0x00000005 (STRTAB)                     0x825c
 0x00000006 (SYMTAB)                     0x815c
 0x0000000a (STRSZ)                      152 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x10718
 0x00000002 (PLTRELSZ)                   56 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x82f4
 0x00000000 (NULL)                       0x0

Отладочная информация также указана после смещения, поэтому gdb программа не понравилась.


Так почему именно эта особая коррупция?Это не ошибка ни в чем;это преднамеренная функция вашего клиента FTP , по умолчанию передающая файлы в «текстовом режиме», что означает (среди прочего), что он преобразует окончания строк в стиле DOS (\r\n) в стиль Unix(\n).Потому что это было бы то, что вы хотели, если бы это был 1991 год, и вы переносили текстовые файлы с вашего IBM PC на ваш институциональный файловый сервер.Это в принципе никогда не то, что сейчас нужно, даже если вы перемещаете текстовые файлы.К счастью, вы можете отключить его: просто наберите binary в приглашении FTP перед командами передачи файлов.* К счастью, насколько я знаю, нет способа заставить эту палку ;Вы должны делать это каждый раз.Я рекомендую переключиться на scp, который всегда передает файлы дословно, а также проще в управлении из автоматизации сборки.

1 голос
/ 19 января 2012

Перво-наперво - тот факт, что вы видите только ошибку сегмента, НЕ указывает на то, что программа вообще не работала.Что происходит, так это то, что выходные данные из вызовов printf буферизуются по строкам, а когда программа переходит в состояние сбоя, она никогда не записывается.

Если вы добавляете fflush (stdout);после каждого printf вы увидите свой вывод до segfault.

Теперь, в вашей исходной программе, какой смысл fcntl (fd, F_SETFL, 0);вызов?Чего вы пытаетесь достичь с этим?Вы пытаетесь отключить неблокирующий режим?Что если вы не сделаете этот вызов?

Что касается вашего второго теста, я вижу, что вы используете perror, но опять же отсутствие сообщений об ошибках не говорит вам, что программа не работает -он просто говорит вам, что вы не получили никаких сообщений об ошибках, и вы все еще не очищаете стандартный вывод, поэтому вы никогда не увидите printf из run_experiment.

Я также вижу, что во втором тесте вы 'Вы делаете fdopen с режимом чтения, затем пытаетесь записать в этот указатель FILE.Хотя это, конечно, не должно приводить к сбою, оно также не должно работать.

Теперь, вне вашей программы, вы уверены, что последовательный порт работает нормально?Попробуйте выполнить 'cat> / dev / ttyS0' и посмотрите, что произойдет, просто чтобы убедиться, что с аппаратным обеспечением это не так уж и плохо.

...