GaloisInc / elf-edit

The elf-edit library provides a datatype suitable for reading and writing Elf files.
Other
36 stars 6 forks source link

`dynSymEntry` doesn't consult `VersionDef`s to find symbol versions #28

Closed RyanGlScott closed 2 years ago

RyanGlScott commented 2 years ago

Here is a test harness program which loads /lib/x86_64-linux-gnu/libz.so.1.2.11, retrieves its dynamic symbol table, and prints each symbol table entry along with its version information:

```hs {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# OPTIONS_GHC -Wall #-} module Main (main) where import Control.Monad ( guard ) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import qualified Data.Foldable as F import Data.Maybe ( listToMaybe ) import qualified Data.Vector as DV import Data.Word ( Word32 ) import qualified Data.ElfEdit as DE withElfHeader :: BS.ByteString -> (forall w . DE.ElfHeaderInfo w -> r) -> r withElfHeader bs f = case DE.decodeElfHeaderInfo bs of Left (_,err) -> error ("Failed to parse ELF file: " ++ show err) Right (DE.SomeElf e) -> f e elfDynamicSymbolTable :: DE.ElfHeader w -> BS.ByteString -> -- ^ File contents DV.Vector (DE.Shdr Word32 (DE.ElfWordType w)) -> -- ^ Section header table Maybe (DV.Vector (DE.SymtabEntry BS.ByteString (DE.ElfWordType w))) elfDynamicSymbolTable hdr contents shdrs = DE.elfClassInstances (DE.headerClass hdr) $ do guard (DE.headerType hdr == DE.ET_DYN) symtab <- case DV.toList $ DV.filter (\s -> DE.shdrType s == DE.SHT_DYNSYM) shdrs of [symtab] -> Just symtab _ -> Nothing let strtabIdx = DE.shdrLink symtab strtab <- shdrs DV.!? fromIntegral strtabIdx let shdrData shdr = DE.slice (DE.shdrFileRange shdr) contents let symtabData = shdrData symtab let strtabData = shdrData strtab case DE.decodeSymtab cl dta strtabData symtabData of Left _ -> Nothing Right entries -> Just entries where cl = DE.headerClass hdr dta = DE.headerData hdr main :: IO () main = do bs <- BS.readFile "/lib/x86_64-linux-gnu/libz.so.1.2.11" withElfHeader bs $ \hdrInfo -> do let hdr = DE.header hdrInfo let cl = DE.headerClass hdr let dta = DE.headerData hdr let (_, elf) = DE.getElf hdrInfo let elfPhdrs = DE.headerPhdrs hdrInfo let elfShdrs = DE.headerShdrs hdrInfo let elfBytes = DE.headerFileContents hdrInfo let mbRes = DE.elfClassInstances cl $ do entries <- elfDynamicSymbolTable hdr elfBytes elfShdrs vam <- DE.virtAddrMap elfBytes elfPhdrs rawDynSec <- listToMaybe $ DE.findSectionByName (BSC.pack ".dynamic") elf let dynBytes = DE.elfSectionData rawDynSec dynSec <- case DE.dynamicEntries dta (DE.headerClass hdr) dynBytes of Left _dynErr -> Nothing Right dynSec -> return dynSec verReqMap <- case DE.dynVersionReqMap dynSec vam of Left _dynErr -> Nothing Right vrm -> return vrm Just $ DV.imap (\symIdx ste -> ( DE.steName ste , case DE.dynSymEntry dynSec vam verReqMap (fromIntegral symIdx) of Left x -> show x Right (_, DE.VersionLocal) -> "VersionLocal" Right (_, DE.VersionGlobal) -> "VersionGlobal" Right (_, DE.VersionSpecific vi) -> "VersionSpecific: " ++ show vi )) entries F.for_ mbRes $ \res -> DV.forM_ res $ \(nm, ver) -> putStrLn $ BSC.unpack nm ++ ", " ++ ver ```

Unfortunately, GitHub won't let me attach /lib/x86_64-linux-gnu/libz.so.1.2.11, but what makes this particular .so file so interesting is its output:

