Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to remove the SONAME #257

Closed
Rhialto opened this issue Dec 13, 2020 · 12 comments
Closed

Add option to remove the SONAME #257

Rhialto opened this issue Dec 13, 2020 · 12 comments

Comments

@Rhialto
Copy link

Rhialto commented Dec 13, 2020

Sometimes, search paths for libraries cause conflicts between incompatible libraries that have the same SONAME. I once ran into that when I installed multiple versions of g++ which used the same SONAME for the C++ library, but they were incompatible. For some programs, the wrong version was loaded, resulting in mysterious problems.

For cases like this, sometimes it isn't even enough to avoid system-global search paths, and even RPATH in an executable can still not solve this. In that case you want to record the absolute path to a library.

It turns out that current tooling already supports this. I looked in NetBSD's runtime linker and in the binutils compile time linker. You get a full path to a library recorded in the linker output, if the library has no SONAME.

So if you can remove a SONAME from a library, you can use this escape hatch.

I tried in the past to make shim libraries that have no SONAME of themselves, and link to only one other library, with only one directory in the RPATH. That works, but is more complicated.

@Rhialto
Copy link
Author

Rhialto commented Dec 13, 2020

There is some mailing list discussion about this, for example starting at http://mail-index.netbsd.org/pkgsrc-users/2020/07/30/msg031864.html and a bunch of "Next by Thread" links.

@Mic92
Copy link
Member

Mic92 commented Aug 10, 2021

Also related NixOS/nixpkgs#45105

@Mic92
Copy link
Member

Mic92 commented Aug 10, 2021

Have you tried to make soname an absolute path? I think this is what https://github.com/NixOS/nixpkgs/pull/45105/files tries on Linux.

@Rhialto
Copy link
Author

Rhialto commented Aug 10, 2021

Ah interesting idea! So if I understand right, the idea is to set the SONAME of a library to an absolute path. Then when that library is referenced (by the compile time linker), the output file will have a DT_NEEDED with that absolute path in it.

An interesting alternative approach to mine: a library with no SONAME will cause the referring file to get an absolute DT_NEEDED (but only if it is specified on the command line that way).

So I tried this on NetBSD (which has at least its own runtime linker, but the compiler and compile time linker are gnu or llvm).

#!/bin/sh

cat >hello.c <<\EOF
#include <stdio.h>
int main(int argc, char **argv) {
    printf("Hello, world!\n");
    return 0;
}
EOF

cp /lib/libc.so absolute.so
chmod +w absolute.so

patchelf --set-soname $PWD/absolute.so ./absolute.so

objdump -x absolute.so | grep SONAME

gcc -c hello.c -o hello.o
# Link without the standard libc but with other default objects
CRTEND="-Wl,--as-needed -lgcc_s -Wl,--no-as-needed -lgcc -Wl,--as-needed -lgcc_s -Wl,--no-as-needed -lgcc"
gcc -o hello -nodefaultlibs hello.o ./absolute.so $CRTEND

objdump -x hello | grep NEEDED

./hello

which had an interesting result:

$ ./doit
  SONAME               /tmp/x/absolute.so
  NEEDED               /tmp/x/absolute.so
/tmp/x/absolute.so: wrong number of segments (3 != 2)

where the last line is the result of attempting to run ./hello.

I'm not sure what that error entails exactly, but I noticed that there is some strange change caused by the patchelf --set-soname $PWD/absolute.so ./absolute.so command already:

$ ldd /lib/libc.so
/lib/libc.so:
$ ldd ./absolute.so 
ldd: ./absolute.so: invalid ELF class 2; expected 1

So it seems that patchelf changes more in the file than expected.

ETA: I found out why ldd gives the confusing error that it expects a 32-bit object but finds a 64-bit one. That is because ldd uses the dynamic linker to first load the module as 64 bits. That fails, because of the same error "wrong number of segments (3 != 2)". But that error is not printed (it is just set with _rtld_error()) , and it tries again to load it as a 32 bits module. Of course it isn't, and the discrepancy is printed.

The NetBSD ld.elf_so expects exactly 2 load segments, and apparently patchelf adds one.

	/*
         * Scan the program header entries, and save key information.
         *
         * We rely on there being exactly two load segments, text and data,
         * in that order.
         */

from /usr/src/libexec/ld.elf_so/map_object.c.

@Rhialto
Copy link
Author

Rhialto commented Aug 10, 2021

Oh, and if I overwrite the patched library with the original, hello runs (so the problem is in the library, not in the executable):

$ cp /lib/libc.so ./absolute.so 
overwrite ./absolute.so? y
$ ldd ./hello
./hello:
        /tmp/x/absolute.so => /tmp/x/absolute.so
$ ./hello
Hello, world!

All this on NetBSD/amd64 9.2.

@Rhialto
Copy link
Author

Rhialto commented Aug 10, 2021

Maybe issue #244 is related? I see changes in the order of program headers and sections, but the main change seems to be with .dynstr.

@genezx
Copy link

genezx commented Oct 11, 2021

+1. Please add the option to remove SONAME.

There are such use cases and I think it's quite common in production environment.

For example, I have a bunch of different versions of third-party .so files with all the same SONAME and RPATH in them, and each of them are not compatible with each other, and then I want to link to a certain version of .so file. So I have to remove the SONAME and RPATH to get a full path linkage to the certain .so file. patchelf has --remove-rpath now, and just lacks an option like --remove-soname.

.so filenames are subject to change. It's not a good idea to reset a bunch of SONAMEs to the full path each time when the filenames or directories change, because it's annoying, and another problem is that the .dynstr section may not have enough room to hold the change.

My current workaround:

  1. run patchelf --remove-rpath.
  2. edit .so file .dynamic section, change field 0xe to 0xf.
  3. run patchelf --remove-rpath again.

@fzakaria
Copy link
Contributor

fzakaria commented Dec 22, 2021

Hi!
I think we can close this issue as you can already remove SONAME with the replace functionality.

❯ patchelf --print-soname libsimple.so
libsimple.so.1.1

❯ patchelf --set-soname "" libsimple.so

❯ patchelf --print-soname libsimple.so

Another example:

❯ patchelf --set-soname "abc" libsimple.so

❯ readelf -a libsimple.so| grep SONAME
 0x000000000000000e (SONAME)             Library soname: [abc]

❯ patchelf --set-soname "" libsimple.so

❯ readelf -a libsimple.so| grep SONAME
 0x000000000000000e (SONAME)             Library soname: []

@Rhialto @genezx @Mic92 should we close?

@Mic92
Copy link
Member

Mic92 commented Dec 22, 2021

Sounds good to me.

@Mic92
Copy link
Member

Mic92 commented Dec 22, 2021

@Rhialto @genezx I can reopen, if the above does not fit your use case.

@Mic92 Mic92 closed this as completed Dec 22, 2021
@Rhialto
Copy link
Author

Rhialto commented Dec 23, 2021

@Mic92 It both does and doesn't, in a way :)

It does, in the sense that with the linker I tried, it seemed to have the same effect as a missing SONAME. The argument I gave to the linker was recorded verbatim in the DT_NEEDED record.

It doesn't, in the sense that this doesn't actually remove the record:

$ patchelf --set-soname "" ./absolute.so
$ patchelf --debug --print-soname ./absolute.so
DT_SONAME is empty

whereas I was expecting that debug("no DT_SONAME found\n"); would be reached.

@Mic92
Copy link
Member

Mic92 commented Dec 23, 2021

Mhm but truncating a string seems to me like the less invasive option because we don't actually need to remove any entries from the dynamic section. Any downsides?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants