0

Based on building a .so that is also an executable I'm trying o reproduce with C++ and I'm getting a segmentation fault on main program execution.

/* pie.cpp */
#include <cstdio>
int foo()
{
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return 42; 
}
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}

/* pie.h */
#ifndef PIE_H
#define PIE_H

int foo();

#endif

/* main.cpp */
#include <cstdio>
#include <string>
#include "pie.h"

std::string data;

int main() 
{
  data="TEST"; 
  printf("in %s %s:%d [%s]\n", __func__, __FILE__, __LINE__, data.c_str());
  return foo(); 
}


$ g++ -fPIC -pie -o pie.so pie.cpp -Wl,-E
$ g++ main.cpp ./pie.so


$ ./pie.so
in main pie.cpp:10
in foo pie.cpp:5
$ ./a.out
Segmentation fault (core dumped)
$

I the definition of "data" is moved from global to local it runs. It seems that global variables are not being initialized.

Can someone explain what happens and what should be done to make it run?

gdb backtrace on code file result:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  std::string::size (this=0x404080 <data>) at /usr/src/debug/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/bits/basic_string.h:716
716       { return _M_rep()->_M_length; }
(gdb) bt
#0  std::string::size (this=0x404080 <data>) at /usr/src/debug/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/bits/basic_string.h:716
#1  std::string::assign (this=0x404080 <data>, __s=0x402010 "TEST", __n=4) at /usr/src/debug/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/bits/basic_string.tcc:262
#2  0x00000000004011c5 in main () at main.cpp:9

Thanks!

SNJ
  • 171
  • 7
  • Global uninitialized variables are stored in .bss section. If it is defined locally, variable is stored in stack. It is the difference. – Emre İriş Mar 22 '21 at 08:32
  • Build a debug version and run it in a debugger to see what's the problem. I suggest to assign the result of `data.c_str()` to a variable and use this variable for `printf` to make it easier to check the values. – Bodo Mar 22 '21 at 08:41
  • Thanks Emre İriş. I know, but the question is why it segfault in this case? – SNJ Mar 22 '21 at 10:37
  • @Bado, I added the gdb result of backtracing the core file for reference. For what I see it segfault at string, but why? – SNJ Mar 22 '21 at 10:39
  • glibc developers decided a few years ago, that `.so`s with an entry point no longer are supported. Not writing this as an answer, because apparently I misplaced my bookmark to the maillist discussion about that. – datenwolf Mar 22 '21 at 10:57
  • Thanks @datenwolf. I'll try to find this info. It's strange that compiler@linker do their jobs without complaining, and that if a global variable is not defined it works. And also /lib64/libc.so.6 can be run – SNJ Mar 22 '21 at 11:13

1 Answers1

1

I just tried code from my old answer; it no longer works using a recent GLIBC (I have 2.31-9+build1):

$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so

$ ./pie.so
in main pie.c:10
in foo pie.c:5

$ ./a.out
./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable

Using your C++ example, I can't build it with your commands:

$ g++ -fPIC -pie -o pie.so pie.cc -Wl,-E
$ gcc main.cc ./pie.so
/usr/bin/ld: /tmp/ccaXra73.o: in function `main':
main.cc:(.text+0x13): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(char const*)'
/usr/bin/ld: main.cc:(.text+0x1f): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const'
/usr/bin/ld: /tmp/ccaXra73.o: in function `__static_initialization_and_destruction_0(int, int)':
main.cc:(.text+0x77): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string()'
/usr/bin/ld: main.cc:(.text+0x8c): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()'
collect2: error: ld returned 1 exit status

If I link with g++ (as you should when you have C++ code), then it links fine but fails the same way as C code does:

$ g++ main.cc ./pie.so

$ ./pie.so
in main pie.cc:9
in foo pie.cc:4

$ ./a.out
./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable

So I guess the answer is: it wasn't intended to work this way (and worked in the past "by accident"), and now GLIBC detects and rejects it.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Thanks @Employed Russian. It should said g++ when compiling main. I corrected the sample. I'm getting a Segmentation Fault, but I'm using glib 2.17. So, I suppose newer version detect this and now reject it instead of Seg Fault. – SNJ Mar 24 '21 at 12:55
  • It seems glibc 2.30 added a check to not allowing PIE objects to be loaded ( https://elixir.bootlin.com/glibc/glibc-2.30/source/elf/dl-load.c line 1204 for the check and line 1218 for the message). – SNJ Mar 24 '21 at 13:09