``` , VersionLocal __snprintf_chk, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.3.4"} free, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __errno_location, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} _ITM_deregisterTMCloneTable, VersionLocal write, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} strlen, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __stack_chk_fail, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.4"} snprintf, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} memset, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} close, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} memchr, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} read, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __gmon_start__, VersionLocal memcpy, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.14"} malloc, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __vsnprintf_chk, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.3.4"} open, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} lseek64, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} _ITM_registerTMCloneTable, VersionLocal strerror, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __cxa_finalize, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} ZLIB_1.2.2, Symbol ZLIB_1.2.2 has unresolvable version requirement index 5. inflateEnd, VersionGlobal inflateInit2_, VersionGlobal crc32_z, Symbol crc32_z has unresolvable version requirement index 14. deflate, VersionGlobal deflateTune, Symbol deflateTune has unresolvable version requirement index 6. deflatePrime, Symbol deflatePrime has unresolvable version requirement index 4. gzerror, VersionGlobal inflateReset, VersionGlobal ZLIB_1.2.0.2, Symbol ZLIB_1.2.0.2 has unresolvable version requirement index 3. gztell, VersionGlobal gzflush, VersionGlobal inflateMark, Symbol inflateMark has unresolvable version requirement index 9. inflateSyncPoint, VersionGlobal ZLIB_1.2.9, Symbol ZLIB_1.2.9 has unresolvable version requirement index 14. adler32_z, Symbol adler32_z has unresolvable version requirement index 14. deflateSetHeader, Symbol deflateSetHeader has unresolvable version requirement index 5. ZLIB_1.2.0.8, Symbol ZLIB_1.2.0.8 has unresolvable version requirement index 4. inflateInit_, VersionGlobal inflateBackEnd, Symbol inflateBackEnd has unresolvable version requirement index 2. gzbuffer, Symbol gzbuffer has unresolvable version requirement index 10. deflateGetDictionary, Symbol deflateGetDictionary has unresolvable version requirement index 14. adler32, VersionGlobal gzseek, VersionGlobal gzfread, Symbol gzfread has unresolvable version requirement index 14. ZLIB_1.2.5.1, Symbol ZLIB_1.2.5.1 has unresolvable version requirement index 11. deflateResetKeep, Symbol deflateResetKeep has unresolvable version requirement index 12. ZLIB_1.2.5.2, Symbol ZLIB_1.2.5.2 has unresolvable version requirement index 12. crc32, VersionGlobal zError, VersionGlobal gzfwrite, Symbol gzfwrite has unresolvable version requirement index 14. gzread, VersionGlobal deflateCopy, VersionGlobal inflateGetHeader, Symbol inflateGetHeader has unresolvable version requirement index 5. gzputc, VersionGlobal gzgetc, VersionGlobal gzwrite, VersionGlobal gzvprintf, Symbol gzvprintf has unresolvable version requirement index 13. deflateReset, VersionGlobal gzeof, VersionGlobal inflate, VersionGlobal gzopen64, Symbol gzopen64 has unresolvable version requirement index 8. inflateReset2, Symbol inflateReset2 has unresolvable version requirement index 9. crc32_combine64, Symbol crc32_combine64 has unresolvable version requirement index 8. deflateInit_, VersionGlobal zlibCompileFlags, Symbol zlibCompileFlags has unresolvable version requirement index 3. gzdirect, Symbol gzdirect has unresolvable version requirement index 6. ZLIB_1.2.2.3, Symbol ZLIB_1.2.2.3 has unresolvable version requirement index 6. deflateInit2_, VersionGlobal deflatePending, Symbol deflatePending has unresolvable version requirement index 11. ZLIB_1.2.2.4, Symbol ZLIB_1.2.2.4 has unresolvable version requirement index 7. gzseek64, Symbol gzseek64 has unresolvable version requirement index 8. gzputs, VersionGlobal gzgets, VersionGlobal inflateResetKeep, Symbol inflateResetKeep has unresolvable version requirement index 12. gzoffset64, Symbol gzoffset64 has unresolvable version requirement index 10. compressBound, Symbol compressBound has unresolvable version requirement index 2. deflateParams, VersionGlobal inflateGetDictionary, Symbol inflateGetDictionary has unresolvable version requirement index 13. get_crc_table, VersionGlobal ZLIB_1.2.7.1, Symbol ZLIB_1.2.7.1 has unresolvable version requirement index 13. inflateBack, Symbol inflateBack has unresolvable version requirement index 2. gzprintf, VersionGlobal inflateValidate, Symbol inflateValidate has unresolvable version requirement index 14. inflateSetDictionary, VersionGlobal inflatePrime, Symbol inflatePrime has unresolvable version requirement index 7. adler32_combine, Symbol adler32_combine has unresolvable version requirement index 5. gzrewind, VersionGlobal gzclose, VersionGlobal gzclose_r, Symbol gzclose_r has unresolvable version requirement index 10. gzdopen, VersionGlobal zlibVersion, VersionGlobal gzclearerr, Symbol gzclearerr has unresolvable version requirement index 3. inflateBackInit_, Symbol inflateBackInit_ has unresolvable version requirement index 2. ZLIB_1.2.3.3, Symbol ZLIB_1.2.3.3 has unresolvable version requirement index 8. adler32_combine64, Symbol adler32_combine64 has unresolvable version requirement index 8. compress, VersionGlobal gzclose_w, Symbol gzclose_w has unresolvable version requirement index 10. ZLIB_1.2.3.4, Symbol ZLIB_1.2.3.4 has unresolvable version requirement index 9. ZLIB_1.2.3.5, Symbol ZLIB_1.2.3.5 has unresolvable version requirement index 10. gzopen, VersionGlobal deflateBound, Symbol deflateBound has unresolvable version requirement index 2. compress2, VersionGlobal gztell64, Symbol gztell64 has unresolvable version requirement index 8. uncompress, VersionGlobal gzoffset, Symbol gzoffset has unresolvable version requirement index 10. deflateSetDictionary, VersionGlobal inflateCopy, Symbol inflateCopy has unresolvable version requirement index 2. gzgetc_, Symbol gzgetc_ has unresolvable version requirement index 12. deflateEnd, VersionGlobal crc32_combine, Symbol crc32_combine has unresolvable version requirement index 5. inflateCodesUsed, Symbol inflateCodesUsed has unresolvable version requirement index 14. gzsetparams, VersionGlobal gzungetc, Symbol gzungetc has unresolvable version requirement index 3. uncompress2, Symbol uncompress2 has unresolvable version requirement index 14. inflateUndermine, Symbol inflateUndermine has unresolvable version requirement index 8. ZLIB_1.2.0, Symbol ZLIB_1.2.0 has unresolvable version requirement index 2. inflateSync, VersionGlobal ```

