我已經創建了一個C程序來寫入嵌入式ARM系統上的串行端口(/ dev/ttyS0)。在嵌入式ARM系統上運行的內核是Linux版本3.0.4,使用與下面列出的相同的交叉編譯器構建。由於C文件中有一行代碼而導致段錯誤,整個程序不運行
我的交叉編譯器是在Ubuntu x86_64主機(3.0.0-14-generic#23-Ubuntu SMP)上運行的arm-linux-gcc(Buildroot 2011.08)4.3.6。我已經使用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
這裏可能會出現什麼問題,以及如何調試問題?這是代碼,交叉編譯器編譯器還是OS版本的問題?
打開文件時,我也嘗試過O_WRONLY和O_RDWR沒有O_NOCTTY的各種組合,但問題仍然存在。
正如@wildplasser在下面的評論中所建議的那樣,我用下面的代碼替代了測試函數,這些代碼很大程度上取決於另一個站點上的代碼(http://www.warpspeed.com.au/cgi-bin/ inf2html.cmd?.. \ HTML \書\ 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文件!
如果段錯誤發生在函數中的某一行,那麼我會假定函數調用會導致段錯誤。但是,完整的程序不運行!
UPDATE:提供更多的信息,這裏是用來建立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/device-system/buildroot/buildroot-2011.08/output/host/usr/arm-unknown-linux -uclibcgnueabi/sysroot --with-build-time-tools =/media/RESEARCH/SAS2-version2/device-system/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/device-system/buildroot/buildroot-2011.08/output/host/usr --with- mpfr =/media/RESEARCH/SAS2-version2/device-system/buildroot/buildroot-2011.08/output/host/usr --disable-nls --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)
這裏是我使用的編譯我的代碼生成文件:
CC=arm-linux-gcc
CFLAGS=-Wall
datacollector: datacollector.o
clean:
rm -f datacollector datacollector.o
UPDATE:使用調試在下面的評論和答案中給出的建議,我發現segfault是由包含引起的字符串中的轉義序列。出於某種奇怪的原因,編譯器不喜歡\r
轉義序列,並且會在不運行代碼的情況下導致段錯誤。
如果\r
轉義序列被刪除,則代碼按預期運行。
因此,問題的代碼行應爲以下:
RET =寫入(FD, 「測試\ n」 個,的sizeof( 「測試\ 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()函數寫入串行端口。
據我所知Pavel的評論如下,如果msg
被聲明爲char*
,最好使用strlen()
。
此外,當使用轉義序列\r
寫入tty時,gcc似乎沒有創建適當的二進制文件。
參考上面我的帖子中給出的最後一個測試程序,下面的代碼行導致段錯誤沒有程序運行:
char *msg = "test\r\n";
正如意見建議由Igor,我已經運行gdb調試器在有問題的代碼行的二進制文件上。我不得不用-g
開關編譯程序。 gdb調試器本身在ARM系統上運行,並且所有的二進制文件都是在主機上使用相同的Makefile爲ARM架構構建的。所有的二進制文件都是使用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)
然而,如果我改變的代碼的一行到下文中,二進制編譯和運行正常。需要注意的是\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系統上運行了一個測試程序。雖然Zack提供了一個在嵌入式系統上運行的詳細腳本,但由於缺少安裝在根文件系統中的開發工具(編譯器和頭文件),因此我無法運行腳本 。 代替安裝這些工具,我簡單地編譯了Zack在腳本中提供的漂亮測試程序,並使用了strace實用程序 。 Strace實用程序在嵌入式系統上運行。
最後,我想我明白髮生了什麼。
使用SPI到以太網橋(KSZ8851SNL),通過FTP將錯誤的二進制文件傳輸到嵌入式系統。 在Linux內核中有一個KSZ8851SNL的驅動程序。
看來,無論是Linux內核驅動程序,嵌入式系統上運行proftpd的服務器軟件,或實際硬件本身(KSZ8851SNL) 在某種程度上破壞二元。二進制在嵌入式系統上運行良好。
下面是使用strace對通過以太網串行鏈路傳送到嵌入式Linux系統的testz二進制輸出:
壞二進制測試:
# 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) = ?
What * does * work?你能寫0字節到那個FD?你能寫一個字節的那個字符串到那個fd嗎? – nickgrim
@nickgrim:非常感謝您的評論。奇怪的是,這似乎是導致整個程序出現段錯誤的一行代碼。看起來程序執行沒有到達該行,並且我不能將字符串的0個字節或1個字節寫入fd。 –
也許寫不允許非尋找fd的?嘗試圍繞fputc()循環:'for(cp =「hello world \ r \ n」; * cp; cp ++)fputc(* cp,fp);',oops:當然需要fdopen ... – wildplasser