When I built a program with –static option and it calls shm_open() function I get Segmentation fault. Without -static option all works like a charm.
Does anybody know why?
Below I have cited a debug info and a part of truncated source code from a big project.
You can comment/uncomment
#STATIC = -static
string from Makefile to reproduce the bug.
$ gdb –args ./debug/example sample017
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 Reading symbols from ./debug/example...done. (gdb) run Starting program: ./example sample017 Program received signal SIGSEGV, Segmentation fault. 0x0000000000000000 in ?? () (gdb) backtrace #0 0x0000000000000000 in ?? () #1 0x000000000049a2e3 in __shm_directory (len=0x7fffffffdca8) at ../sysdeps/unix/sysv/linux/shm-directory.c:124 #2 0x0000000000499ff3 in shm_open () #3 0x0000000000499d55 in read_shm (memory=0x6d1be0, share_name=0x6d1d20 "sample017") at main.c:51 #4 0x0000000000499efe in read_memory (memory=0x6d1be0, argc=0x7fffffffde0c, argv=0x7fffffffdf68) at main.c:98 #5 0x0000000000499f70 in main (argc=2, argv=0x7fffffffdf68) at main.c:112
Strace for statically linked
$strace ./debug/example sample017
execve("./debug/example", ["./debug/example", "sample017"], [/* 64 vars */]) = 0 uname({sysname="Linux", nodename="Lubuntu", ...}) = 0 brk(NULL) = 0xc5e000 brk(0xc5f1c0) = 0xc5f1c0 arch_prctl(ARCH_SET_FS, 0xc5e880) = 0 readlink("/proc/self/exe", "/home/52034/111/debug/example", 4096) = 29 brk(0xc801c0) = 0xc801c0 brk(0xc81000) = 0xc81000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} --- +++ killed by SIGSEGV +++ Segmentation fault
Strace for dynamically linked:
$ strace ./debug/example sample017
execve("./debug/example", ["./debug/example", "sample017"], [/* 64 vars */]) = 0 brk(NULL) = 0x740000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f78fa50e000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=135773, ...}) = 0 mmap(NULL, 135773, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f78fa4ec000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "177ELF21133>1Pt2"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0 mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f78f9f21000 mprotect(0x7f78fa0e1000, 2097152, PROT_NONE) = 0 mmap(0x7f78fa2e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f78fa2e1000 mmap(0x7f78fa2e7000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f78fa2e7000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "177ELF2113>1!"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=31712, ...}) = 0 mmap(NULL, 2128832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f78f9d19000 mprotect(0x7f78f9d20000, 2093056, PROT_NONE) = 0 mmap(0x7f78f9f1f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f78f9f1f000 close(3) = 0 ... end etc...
Compiler:
$ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)
Linux:
$ cat /etc/issue Ubuntu 16.04.3 LTS n l
$cat main.c
#define _GNU_SOURCE #include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include <ctype.h> #include <errno.h> // String library #include <string.h> // Shared memory #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ typedef struct { size_t length; int fd; float *array; #define MAX_NAME_LENGTH 255 char name[MAX_NAME_LENGTH]; } Memory; static Memory *new_memory(void) { Memory *structure = (Memory*)malloc(sizeof(Memory)); if(structure == NULL){ exit(EXIT_FAILURE); } memset(structure, 0, sizeof(Memory)); return structure; } static void detach_memory(Memory *memory) { munmap(memory->array,(memory->length)*sizeof(float)); free(memory); } static bool read_shm( Memory *memory, char *share_name ) { if ((memory->fd = shm_open(share_name, O_RDONLY|O_EXCL, 0400)) == -1){ return(false); } memcpy(memory->name,share_name,strlen(share_name)+1); struct stat info; if (fstat(memory->fd, &info) == -1 || errno == EBADF){ return(false); } memory->length = (size_t)info.st_size / sizeof(float); float *array = mmap(0, (size_t)info.st_size, PROT_READ, MAP_SHARED,memory->fd, 0); if (array == (float*)-1){ exit(EXIT_FAILURE); } if (memory->length < 2){ exit(EXIT_FAILURE); } memory->array = array; return(true); } void read_memory( Memory *memory, int *argc, char **argv ) { if (*argc == 1){ exit(EXIT_FAILURE); } char *path = strdup(argv[1]); const char * const file = basename(path); char *share_name = strdup(file); char *ext = strchr(share_name, '.'); if(ext){ *ext = ''; } if(!(read_shm(memory,share_name))){ free(path); free(share_name); exit(EXIT_FAILURE); } free(path); free(share_name); } int main(int argc, char **argv) { Memory *memory = new_memory(); read_memory(memory,&argc,argv); for(size_t i = 0; i < memory->length;i++){ printf("memory: %.5fn",memory->array[i]); } detach_memory(memory); return(EXIT_SUCCESS); }
$cat Makefile
CC ?= cc CFLAGS += -pipe -std=c11 CFLAGS += -fbuiltin LDFLAGS += -lm -lrt -lpthread EXE = example ARGUMENTS ?= #STATIC = -static STRIP = -s WFLAGS += -Wall -Wextra -Wpedantic -Wshadow WFLAGS += -Wconversion -Wsign-conversion -Winit-self -Wunreachable-code -Wformat-y2k WFLAGS += -Wformat-nonliteral -Wformat-security -Wmissing-include-dirs WFLAGS += -Wswitch-default -Wtrigraphs -Wstrict-overflow=5 WFLAGS += -Wfloat-equal -Wundef -Wshadow WFLAGS += -Wbad-function-cast -Wcast-qual -Wcast-align WFLAGS += -Wwrite-strings WFLAGS += -Winline ifneq ($(CC), clang) WFLAGS += -Wlogical-op CFLAGS += -finline-functions CFLAGS += -flto endif MAKEFLAGS += --no-print-directory CONFIG += ordered # # Project files # SRCS = $(wildcard *.c) # Exclude a file OBJS = $(SRCS:.c=.o) # # Debug build settings # DBGDIR = debug DBGEXE = $(DBGDIR)/$(EXE) DBGOBJS = $(addprefix $(DBGDIR)/, $(OBJS)) DBGLIBPATH = ../../logger/lib/release DBGCFLAGS += -g -ggdb -ggdb1 -ggdb2 -ggdb3 -O0 -DDEBUG DBGLDFLAGS += -lm -lc .PHONY: all clean debug prep release remake clang openmp one liblogger # Default build all: debug # # Debug rules # debug: liblogger $(DBGEXE) $(DBGEXE): $(DBGOBJS) $(CC) $(CFLAGS) $(DBGCFLAGS) -L$(DBGLIBPATH) $(WFLAGS) $(STATIC) $(DBGLDFLAGS) -o $(DBGEXE) $^ $(LDFLAGS) @echo "$(DBGEXE) linked." -include $(DBGDEP) $(DBGDIR)/%.o: %.c @mkdir -p $(DBGDIR) @$(CC) -MM $(CFLAGS) $(DBGCFLAGS) $(WFLAGS) $< | sed '1s/^/$$(DBGDIR)//' > $(@D)/$(*F).d $(CC) -c $(CFLAGS) $(DBGCFLAGS) $(WFLAGS) -o $@ $< @echo $<" compiled." clean: @rm -rf *.out.* doc $(DBGDEP) $(DBGEXE) $(DBGOBJS) $(DBGDIR)/*.d @test -d $(DBGDIR) && rm -d $(DBGDIR) || true
By the way, is is works well on the older platform:
Linux:
$cat /etc/issue Ubuntu 13.10 n l
Compiler:
$gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.8.1-10ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)
Advertisement
Answer
You’ve uncovered a bug in how the static librt.a
and libpthread.a
interact. (librt.a:shm_open()
calls it, and it is defined in libpthread.a
. Their dynamic versions link it just fine.)
Specifically, libpthread.a
does implement const char *__shm_directory(size_t *len);
, but for whatever reason, it gets mislinked somehow, causing a segfault. I suspect some kind of weak symbol shadowing, but haven’t investigated it much further.
The fix is to implement the function yourself. It is a simple function, that returns the path to the directory, including a final slash, where the shared memory files are to be created in. On Linux systems, this should always be a tmpfs
mount at /dev/shm/
.
I would suggest creating a new C source file, maybe shm_open_fix.c:
#include <stdlib.h> #include <string.h> /* This avoids a segfault when code using shm_open() is compiled statically. (For some reason, compiling the code statically causes the __shm_directory() function calls in librt.a to not reach the implementation in libpthread.a. Implementing the function ourselves fixes this issue.) */ #ifndef SHM_MOUNT #define SHM_MOUNT "/dev/shm/" #endif static const char shm_mount[] = SHM_MOUNT; const char *__shm_directory(size_t *len) { if (len) *len = strlen(shm_mount); return shm_mount; }
and compile and link it into your final binary. This should take care of the segfault. Also, the resulting binary no longer depends on libpthread.a
at all, unless you use the pthread functions elsewhere in your code.