There are quite a few results where elf-edit claims that a symbol has an unresolvable version requirement index. readelf, on the other hand, disagrees with this assessment, as it is able to find version numbers for each of these problematic symbols:

``` $ readelf --dyn-syms /lib/x86_64-linux-gnu/libz.so.1.2.11 Symbol table '.dynsym' contains 120 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __snprintf_chk@GLIBC_2.3.4 (15) 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.2.5 (16) 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (16) 4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND write@GLIBC_2.2.5 (16) 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.2.5 (16) 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (17) 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND snprintf@GLIBC_2.2.5 (16) 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@GLIBC_2.2.5 (16) 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND close@GLIBC_2.2.5 (16) 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memchr@GLIBC_2.2.5 (16) 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND read@GLIBC_2.2.5 (16) 13: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (18) 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc@GLIBC_2.2.5 (16) 16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __vsnprintf_chk@GLIBC_2.3.4 (15) 17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND open@GLIBC_2.2.5 (16) 18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND lseek64@GLIBC_2.2.5 (16) 19: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable 20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strerror@GLIBC_2.2.5 (16) 21: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (16) 22: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.2 23: 000000000000c1e0 154 FUNC GLOBAL DEFAULT 15 inflateEnd 24: 0000000000009c80 240 FUNC GLOBAL DEFAULT 15 inflateInit2_ 25: 0000000000003280 12 IFUNC GLOBAL DEFAULT 15 crc32_z@@ZLIB_1.2.9 26: 0000000000005800 6240 FUNC GLOBAL DEFAULT 15 deflate 27: 0000000000005550 170 FUNC GLOBAL DEFAULT 15 deflateTune@@ZLIB_1.2.2.3 28: 0000000000005430 282 FUNC GLOBAL DEFAULT 15 deflatePrime@@ZLIB_1.2.0.8 29: 0000000000010cf0 83 FUNC GLOBAL DEFAULT 15 gzerror 30: 0000000000009b40 82 FUNC GLOBAL DEFAULT 15 inflateReset 31: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.0.2 32: 0000000000010c50 9 FUNC GLOBAL DEFAULT 15 gztell 33: 0000000000012b80 237 FUNC GLOBAL DEFAULT 15 gzflush 34: 000000000000ccd0 135 FUNC GLOBAL DEFAULT 15 inflateMark@@ZLIB_1.2.3.4 35: 000000000000c8c0 94 FUNC GLOBAL DEFAULT 15 inflateSyncPoint 36: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.9 37: 0000000000002340 1752 FUNC GLOBAL DEFAULT 15 adler32_z@@ZLIB_1.2.9 38: 00000000000052f0 138 FUNC GLOBAL DEFAULT 15 deflateSetHeader@@ZLIB_1.2.2 39: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.0.8 40: 0000000000009d70 19 FUNC GLOBAL DEFAULT 15 inflateInit_ 41: 0000000000008e00 62 FUNC GLOBAL DEFAULT 15 inflateBackEnd@@ZLIB_1.2.0 42: 0000000000010950 70 FUNC GLOBAL DEFAULT 15 gzbuffer@@ZLIB_1.2.3.5 43: 0000000000004fd0 240 FUNC GLOBAL DEFAULT 15 deflateGetDictionary@@ZLIB_1.2.9 44: 0000000000002a20 11 FUNC GLOBAL DEFAULT 15 adler32 45: 0000000000010c00 9 FUNC GLOBAL DEFAULT 15 gzseek 46: 00000000000118d0 292 FUNC GLOBAL DEFAULT 15 gzfread@@ZLIB_1.2.9 47: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.5.1 48: 00000000000050c0 310 FUNC GLOBAL DEFAULT 15 deflateResetKeep@@ZLIB_1.2.5.2 49: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.5.2 50: 0000000000003290 11 FUNC GLOBAL DEFAULT 15 crc32 51: 0000000000010180 25 FUNC GLOBAL DEFAULT 15 zError 52: 0000000000012640 129 FUNC GLOBAL DEFAULT 15 gzfwrite@@ZLIB_1.2.9 53: 0000000000011710 444 FUNC GLOBAL DEFAULT 15 gzread 54: 0000000000007760 646 FUNC GLOBAL DEFAULT 15 deflateCopy 55: 000000000000c550 106 FUNC GLOBAL DEFAULT 15 inflateGetHeader@@ZLIB_1.2.2 56: 00000000000126d0 391 FUNC GLOBAL DEFAULT 15 gzputc 57: 0000000000011a00 195 FUNC GLOBAL DEFAULT 15 gzgetc 58: 00000000000125d0 99 FUNC GLOBAL DEFAULT 15 gzwrite 59: 00000000000128c0 507 FUNC GLOBAL DEFAULT 15 gzvprintf@@ZLIB_1.2.7.1 60: 0000000000005200 227 FUNC GLOBAL DEFAULT 15 deflateReset 61: 0000000000010cd0 28 FUNC GLOBAL DEFAULT 15 gzeof 62: 0000000000009e90 9032 FUNC GLOBAL DEFAULT 15 inflate 63: 00000000000108b0 17 FUNC GLOBAL DEFAULT 15 gzopen64@@ZLIB_1.2.3.3 64: 0000000000009ba0 214 FUNC GLOBAL DEFAULT 15 inflateReset2@@ZLIB_1.2.3.4 65: 00000000000032b0 9 FUNC GLOBAL DEFAULT 15 crc32_combine64@@ZLIB_1.2.3.3 66: 0000000000007730 39 FUNC GLOBAL DEFAULT 15 deflateInit_ 67: 0000000000010170 10 FUNC GLOBAL DEFAULT 15 zlibCompileFlags@@ZLIB_1.2.0.2 68: 0000000000011d90 83 FUNC GLOBAL DEFAULT 15 gzdirect@@ZLIB_1.2.2.3 69: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.2.3 70: 0000000000007430 764 FUNC GLOBAL DEFAULT 15 deflateInit2_ 71: 0000000000005380 161 FUNC GLOBAL DEFAULT 15 deflatePending@@ZLIB_1.2.5.1 72: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.2.4 73: 0000000000010a60 402 FUNC GLOBAL DEFAULT 15 gzseek64@@ZLIB_1.2.3.3 74: 0000000000012860 96 FUNC GLOBAL DEFAULT 15 gzputs 75: 0000000000011c20 360 FUNC GLOBAL DEFAULT 15 gzgets 76: 0000000000009a50 234 FUNC GLOBAL DEFAULT 15 inflateResetKeep@@ZLIB_1.2.5.2 77: 0000000000010c60 94 FUNC GLOBAL DEFAULT 15 gzoffset64@@ZLIB_1.2.3.5 78: 0000000000010310 34 FUNC GLOBAL DEFAULT 15 compressBound@@ZLIB_1.2.0 79: 0000000000007060 665 FUNC GLOBAL DEFAULT 15 deflateParams 80: 000000000000c280 168 FUNC GLOBAL DEFAULT 15 inflateGetDictionary@@ZLIB_1.2.7.1 81: 0000000000003270 12 FUNC GLOBAL DEFAULT 15 get_crc_table 82: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.7.1 83: 0000000000007ae0 4882 FUNC GLOBAL DEFAULT 15 inflateBack@@ZLIB_1.2.0 84: 0000000000012ac0 181 FUNC GLOBAL DEFAULT 15 gzprintf 85: 000000000000cc50 117 FUNC GLOBAL DEFAULT 15 inflateValidate@@ZLIB_1.2.9 86: 000000000000c330 540 FUNC GLOBAL DEFAULT 15 inflateSetDictionary 87: 0000000000009d90 146 FUNC GLOBAL DEFAULT 15 inflatePrime@@ZLIB_1.2.2.4 88: 0000000000002a30 228 FUNC GLOBAL DEFAULT 15 adler32_combine@@ZLIB_1.2.2 89: 00000000000109a0 187 FUNC GLOBAL DEFAULT 15 gzrewind 90: 0000000000010520 43 FUNC GLOBAL DEFAULT 15 gzclose 91: 0000000000011df0 152 FUNC GLOBAL DEFAULT 15 gzclose_r@@ZLIB_1.2.3.5 92: 00000000000108d0 124 FUNC GLOBAL DEFAULT 15 gzdopen 93: 0000000000010160 12 FUNC GLOBAL DEFAULT 15 zlibVersion 94: 0000000000010d50 113 FUNC GLOBAL DEFAULT 15 gzclearerr@@ZLIB_1.2.0.2 95: 00000000000079f0 238 FUNC GLOBAL DEFAULT 15 inflateBackInit_@@ZLIB_1.2.0 96: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.3.3 97: 0000000000002b20 228 FUNC GLOBAL DEFAULT 15 adler32_combine64@@ZLIB_1.2.3.3 98: 0000000000010300 15 FUNC GLOBAL DEFAULT 15 compress 99: 0000000000012dd0 363 FUNC GLOBAL DEFAULT 15 gzclose_w@@ZLIB_1.2.3.5 100: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.3.4 101: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.3.5 102: 0000000000010890 17 FUNC GLOBAL DEFAULT 15 gzopen 103: 0000000000005600 376 FUNC GLOBAL DEFAULT 15 deflateBound@@ZLIB_1.2.0 104: 00000000000101c0 317 FUNC GLOBAL DEFAULT 15 compress2 105: 0000000000010c10 56 FUNC GLOBAL DEFAULT 15 gztell64@@ZLIB_1.2.3.3 106: 0000000000010500 28 FUNC GLOBAL DEFAULT 15 uncompress 107: 0000000000010cc0 9 FUNC GLOBAL DEFAULT 15 gzoffset@@ZLIB_1.2.3.5 108: 0000000000004d10 696 FUNC GLOBAL DEFAULT 15 deflateSetDictionary 109: 000000000000c920 709 FUNC GLOBAL DEFAULT 15 inflateCopy@@ZLIB_1.2.0 110: 0000000000011ad0 9 FUNC GLOBAL DEFAULT 15 gzgetc_@@ZLIB_1.2.5.2 111: 0000000000007300 302 FUNC GLOBAL DEFAULT 15 deflateEnd 112: 00000000000032a0 9 FUNC GLOBAL DEFAULT 15 crc32_combine@@ZLIB_1.2.2 113: 000000000000cd60 104 FUNC GLOBAL DEFAULT 15 inflateCodesUsed@@ZLIB_1.2.9 114: 0000000000012c70 347 FUNC GLOBAL DEFAULT 15 gzsetparams 115: 0000000000011ae0 318 FUNC GLOBAL DEFAULT 15 gzungetc@@ZLIB_1.2.0.2 116: 0000000000010340 448 FUNC GLOBAL DEFAULT 15 uncompress2@@ZLIB_1.2.9 117: 000000000000cbf0 86 FUNC GLOBAL DEFAULT 15 inflateUndermine@@ZLIB_1.2.3.3 118: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS ZLIB_1.2.0 119: 000000000000c5c0 768 FUNC GLOBAL DEFAULT 15 inflateSync ```

