How to ignore undefined symbols at runtime in Linux? - linux

I know that by default undefined symbols are ignored at compile time. However, I would also like them to be ignored at run-time. I need to distribute a .so that will run with and without MPI. I will know ahead of time if it is an MPI job and if it is not I won't make any MPI_* calls. If it's not an MPI run I need the application to not care that it cannot resolve the MPI_* symbols.
Is this possible? I could have sworn I've done this before but I can't get it working. Everytime I run I immediately get the following even though the logic in my code will never allow that symbol to be referenced:
undefined symbol: hpmp_comm_world
For what it's worth I am using the Intel Fortran Compiler to build the .so file.
EDIT
I found the linker flag: "-z lazy" which is supposed to resolve references to functions when the function is called which is what I want. That doesn't fix my problem, but hpmp_comm_world is a variable - not a function. Should that make a difference?

You can define a symbol to be a weak reference to its definition. Then, the symbol's value will be zero if the definition is not present.
For example, suppose the following is ref.c, which references a function and variable that may or may not be present; we'll use it to build libref.so (corresponding to your library, in your question):
#include <stdio.h>
void global_func(void);
void global_func(void) __attribute__ ((weak));
extern int global_variable __attribute__((weak));
void ref_func() {
printf("global_func = %p\n", global_func);
if (&global_variable)
global_variable++;
if (global_func)
global_func();
}
Here, global_func and global_variable are the weak references to the possibly-available function and variable. This code prints the function's address, increments the variable if it is present, and calls the function if it is present. (Note that the function's and variable's addresses are zero when they are not defined, so it is &global_variable that you must compare with zero.)
And suppose this is def.c, which defines global_func and global_variable; we'll use it to build libdef.so (corresponding to MPI, in your question):
#include <stdio.h>
int global_variable;
void global_func(void) {
printf("Hi, from global_func! global_variable = %d\n", global_variable);
}
And finally, suppose we have a main program, main.c, which calls ref_func from libref.so:
#include <stdio.h>
extern void ref_func(void);
int main(int argc, char **argv) {
printf("%s: ", argv[0]);
ref_func();
return 0;
}
Here's the Makefile that builds libref.so and libdef.so, and then builds two executables, both of which link against libref.so, but only one of which links against libdef.so:
all: ref-absent ref-present
ref-absent: main.o libref.so
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $#
ref-present: main.o libref.so libdef.so
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $#
lib%.so: %.o
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ -o $#
ref.o def.o: CFLAGS += -fpic
clean:
rm -f *.o *.so ref-absent ref-present
Do the build:
$ make
cc -c -o main.o main.c
cc -fpic -c -o ref.o ref.c
cc -shared ref.o -o libref.so
cc main.o libref.so -o ref-absent
cc -fpic -c -o def.o def.c
cc -shared def.o -o libdef.so
cc main.o libref.so libdef.so -o ref-present
$
Note that both ref-absent and ref-present linked without problems, even though there is no definition for global_name in ref-absent.
Now we can run the programs, and see that ref-absent skips the function call, while ref-present uses it. (We have to set LD_LIBRARY_PATH to allow the dynamic linker to find our shared libraries in the current directory.)
$ LD_LIBRARY_PATH=. ./ref-absent
./ref-absent: global_func = (nil)
$ LD_LIBRARY_PATH=. ./ref-present
./ref-present: global_func = 0x15d4ac
Hi, from global_func! global_variable = 1
$
The trick for you will be getting the ((weak)) attribute attached to every declaration of every MPI function your library references. However, as ref.c shows, there can be multiple declarations, and as long as one of them mentions the weak attribute, you're done. So you'll probably have to say something like this (I don't really know MPI):
#include <mpi.h>
mpi_fake_type_t mpi_function_foo(mpi_arg_type_t) __attribute__((weak));
mpi_fake_type_t mpi_function_bar(mpi_other_arg_type_t) __attribute__((weak));
Every reference to an MPI function needs to be in the scope of a ((weak)) declaration for that function; that's how the compiler decides what sort of symbol reference to put in the object file. You'll want to have automated tests to verify that you haven't accidentally generated any non-weak references.

Related

Linking issue in Cross Compilation for arm in linux using aarch64-linux-gnu-

I got an error when try to link (-aarch64-linux-gnu-ld) (the script containing the Makefile was downloaded from https://github.com/Icenowy/aw-el2-barebone). Error is "aarch64-linux-gnu-ld: Error: unable to disambiguate: -nostartfiles (did you mean --nostartfiles ?)
make: *** [Makefile:31: el2-bb.elf] Error 1" How to recode the line 31? of the Makefile
CROSS_COMPILE = /usr/bin/aarch64-linux-gnu-
DEBUG = 1
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
INCLUDES = -I ./include -I ./uboot-headers -ffreestanding
DEFINES = -DSOC_HEADER="\"h6.h\""
ifneq ($(DEBUG),1)
DEFINES += -DNDEBUG
endif
ASFLAGS = $(INCLUDES) $(DEFINES) -D__ASSEMBLY__
CFLAGS = $(INCLUDES) $(DEFINES) -O2
LDSCRIPT = ldscripts/a64.ld
LDSCRIPTS = ldscripts/a64.ld ldscripts/common.ld
LDFLAGS = -nostdlib -nostartfiles -static -T $(LDSCRIPT)
OBJS = start.o init.o uart.o stack.o exceptions.o exception_funcs.o panic.o pgtables.o trapped_funcs.o
all: el2-bb.bin
el2-bb.elf: $(OBJS) $(LDSCRIPTS)
$(LD) $(LDFLAGS) $(OBJS) -o $#
el2-bb.bin: el2-bb.elf
$(OBJCOPY) -O binary $< $#
.PHONY: clean
clean:
rm -f el2-bb.* *.o
-nostartfiles is a gcc option but not an ld option.
If you use gcc to invoke the linker (say with LD=gcc in your Makefile) for making a standalone program that does not use standard startup files, you should use -nostartfiles because gcc links the startup files by default and -nostartfiles disables this.
ld doesn't links any startup files by default, so there is no option to disable linking them. You always pass startup files explicitly to ld. If you don't want startup files, just don't pass them to ld.
In order to see what startup files on your system are create an empty C program:
int main(){}
and compile it:
gcc -c empty.c
gcc -v empty.o
You are likely to see an invocation of ld (or perhaps of collect2, which calls ld) with a long, long list of options and object files.
Now run
gcc -nostartfiles -v empty.o
The .o files are now gone. That's exactly what -nostartfiles does.
When you invoke ld empty.o, these files and options are not there to begin with. In order to make a working program for say a Linux system, you need to pass most of them to ld explicitly. If you are building a program for something other system, you may not need some or all of them. So just don't pass them in.

versioned symbols with Linux ld-linux.so

I am trying to understand how ld-linux.so resolves references to versioned symbols on Linux. I have the following files:
test.c:
void f();
int main()
{
f();
}
a.c and b.c:
void f() {}
symbols.txt:
ABC {
global:
*;
};
Makefile:
all: liba.so libb.so test
liba.so: a.c
gcc -g -shared $^ -o $# -Wl,--version-script=symbols.txt
libb.so: b.c
gcc -g -shared $^ -o $#
test: test.c liba.so
gcc -g test.c -la -L. -o $#
clean:
rm -f liba.so libb.so test
I then ran the following command
LD_PRELOAD=./libb.so LD_LIBRARY_PATH=. ./test
I find that f() from b.c is invoked even though the symbol f in libb.so does not have the version required by test (f#ABC). Why does this happen ?
version-script is used to confine the symbols to be exported in shared libraries. this means faster link speed and few change for symbols conflict.
By default, most function name will be exported, so you can link with libb.so without any problem.
If you want to determine which version of function should be used, You need to specify it in your program. This need some assembly code to specify .symver.
For more details, please read Ulrich Drepper’s paper https://www.akkadia.org/drepper/dsohowto.pdf

jsoncpp error: ‘PrecisionType’ is not a class or namespace

I download jsoncpp on Github. And built and installed it with meson and ninja. But when I include <json/json.h> in my program, I got the error:
‘PrecisionType’ is not a class or namespace.
Is there anything wrong in "json/writer.h" ?
Error:
My guess is that you are using the example test code from
https://finbarr.ca/jsoncpp-example/
The issue would be a Makefile problem in the ordering of the flags:
change Makefile from example to look like this:
CXX = g++
CPPFLAGS = -std=gnu++11
LDFLAGS = -L/{YOUR_PATH}/jsoncpp/build/debug/src/lib_json -ljsoncpp
INC = -I/{YOUR_PATH}/jsoncpp/include
main: main.cpp
$(CXX) -o main main.cpp $(CPPFLAGS) $(INC) $(LDFLAGS)
Fix your path, but make sure the order of flags is correct...
The way you are trying to access an enum (PrecisionType is an enum)i.e. PrecisionType::significantDigits is compliant to -std=c++11 and not to older ones
/** \brief Type of precision for formatting of real values.
*/
enum PrecisionType {
significantDigits = 0, ///< we set max number of significant digits in string
decimalPlaces ///< we set max number of digits after "." in string
};
Solution
If you are compiling with just g++ you can add the following g++ -std=c++11 <file-name>
If its a Makefile you need to edit your CPPFLAGS to CPPFLAGS= -std=c++11
Regards,

undefined reference while linking with a shared object

I'm a dumb newbie.
I've got a file named file.c with the functions my_putstr(char *) and my_strlen(char *)
my_putstr() writes the parameter with write() (unistd.h)
I wanted to create a library from file.c so I did :
gcc -fPIC -c file.c
gcc -shared -o libfile.so file.o
Then I created a main.c file and called my_putstr() from it.
I tried to compile and link my .so
gcc -L. -lfile main.c -o test
But I got an undefined reference to my_putstr()
I tried to create a .h with my_putstr() and my_strlen() in it, and include it to the main but I got the same error.
Sorry for stupid questions.
Havaniceday.
Your question suffers lack of information, but I can suggest you at first try
gcc main.c ./libfile.so -Wl,-rpath . -o test
If this will fail, you have something wrong with your sources.
If everything is ok at this point, then try
gcc main.c -L . -lfile -Wl,-rpath . -o test
If this will output undefined reference, then probably you already have something like libfile.a without my_putstr(may be from previous experiments) in your lib path.
If everything is ok with it, then your linker is sensible to order in which libraries is supplied to command string, and you must remember, then library always comes after object, that uses this library.

Multithreaded program C using multiple .cpp files

I have a multi threaded program in C that was working well but was in one single main.cpp file.
I have moved the thread in another .cpp file and added it's signature, void* displayScreen(void*); , in the header. I include the header in my initial main.cpp file.
Compiling works but the linker returns an error when trying to call pthread_create(): undefined reference to `displayScreen(void*)'
It looks like it compiled displayScreen(void *) fine but does not know where to load it from. Is there a way for me to tell the linker where to find it or am I doing it wrong please?
Thank you very much.
Adding the signature alone lets you compile the main translation unit, but you still have to compile the implementation of the function separately and link the two:
main.cpp
void* displayScreen(void*);
int main()
{
/* .... */
}
display.cpp
void* displayScreen(void*)
{
/* implementation */
}
Compile:
g++ -O2 -o main.o main.cpp
g++ -O2 -o display.o display.cpp
Link:
g++ -o myprogram main.o display.o -lpthread -s

Resources