0

The context is as follows: on my o.s. there is (already) liba.so.v2 of "liba" which contains the string 'symbol_version-2' as the symbol version for its functions , e.g. functionX@version-2 (in my case, for ex. wrefresh@NCURSES6_5.0.19991023 in objdump -T / readelf output). There is this new binary_a which I downloaded and want to run. This, however, has a dependency for a previous versions of the lib, liba.so.v1, which contains a different symbol version string, e.g. 'symbol_version-1' (in my case wrefresh@NCURSES_5.0.19991023 (notice the missing '6').

Notes:

  • The functions (all of them) exist in both so libs and the new lib is found by ld.so (checked that using LD_DEBUG=libs ldd ./binary_a )
  • The binary unfortunately doesn't have any reference to the 'new' symbol, otherwise, it would be easy to hexedit the binary , and adjust the vna_flags (see NewAppsOnOldGlibc link below)

My questions are :

  1. how to patch the binary such as to refer to the new symbol in the new lib - using either dedicated Tools (objcopy / elfedit, others ...?), or even hexedit. For the latter case I'd need please step-by-step advice on how to recalculate the hashes (if needed; according to link it seems it's needed). There seems to be enough details in dynTable-hash , however the information presented there is too complicated for me to make any practical use of for my use-case.
  2. Why is ld.so (still) complaining that "it cannot find version", after I've hexedit out all references to the old lib.so and the old symbol ? Is ld.so's cache somehow interferring ?

All my attempts led to ldd (actually ld.so) complaining after patching the file, that it cannot find the new symbol version...

The ideal solution would avoid (re)compiling stuff (i.e. the binary_a) , except maybe compiling a 'dummy' function like in previous link, would avoid using LD_PRELOADs variables.

What I've tried:

  • In my tests with patchelf, it produced a corrupt binary with flags --remove-needed liba.so.v2 ; using patchelf --replace-needed liba.so.v2 liba.so.v1 did rearrange the 'physical' layout of the file (as viewed with hexedit), but the symbols still referenced the v2 so lib, so I have doubts that this option worked as intended .To add more info: am hitting this with patchelf. The 'rearrangement' of the binary, seems it is referred to be patchelf --debug when among other lines it says: shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug
  • Testing with elfsh yielded another error, upon loading the binary, don't have it in front of me know, but somthing like: 'elf header AMD64-x64 not recognized'
  • When 'hexediting' **another ** binary, where the symbol versions had the same string length (not like in this case, where there is a one char difference in the version string length) - so, an easier case - doing ldd on the resulted binary still complained that "symbol_version-2" not found. I would imagine adding a new entry ("symbol_version-2" ) in the .dynstr Table is not feasible (i.e. without decompiling the binary), is it ? If that would be doable, then I'd simply hexedit (overwrite) in the vna_flags field of Elfxx_Vernaux entry corresponding to the 'old' symbol, to be the offset (in the .dynstr Table) of this new string, i.e. "symbol_version-2", again along the lines of NewAppsOnOldGlibc

Would following help - (a) changing the soname inside the lib.so.v2 file (using patchelf --set-soname or similar), (b) renaming the lib.so.v2 file to lib.so.v1 and (c) fiddling with the version name (string) only ? This would lead me again to asking: how would i trick ld.so into accepting my manual modification to the binary ?

Thanks in advance for your help !

cg79
  • 11
  • 1
    Isn’t the correct solution to install libncurses5 instead of trying to patch the binary? Current ncurses ships both libncurses5 and libncurses6 packages, and not just for the fun of it. – Stephen Kitt Jan 17 '22 at 09:47
  • This would be indeed the most strightforward solution, but in some cases there are external limitations imposed, e.g. each new installed package needs to be audited first by whomever, or there is no internet egress connectivity , plus, sometimes a handful more :) Plus, I presume some software vendors simply link against whichever is the latest version of the dependency .so file, without any other real reason (not focusing here on security fixes which is a valid case). – cg79 Jan 17 '22 at 10:07
  • Also, installing an older version of a lib (which has probably more bugs + security issues than a newer version) , just to satisfy a particular requirement of one older version - is relatively hard to describe as 'correct' solution.
    Also, if you could please provide more deep insight as to why (aside the "they're incompatible in some way*") there are several versions of the given ncurses lib (besides the 'not just for fun' reason) , it would be much more valueable than just stating the obvious I guess the, no offense - Thanks !
    – cg79 Jan 19 '22 at 15:03
  • Ah, I thought you were interested in the general discussion rather than ncurses specifics ;-). For ncurses specifically, it’s not an older version of the library, it’s the same version built with an older API and ABI (notably, with different underlying types for some important data types). See https://salsa.debian.org/debian/ncurses/-/blob/master/debian/rules for details. – Stephen Kitt Jan 19 '22 at 15:03
  • The underlying type difference in particular means that modifying a dependent binary to link to libncurses6 instead of libncurses5 is unlikely to produce a fully working result. – Stephen Kitt Jan 19 '22 at 15:06
  • These last 2 comments of yours definitely shed more light on the issue (in the general way, as well as for this specific case/lib). So, I guess, am I safe to assume these discrepancies in the ABI versions stemprobably from being compiled by newer versions of the compiler ? Or what other reasons could account for this ABI incompatibility - let's presume the piece of code (e.g. the specific functions needed from this lib) is identical between the 2 versions of the lib ? One reason might be changing f.e. uint8_t instead of unsigned char. Any other reasons ? Thanks – cg79 Jan 19 '22 at 15:14
  • The compiler doesn’t affect the ABI; there have been some cases in the past where G++ broke ABI on upgrades, but that was known in advance (and resulted in a ton of work in distributions). If a library changes its soname, that means that the ABI is changing in some way. That can be an innocuous change for many dependents, e.g. if a little-used function is removed (so a binary not using the old function could use the new library). – Stephen Kitt Jan 19 '22 at 15:20
  • In other cases though it can be a significant but non-obvious change, e.g if API types change in a source-compatible way — so the API doesn’t change, but the ABI does. As a general rule, if a library changes its soname, it’s explicitly because it can no longer be used with existing binaries. That’s also why there aren’t any nice tools to do what you’re asking in your question. – Stephen Kitt Jan 19 '22 at 15:21
  • 1
    Thank you, have a [much] better understanding now – cg79 Jan 19 '22 at 15:30

0 Answers0