What accounts for this discrepancy? I believe it has to do with the way that dynSymEntry is implemented. When looking up symbol versions, it will only consult the required symbol version definitions in the .gnu.version_r section (by way of its VersionReqMap argument). /lib/x86_64-linux-gnu/libz.so.1.2.11, on the other hand, doesn't just have a .gnu.version_r section—it also has symbol version definitions in a .gnu.version_d section. (See here for a more in-depth explanation of .gnu.version_d and .gnu.version_r.)

In this example, the version information we need lives in the .gnu_version_d section. As a proof of concept, if you tweak this program to consult .gnu.version_d:

```diff $ diff -ru Bug.hs Bug2.hs --- Bug.hs 2022-04-14 21:36:51.096314470 -0400 +++ Bug2.hs 2022-04-14 21:35:37.867868893 -0400 @@ -8,9 +8,11 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import qualified Data.Foldable as F +import qualified Data.List as L +import qualified Data.Map as Map import Data.Maybe ( listToMaybe ) import qualified Data.Vector as DV -import Data.Word ( Word32 ) +import Data.Word ( Word16, Word32 ) import qualified Data.ElfEdit as DE @@ -67,8 +69,18 @@ verReqMap <- case DE.dynVersionReqMap dynSec vam of Left _dynErr -> Nothing Right vrm -> return vrm + verDefs <- case DE.dynVersionDefs dynSec vam of + Left _dynErr -> Nothing + Right defs -> return defs + verDefMap <- case versionDefMap verDefs of + Left _dynErr -> Nothing + Right vdm -> return vdm Just $ DV.imap (\symIdx ste -> ( DE.steName ste , case DE.dynSymEntry dynSec vam verReqMap (fromIntegral symIdx) of + Left x@(DE.UnresolvedVersionReqAuxIndex _ idx) -> + case Map.lookup idx verDefMap of + Just vi -> "VersionSpecific: " ++ show vi + Nothing -> show x Left x -> show x Right (_, DE.VersionLocal) -> "VersionLocal" Right (_, DE.VersionGlobal) -> "VersionGlobal" @@ -77,3 +89,32 @@ F.for_ mbRes $ \res -> DV.forM_ res $ \(nm, ver) -> putStrLn $ BSC.unpack nm ++ ", " ++ ver + +type VersionDefMap = Map.Map Word16 DE.VersionId + +versionDefMap :: [DE.VersionDef] -> Either DE.DynamicError VersionDefMap +versionDefMap [] = Right Map.empty +versionDefMap defs = do + m0 <- F.foldlM insVersionDef Map.empty defs + base_def <- + case L.find (\def -> DE.vd_flags def == DE.ver_flg_base) defs of + Just def -> Right def + Nothing -> Left $ error "TODO RGS" + file <- + case Map.lookup (DE.vd_ndx base_def) m0 of + Just def -> Right $ DE.vd_string def + Nothing -> Left $ error "TODO RGS" + Right $ fmap (\def -> DE.VersionId { DE.verFile = file + , DE.verName = DE.vd_string def + }) + m0 + where + insVersionDef :: + Map.Map Word16 DE.VersionDef -> + DE.VersionDef -> + Either DE.DynamicError (Map.Map Word16 DE.VersionDef) + insVersionDef m def = + let ndx = DE.vd_ndx def in + case Map.lookup ndx m of + Nothing -> Right $! Map.insert ndx def m + Just{} -> Left $! DE.DupVersionReqAuxIndex ndx ```

Then it will successfully discover versions for all symbols:

``` , VersionLocal __snprintf_chk, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.3.4"} free, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __errno_location, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} _ITM_deregisterTMCloneTable, VersionLocal write, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} strlen, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __stack_chk_fail, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.4"} snprintf, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} memset, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} close, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} memchr, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} read, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __gmon_start__, VersionLocal memcpy, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.14"} malloc, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __vsnprintf_chk, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.3.4"} open, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} lseek64, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} _ITM_registerTMCloneTable, VersionLocal strerror, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} __cxa_finalize, VersionSpecific: VersionId {verFile = "libc.so.6", verName = "GLIBC_2.2.5"} ZLIB_1.2.2, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2"} inflateEnd, VersionGlobal inflateInit2_, VersionGlobal crc32_z, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} deflate, VersionGlobal deflateTune, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2.3"} deflatePrime, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.8"} gzerror, VersionGlobal inflateReset, VersionGlobal ZLIB_1.2.0.2, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.2"} gztell, VersionGlobal gzflush, VersionGlobal inflateMark, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.4"} inflateSyncPoint, VersionGlobal ZLIB_1.2.9, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} adler32_z, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} deflateSetHeader, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2"} ZLIB_1.2.0.8, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.8"} inflateInit_, VersionGlobal inflateBackEnd, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} gzbuffer, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} deflateGetDictionary, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} adler32, VersionGlobal gzseek, VersionGlobal gzfread, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} ZLIB_1.2.5.1, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.1"} deflateResetKeep, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.2"} ZLIB_1.2.5.2, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.2"} crc32, VersionGlobal zError, VersionGlobal gzfwrite, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} gzread, VersionGlobal deflateCopy, VersionGlobal inflateGetHeader, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2"} gzputc, VersionGlobal gzgetc, VersionGlobal gzwrite, VersionGlobal gzvprintf, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.7.1"} deflateReset, VersionGlobal gzeof, VersionGlobal inflate, VersionGlobal gzopen64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} inflateReset2, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.4"} crc32_combine64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} deflateInit_, VersionGlobal zlibCompileFlags, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.2"} gzdirect, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2.3"} ZLIB_1.2.2.3, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2.3"} deflateInit2_, VersionGlobal deflatePending, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.1"} ZLIB_1.2.2.4, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2.4"} gzseek64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} gzputs, VersionGlobal gzgets, VersionGlobal inflateResetKeep, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.2"} gzoffset64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} compressBound, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} deflateParams, VersionGlobal inflateGetDictionary, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.7.1"} get_crc_table, VersionGlobal ZLIB_1.2.7.1, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.7.1"} inflateBack, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} gzprintf, VersionGlobal inflateValidate, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} inflateSetDictionary, VersionGlobal inflatePrime, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2.4"} adler32_combine, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2"} gzrewind, VersionGlobal gzclose, VersionGlobal gzclose_r, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} gzdopen, VersionGlobal zlibVersion, VersionGlobal gzclearerr, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.2"} inflateBackInit_, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} ZLIB_1.2.3.3, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} adler32_combine64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} compress, VersionGlobal gzclose_w, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} ZLIB_1.2.3.4, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.4"} ZLIB_1.2.3.5, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} gzopen, VersionGlobal deflateBound, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} compress2, VersionGlobal gztell64, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} uncompress, VersionGlobal gzoffset, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.5"} deflateSetDictionary, VersionGlobal inflateCopy, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} gzgetc_, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.5.2"} deflateEnd, VersionGlobal crc32_combine, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.2"} inflateCodesUsed, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} gzsetparams, VersionGlobal gzungetc, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0.2"} uncompress2, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.9"} inflateUndermine, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.3.3"} ZLIB_1.2.0, VersionSpecific: VersionId {verFile = "libz.so.1", verName = "ZLIB_1.2.0"} inflateSync, VersionGlobal ```

