A “std::bad_alloc” issue caused by typo

Last week, I fixed a bug which was caused by a typo. The simplified code is like this:

#include <vector>
using namespace std;

class A {
    int i;
public:
    A(int i): i(i) {}
};

class B {
    vector<A> v;
public:
    B(vector<A> v1): v(v) {}
};

int main() {
    vector<A> a(1, 0);
    B b(a);
    return 0;
}

Please note the constructor of B:

B(vector<A> v1): v(v) {}

It was supposed to use v1 to initialize v, while I misspelled: v(v). My compiler is gcc 6.3.1, compile and run it:

# g++ -g test.cpp
# ./a.out
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

Change B(vector<A> v1): v(v) to B(vector<A> v1): v(v1), then all is OK.

The caveat of building OpenMP program

When building OpenMP program, you must be sure to use -fopenmp option in both compile and link stage (refer stackoverflow), else you may get a hit.

Take the following example:

#include <unistd.h>
#include <omp.h>

int main(void){

        #pragma omp parallel num_threads(4)
        for(;;)
        {
            sleep(1);
        }

        return 0;
}

Use gcc to build it (contains both compile and link):

gcc -fopenmp parallel.c

Execute the program and check the threads number:

$ ./a.out &
[1] 5684
$ ps -T 5684
  PID  SPID TTY      STAT   TIME COMMAND
 5684  5684 pts/16   Sl     0:00 ./a.out
 5684  5685 pts/16   Sl     0:00 ./a.out
 5684  5686 pts/16   Sl     0:00 ./a.out
 5684  5687 pts/16   Sl     0:00 ./a.out

There are 4 threads which as our expected.

Then we create a neat Makefile and split the compile and link stages separately:

all:
        gcc -fopenmp -c parallel.c -o parallel.o
        gcc parallel.o
clean:
        rm *.o a.out

Run the Makefie:

$ make
gcc -fopenmp -c parallel.c -o parallel.o
gcc parallel.o
parallel.o: In function `main':
parallel.c:(.text+0x19): undefined reference to `GOMP_parallel'
collect2: error: ld returned 1 exit status
make: *** [Makefile:3: all] Error 1

During the link phase, the gcc complained it can’t find GOMP_parallel. So we need to add -fopenmp in link command too:

all:
        gcc -fopenmp -c parallel.c -o parallel.o
        gcc -fopenmp parallel.o
clean:
        rm *.o a.out

This time all is OK:

$ make
gcc -fopenmp -c parallel.c -o parallel.o
gcc -fopenmp parallel.o
$ ./a.out &
[2] 6502
$ ps -T 6502
  PID  SPID TTY      STAT   TIME COMMAND
 6502  6502 pts/16   Sl     0:00 ./a.out
 6502  6503 pts/16   Sl     0:00 ./a.out
 6502  6504 pts/16   Sl     0:00 ./a.out
 6502  6505 pts/16   Sl     0:00 ./a.out

You can also use ldd tool to check a.out‘s dynamic libraries:

$ ldd /usr/lib/libgomp.so.1
    linux-vdso.so.1 (0x00007ffd9c0dd000)
    libgomp.so.1 => /usr/lib/libgomp.so.1 (0x00007fe5554ee000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fe5552d0000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007fe554f2c000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fe554d28000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe55571c000)

The libgomp includes the GOMP_parallel definition.

Why doesn’t “~ /.profile” take effect in Arch Linux?

I followed Rust tutorial to install Rust on my Arch Linux, and found the Rust directory is indeed added into ~/.profile file:

$ cat ~/.profile

export PATH="$HOME/.cargo/bin:$PATH"

But after re-login, I couldn’t see Rust folder is in $PATH variable:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/opt/cuda/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl

It didn’t take effect!

After reading Arch Linux Bash document, I noticed the following statement which describes ~/.bash_profile:

If this file does not exist, ~/.bash_login and ~/.profile are checked in that order.

So this means that if ~/.bash_profile exists,~/.bash_login and ~/.profile won’t be checked, right? Let’s do a check:

(1) Use strace command to record the files’ names related to current user when logining.

$ strace -o out -e open bash -l
$ grep "/home/xiaonan" out
open("/home/xiaonan/.bash_profile", O_RDONLY) = 3
open("/home/xiaonan/.bashrc", O_RDONLY) = 3
open("/home/xiaonan/.bash_history", O_RDONLY) = 3
open("/home/xiaonan/.bash_history", O_RDONLY) = 3
$ exit
logout

As expected, the ~/.profile, i.e., /home/xiaonan/.profile wasn’t opened.

(2) Renamed ~/.bash_profile to pretend it didn’t exist, checked whether ~/.profile would be read:

$ mv .bash_profile .bash_profile.bak
$ strace -o out -e open bash -l
$ grep "/home/xiaonan" out
open("/home/xiaonan/.bash_profile", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/home/xiaonan/.bash_login", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/home/xiaonan/.profile", O_RDONLY) = 3
open("/home/xiaonan/.bash_history", O_RDONLY) = 3
open("/home/xiaonan/.bash_history", O_RDONLY) = 3
$ echo $PATH
/home/xiaonan/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/opt/cuda/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
$ exit
logout

As things turned out, when the ~/.bash_profile didn’t exist:

open("/home/xiaonan/.bash_profile", O_RDONLY) = -1 ENOENT (No such file or directory)

The bash would access ~/.bash_login and ~/.profile in sequence, and $HOME/.cargo/bin was added into $PATH finally.

The solution of this issue is adding following statement in ~/.bash_profile:

[[ -f ~/.profile ]] && . ~/.profile

Now it works!

What is the effect of extern “C”?

I often see the following code in C header files:

#ifdef __cplusplus
extern "C" {
#endif

......

#ifdef __cplusplus
}
#endif

What does it mean? Since there is __cplusplus marco, it must be related to C++ compilation. Let’s see a simple program (print.c):

$ cat print.c
#include <stdio.h>

void print(void)
{
        printf("Hello world!\n");
}

Use gcc to generate object file:

$ gcc -c print.c
$ 

Then create a main.cpp file which calls print() in its main() function:

$ cat main.cpp
extern void print(void);

int main(void)
{
        print();
        return 0;
}

Compile main.cpp and link with print.o:

$ g++ main.cpp print.o
/tmp/cc60fu19.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `print()'
collect2: error: ld returned 1 exit status

It is weird, right? the print() function must be defined in print.o, why can’t g++ find it? Let’s do a simple magic, add "C" in extern void print(void);:

$ cat main.cpp
extern "C" void print(void);

int main(void)
{
        print();
        return 0;
}

Try compile main.cpp again:

$ g++ main.cpp print.o
$ ./a.out
Hello world!

It is OK now! The root cause is related to name mangling. To be simplified, when compile C++ code, the names of functions, global variables, etc will be modified, not the same as original format. While compile C code, this won’t happen. The function of extern "C" is to tell C++ compiler search the original name, not the mangled ones. To get a sense of name mangling, you can check the print() name in object file:

$ readelf -s print.o | grep print
 1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS print.c
 9: 0000000000000000    17 FUNC    GLOBAL DEFAULT    1 print

Then use g++ to compile print.c, and check function name again:

$ g++ -c print.c
$ readelf -s print.o | grep print
 1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS print.c
 9: 0000000000000000    17 FUNC    GLOBAL DEFAULT    1 _Z5printv

You can see the print() function name is actually _Z5printv when use g++ to generate the object file.

References:
Why do we use extern “C”?;
In C++ source, what is the effect of extern ā€œCā€?.

The differences between using gcc/g++ to compile *.c/*.cpp files

I bump into Compiling a C++ program with gcc today, and think it is a very interesting topic. So I do the following tests:

(1) Create a canonical C source file:

$ cat main.c
#include <stdio.h>

void hello(void)
{
        printf("Hello World!\n");
}

int main(void)
{
        hello();
}

Use gcc to compile it and search hello in symbol table of executable file:

$ gcc main.c
$ readelf -s a.out | grep hello
53: 00000000004004f6    17 FUNC    GLOBAL DEFAULT   13 hello

Then use g++ to compile it and also search hello in a.out:

$ g++ main.c
$ readelf -s a.out | grep hello
54: 0000000000400526    17 FUNC    GLOBAL DEFAULT   13 _Z5hellov

Since hello is name mangled to _Z5hellov when using g++, we can make sure that g++ will compile *.c file as C++.

(2) Modify main.c to a standard C++ file:

$ cat main.cpp
#include <iostream>

void hello(void)
{
        std::cout << "Hello, world!" << std::endl;
}

int main(void)
{
        hello();
}

Use gcc to compile it:

$ gcc main.cpp
/tmp/cccAb8IH.o: In function `hello()':
main.cpp:(.text+0xa): undefined reference to `std::cout'
main.cpp:(.text+0xf): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
main.cpp:(.text+0x14): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
main.cpp:(.text+0x1c): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
/tmp/cccAb8IH.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x56): undefined reference to `std::ios_base::Init::Init()'
main.cpp:(.text+0x65): undefined reference to `std::ios_base::Init::~Init()'
collect2: error: ld returned 1 exit status

We can see there are many link errors. The reason is when gcc compiles *.cpp file, it will employ C++ compiler, but it only use defaults C and gcc helpers libraries during linkage stage, and won’t use stdc++ library. Modify the command, then all is OK:

$ gcc main.cpp -lstdc++
$ readelf -s a.out | grep hello
41: 00000000004007f7    21 FUNC    LOCAL  DEFAULT   13 _GLOBAL__sub_I__Z5hellov
59: 0000000000400786    35 FUNC    GLOBAL DEFAULT   13 _Z5hellov

Certainly , since it is a regular C++ file, use g++ will undoubtedly be OK:

$ g++ main.cpp
$ readelf -s a.out | grep hello
41: 0000000000400817    21 FUNC    LOCAL  DEFAULT   13 _GLOBAL__sub_I__Z5hellov
59: 00000000004007a6    35 FUNC    GLOBAL DEFAULT   13 _Z5hellov

(3) Modify the above file name from main.cpp to main.c.

$ cat main.c
#include <iostream>

void hello(void)
{
        std::cout << "Hello, world!" << std::endl;
}

int main(void)
{
        hello();
}

Use g++ to compile it:

$ g++ main.c
$ readelf -s a.out | grep hello
41: 0000000000400817    21 FUNC    LOCAL  DEFAULT   13 _GLOBAL__sub_I__Z5hellov
59: 00000000004007a6    35 FUNC    GLOBAL DEFAULT   13 _Z5hellov

All is OK, and the stdc++ library will be used during linkage stage.

Use gcc instead:

$ gcc main.c
main.c:1:20: fatal error: iostream: No such file or directory

                    ^
compilation terminated.

This error identifies gcc always uses standard C compiler to *.c file, so it can’t find C++ related header files.

P.S. my gcc version:

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/6.3.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc-multilib/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release
Thread model: posix
gcc version 6.3.1 20170109 (GCC)