tomoakin / RPostgreSQL

Automatically exported from code.google.com/p/rpostgresql
64 stars 20 forks source link

Bug linker flag when build with/link to exist postgresql library in custom path #108

Open lguangyu opened 5 years ago

lguangyu commented 5 years ago

Hi All,

In short words the configure script may result in illed src/Makevars file with an excessive, incorrectly used -L flag. This confuses ld and the result shared library will not be linked into libpq.so, causing unresolved symbol error when library is loaded.

Below is the situation I find this issue:

I'm building this on a cluster server which I don't have root privilege. dplyr asks RPostgreSQL but the default building process fails (missing libpq-fe.h); Very interestingly, I see the packages contains libpq in the source directory, but it somewhat not attempted to build with that and simply asks for an existing build. Anyway, I tried my best to build the package against an existing PostgreSQL build (manually built from source). Though eventually I made it through (I had fair experience on the gmake system), here is the very painful process:

The goal is to build against the existing build then use -rpath to force search for libpq.so in a non-standard path (I don't want to contaminate the LD_LIBRARY_PATH and also don't want to do export LD_LIBRARY_PATH each time I run this package).

1. Try install.packages with some control over configure:

install.packages("RPostgreSQL", configure.vars="CFLAGS='-I<postgresql_prefix>/include' LDFLAGS='-Wl,-rpath,<postresql_prefix>/lib,-rpath,<gcc_prefix>/lib64'")

It fails again with same error if I didn't specify the CFLAGS at all. (Note, according to the cluster management the gcc I used is not in the standard path either; so I also have to specify the rpath of gcc runtime if I don't want to export it forever.)

2. Manual build

Then I realized some fine control over the building process is necessary. So I manually downloaded the package and untar'd it. Run with command

R CMD INSTALL --preclean --configure-vars="CFLAGS='-I<postgresql_prefix>/include' LDFLAGS='-Wl,-rpath,<postresql_prefix>/lib,-rpath,<gcc_prefix>/lib64'" .

It failed identically as above. It seems CFLAGS and LDFLAGS were not passed to R CMD SHLIB call, and in case if I did write something wrong R doesn't even want to complain explicitly about it as it is supposed to (like python, for example).

3. Using global Makevars instead of per-package configure

I had to try putting CFLAGS and LDFLAGS into ~/.R/Makevars, then do

R CMD INSTALL --preclean --no-clean-on-error .

It finally worked on compilation and linking, but failed the load test:

** testing if installed package can be loaded
Error: package or namespace load failed for ‘RPostgreSQL’ in dyn.load(file, DLLpath = DLLpath, ...):
 unable to load shared object '/home/li.gua/usr/R/RPostgreSQL/libs/RPostgreSQL.so':
  /home/li.gua/usr/R/RPostgreSQL/libs/RPostgreSQL.so: undefined symbol: PQfmod
Error: loading failed
Execution halted
ERROR: loading failed
****

This is quite a surprise when linking as .so succeeded while leaving symbols unresolved. Using ldd revealed the result RPostgreSQL.so does not link to libpq.so:

$ ldd src/RPostgreSQL.so 
    linux-vdso.so.1 =>  (0x00007ffe18982000)
    libR.so => not found
    libc.so.6 => /lib64/libc.so.6 (0x00002abed66fe000)
    /lib64/ld-linux-x86-64.so.2 (0x00002abed62c9000)

4. Re-call linking, identify the fault

I then examined link call made by R CMD SHLIB:

gcc -std=gnu99 -shared -L/shared/centos7/r-project/3.5.3/lib64/R/lib -L/home/li.gua/usr/postgresql/11.4-minimal/lib -Wl,-rpath,/home/li.gua/usr/postgresql/11.4-minimal/lib,-rpath,/shared/centos7/gcc/7.2.0/lib64 -o RPostgreSQL.so RS-DBI.o RS-PQescape.o RS-PostgreSQL.o RS-pgsql-copy.o RS-pgsql-getResult.o RS-pgsql-pqexec.o RS-pgsql-pqexecparams.o -L -lpq -L/shared/centos7/r-project/3.5.3/lib64/R/lib -lR

Noticed the weird bare -L flag shown in the above command. I manually removed that and re-executed the command, the result RPostgreSQL.so now had below ldd output:

 $ldd src/RPostgreSQL.so 
    linux-vdso.so.1 =>  (0x00007ffdf14b0000)
    libpq.so.5 => /home/li.gua/usr/postgresql/11.4-minimal/lib/libpq.so.5 (0x00002b1b61edf000)
    libR.so => not found
    libc.so.6 => /lib64/libc.so.6 (0x00002b1b6212f000)
    libssl.so.10 => /lib64/libssl.so.10 (0x00002b1b624fc000)
    libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00002b1b6276e000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b1b62bcf000)
    /lib64/ld-linux-x86-64.so.2 (0x00002b1b61aaa000)
    libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00002b1b62deb000)
    libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00002b1b63038000)
    libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00002b1b63320000)
    libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00002b1b63524000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00002b1b63757000)
    libz.so.1 => /home/li.gua/.local/lib/libz.so.1 (0x00002b1b6395b000)
    libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00002b1b63b78000)
    libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00002b1b63d86000)
    libresolv.so.2 => /lib64/libresolv.so.2 (0x00002b1b63f8a000)
    libselinux.so.1 => /lib64/libselinux.so.1 (0x00002b1b641a3000)
    libpcre.so.1 => /lib64/libpcre.so.1 (0x00002b1b643ca000)

At this point I know it has to be OK. Copy this library to replace the ill version fixed the loading test failure as expected:

$ R -e 'library("RPostgreSQL")'

R version 3.5.3 (2019-03-11) -- "Great Truth"
Copyright (C) 2019 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> library("RPostgreSQL")
Loading required package: DBI
> 
> 

Investigation of the files generated by configure revealed the faulty -L flag residing in src/Makevars. This has to be some issue with improperly designed configure process.

$ cat src/Makevars
PKG_CPPFLAGS=-Isrc/libpq
PKG_LIBS=-L -lpq
R_OS_TYPE=
matt-wirtz commented 3 years ago

Hello lguangyu,

thanks for posting your investigations so detailed since I'm running into the same problem on OpenSUSE Leap 15.2 with R 4.0.

Up until point 4 I can follow your description but then I don't understand how you examined the link call made by R CMD SHLIB and more importantly where you removed the bare -L flag. Should I modify the src/Makevars file and remove the "-L" in the PKG_LIBS assignment?

Thanks for the help.

matt-wirtz commented 3 years ago

Oh, when looking again at the output of the R CMD INTSALL command I can see that the link call mentioned in step 4 is included in this output on the console. So just copy and past from there and removing the bare -L flag worked. Then ldd RPostgreSQL.so shows that libpq.so.5 has been picked up.

One additional info: When installing packages which fail during the build or test load the package is removed again. In order to keep the RPotgreSQL package even if the test load fails, I used in Rstudio:

install.packages('RPostgreSQL', INSTALL_opts=c("--no-test-load))

and then replaced the RPostgreSQL.so.

tomoakin commented 3 years ago

The included libpq is configured only for windows and macos. In Linux systems, you should be able to compile PostgreSQL and setup such that pg_config in the path identify the appropriate header/library locations, somewhere under your HOME directory if you don't have the root privilege.