I propose that elf-edit take .gnu.version_d into account somehow in dynSymEntry. I'm not entirely sure whether it's more correct to first look in .gnu.version_r and then .gnu.version_d or vice versa.

RyanGlScott commented 2 years ago

A smaller example, courtesy of this blog post:

$ gcc foo.2.c -Wl,--version-script=symver.2.map -fpic -o libfoo.2.so -Wl,-soname,libfoo.so -shared
$ readelf --dyn-syms libfoo.2.so 

Symbol table '.dynsym' contains 9 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_finalize
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000001102    10 FUNC    GLOBAL DEFAULT   11 foo@MYSTUFF_1.1
     6: 00000000000010f5    13 FUNC    GLOBAL DEFAULT   11 foo@@MYSTUFF_1.2
     7: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS MYSTUFF_1.1
     8: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS MYSTUFF_1.2

Notice the two foo symbols, both of which live in .gnu.version_d. If you replace /lib/x86_64-linux-gnu/libz.so.1.2.11 with libfoo.2.so in the test harness above, you'll get:

, VersionLocal
__cxa_finalize, VersionLocal
_ITM_registerTMCloneTable, VersionLocal
_ITM_deregisterTMCloneTable, VersionLocal
__gmon_start__, VersionLocal
foo, Symbol foo has unresolvable version requirement index 32771.
foo, Symbol foo has unresolvable version requirement index 2.
MYSTUFF_1.1, Symbol MYSTUFF_1.1 has unresolvable version requirement index 3.
MYSTUFF_1.2, Symbol MYSTUFF_1.2 has unresolvable version requirement index 2.

Unfortunately, this example reveals that my proposed fix above is inadequate, since it will find the version for the latter foo but not the former:

, VersionLocal
__cxa_finalize, VersionLocal
_ITM_registerTMCloneTable, VersionLocal
_ITM_deregisterTMCloneTable, VersionLocal
__gmon_start__, VersionLocal
foo, Symbol foo has unresolvable version requirement index 32771.
foo, VersionSpecific: VersionId {verFile = "libfoo.so", verName = "MYSTUFF_1.2"}
MYSTUFF_1.1, VersionSpecific: VersionId {verFile = "libfoo.so", verName = "MYSTUFF_1.1"}
MYSTUFF_1.2, VersionSpecific: VersionId {verFile = "libfoo.so", verName = "MYSTUFF_1.2"}

I'm entirely unclear on how index 32771 arises—more investigation is required.

RyanGlScott commented 2 years ago

Argh. It turns out that 32771 isn't really the index. Rather, it's the bitwise-ORed combination of 3 (the actual index we want) and VERSYM_VERSION, or 0x7fff. It turns out that there is a GNU extension that allows hidden symbols, which you can test by masking off the bits in VERSYM_HIDDEN. Sure enough, 32771 is 0x8003 is hexadecimal, so this is a hidden symbol. (Finding documentation for this was... challenging.)

In order to fix this, then, I think we'd need to mask off VERSYM_VERSION in the implementation of dynSymEntry.