From c38aa8169e19355136fd834f71a5562a523d633c Mon Sep 17 00:00:00 2001 From: Armando Montanez Date: Thu, 24 Jan 2019 22:39:21 +0000 Subject: [PATCH] [elfabi] Add support for reading dynamic symbols from binaries This patch adds initial support for reading dynamic symbols from ELF binaries. Currently, STT_NOTYPE, STT_OBJECT, STT_FUNC, and STT_TLS are explicitly supported. Other symbol types are mapped to ELFSymbolType::Unknown to improve signal/noise ratio. Symbols must meet two criteria to be read into in an ELFStub: - The symbol's binding must be STB_GLOBAL or STB_WEAK. - The symbol's visibility must be STV_DEFAULT or STV_PROTECTED. This filters out symbols that aren't of interest during compile-time linking against a shared object. This change uses DT_HASH and DT_GNU_HASH to determine the size of .dynsym. Using hash tables to determine the number of symbols in .dynsym allows llvm-elfabi to work on binaries without relying on section headers. Differential Revision: https://reviews.llvm.org/D56031 llvm-svn: 352121 --- test/tools/llvm-elfabi/Inputs/gnu_hash.so | Bin 0 -> 16088 bytes test/tools/llvm-elfabi/Inputs/sysv_hash.so | Bin 0 -> 16088 bytes .../llvm-elfabi/binary-read-add-soname.test | 5 +- test/tools/llvm-elfabi/binary-read-arch.test | 5 +- .../llvm-elfabi/binary-read-bad-soname.test | 5 +- .../llvm-elfabi/binary-read-bad-vaddr.test | 5 +- .../binary-read-neededlibs-bad-offset.test | 5 +- .../llvm-elfabi/binary-read-neededlibs.test | 5 +- .../binary-read-replace-soname.test | 5 +- .../binary-read-soname-no-null.test | 5 +- .../tools/llvm-elfabi/binary-read-soname.test | 5 +- .../binary-read-syms-gnu-hash.test | 22 +++ .../binary-read-syms-sysv-hash.test | 22 +++ tools/llvm-elfabi/ELFObjHandler.cpp | 179 +++++++++++++++++- 14 files changed, 248 insertions(+), 20 deletions(-) create mode 100755 test/tools/llvm-elfabi/Inputs/gnu_hash.so create mode 100755 test/tools/llvm-elfabi/Inputs/sysv_hash.so create mode 100644 test/tools/llvm-elfabi/binary-read-syms-gnu-hash.test create mode 100644 test/tools/llvm-elfabi/binary-read-syms-sysv-hash.test diff --git a/test/tools/llvm-elfabi/Inputs/gnu_hash.so b/test/tools/llvm-elfabi/Inputs/gnu_hash.so new file mode 100755 index 0000000000000000000000000000000000000000..3970e587d7255b2bc3f40a94e59ef187062b7eae GIT binary patch literal 16088 zcmeHOU2Ggz6+XLm(skm*P8veeAa$q|kNd^59i*L%+k@x)j-6atnG_&kVSuSBBCaBP@80HQDqUHEBGrJMk4&X20H4saZ#qAHfvYPSTVZg*B-8P~09esJ>sXsPR~{onVntqhJ(Gyl?Bn zvL9hw<$nlaG0%SG@6&udejP@48t<6$_v_cyJXv%6#5-R4gT@0&Orqwe(!f(HZdH&2 z;MX6MQNWtHe;VTy)ym_;sq+}FwMcQL4gQrjcn$H#TI5+lOr5G7)^UOFhg*ksz;IdM z-SCPJ{{iBM;3#xPr9boWW9%M93k#o@w-7iC`(Q=!{xx=hkoXHjhiXwDM0-DK4PO*O z=too^6;vK^x&Tk+_o##Nu;Uy6%<-=5qPM($NI!zFS20s>XQ+$L)t!a+4*vO9?;h-Q zYG~s-0k$JTV=Q$C+p>Uc8UKkD8RQMqu-ls{I#^;lY=x@T+M}e?} zPIP>h?tPm3=03nraDZEQfj%p&07sM#U7vu^Y;Wj9_d}7}NC3sq>kh_tY zfxY1y=pk*NLwg_QI!Za%{M*Js#@*E?jL?$t_Lg@H6FMqkHzjPB@8$*PaU7`fxG{g>2@JrLIG=8#c3}eZ zuO7f9>8eq$*O&Ih&@V*Oxk(fI+ajhMmF`n(8T7a>gu;#Ljm>K1!TgN}p`LHkqIfNX zZ$>S?SxY{cUke$jw`<+EtH}+~4Of%v^EcL^=Ub_wI zyno-AUyoplA%uzaaDCaBe>1WuanX{;Qy-c>2v9$n#&^nBKS})@W`VU8oJRfRHR>Zx zu(sS_LB;_DuMYvkXl1=#eq zL_a{G{6=Zn3*G|;0tNyG0tNyG0tNyG0tNyG0tNyG0tNyG0tWsU82DtHNzxC`%wd-a>d^`i46Gs2Xoe^`BLJ{3&1X!#t?)53MeZjGWi{M(t_GDr)ES zGkX8&p;NK}38(mF+dZ!(5~JF%-mmw=SS6J!JC>HpTg9yQyzQ2qQc>D8t#8^Y+i4Af zMBzQ_jgm9-npGRp0SilC2%lpI`e_8lH418TM9>;{CFy0j!BU0Z%)#`X> zlz&9|^PchVD!ki={{-=HcpuED|Hp~F*O(jOmigZlwl0Y9je&iV4v~L1&wnCgxOW%- zYcxRk$gckr{t{sgUw-)m0*7IDUB*8JzPm1?2lH$GB-sB{8+-!s$6EMXh_|ZmcmeTN z^&PJv-m1RicLHxQfS#T!mgfqfXNwhG-3dE7rYpNStDFPj?IGgYd5b!#HIw&1w{vF3 zwF`6NXm=={!$Q0bh zv8{rW!nu_&`EWR{o9j;X9g7q$XYGwvq$0lOQxEX(Pl*(LMR=r$GT)r4fn3u=Ko{3MI zld+45IJye%7V~WgVp;6dIp=`16)9l9TxQM^ikn16cDqL>)$B{vsIP zaC4$q%kcQT&S1^!4EERY>t0jJ*QbS|D1ylkNekEyjuPNXv(6r-`~&QO?0R6 zIiA;1tkJYX{PDNZ`a$u1JEFI*ABN?+2*+n)_<)u6kLT~df{A+Y#q;O&SVQwbN^?Bx zA0kfQNjRR@XRO(e({P_Pt<&h*;duUClr`7w^7jai-293|OGY%@=ZUC}Dz9g)Fjcva7g+aaLRQ$3s()S+v?#1)r o&+p$O)|k7BB{e)GJAY}ysLA6p{6}oGivPq}>G4-zfUgVx0#)uIcK`qY literal 0 HcmV?d00001 diff --git a/test/tools/llvm-elfabi/Inputs/sysv_hash.so b/test/tools/llvm-elfabi/Inputs/sysv_hash.so new file mode 100755 index 0000000000000000000000000000000000000000..46b3b2abd1e8a2e5605bfcfc418ce09d33529728 GIT binary patch literal 16088 zcmeHOU2Ggz6+XL8+D+=zP8x#KAa#foDWS^Lc5rF}rn~XqSlVq`*QO!D8pgY`KkELt zv$K(1h+1K&vf4)Ep@@Qb;0eK7g+Sr~q^7PykW%FGfGVC0QiY8yVIU9N2eO=V=bX)W zvI~NW2gICc_uTWHpL^%qnVoU=o|h++Q_)BSM0()!psaomiL+y1R3ZT4Fbezd{TLjD z2N2$auQcQR43kvUr?+5UC#I@a_%6hR2-a!0#hxi{L zb`Xxhp19|f{1bse^DOwpr5dZP;2<2Qe=OO zZB$Tuw6G8G!~8B8D325h2LO8D&z`ArDW7VQ$oYgxO6a_lHScFEsN~!boUR)=ekc|e zOM11UpQTZnN14;bLb9e0EaG2cHO38Nfg>nuNFwI=4QZ}ou<+!E^W}#eg z!OWrpb7n4OW^BjK6{@c7%%#SQm9jl&Ef#Ih*V$vHmn|w_6$>xfFrAz^KW+}|!}vQB0G%JGGwjfYchT?fS=UJ3qUpe^#?UVJ=}jDMUM4+mcA?Kyt{=C`ue|!C*(!c zB!@i(k(Xv6vfLjz*85=WIubxR^ty^U>e@*>-2ZBH{J;azD;VMVK8OA$`j1cz@jt8F zgoF|@5Hb)l5Hb)l5Hb)l5Hj#zWWcyKwhx~n=Z$N7BdCqfG>!TP?op$$_s5?^^BZru z2NqfzS-tPEud7uoto1z>hkG{W|EdoIuZ_HoYTh_?7xRr9cYBQbj&Wo2DI;>zc<-(| zfCUR&u$KyUtM|%S;R&3m_JpxI_7o;yNnB5_(HNV>`kM#v5p&aMwOTj#CNM5S%Xx(s z_O~@GIpKvbY-KUxwh~G=>Q|ce+MU%acOre?q=)XUEWR0y$z~&UXLT!Lq~B}wUazNi zlo_q3wpXugL*HwsILb(;wi}19s)XA1>P<}eb^Y+w#^mITQ_5F?5>WxS}wy<`OD`Jk;lSR^8jLl|^%_PpTuy*^lvAP|@5+eu`>Cx7@vHDJI z&BN8mJD%3a;==%~6B&G`daV;==dlWGZQwFmCoYkVG{M$-n*}d7d=D8283-8&83-8& z83-8&83-8&83-8&8TkKYfc|hK`3k;n;a`pNJGBCRZ#U@&C6wQ?_N?$dWFTZ9WFTZ9 zWFTZ9WFTZ9WFTZ9WFTZ9WFTbVe}REdby+0+fJ_q9v?rNFaN5&Mf~S6CL|M1YCwp(3 zJRxpJRq`Qg*|Ylw`;Rg%`+$$JEywk!*t|g614|mNL4aEaKZ-uVHGnQ+i zvt*{Jv%+*$YlWVxxSref{FGo?)C%F1kTK3}l?rKGW(A8cR;!?=E2WZMMhlMxtD(+U zUB?@F8q$tib!*wIp2n^U?CCI*GtbRo=f}N94^tLY1?lkcL8h6hm^szRVP@QlQ#GyH zGT_8ZMccJA`jdl0Ls+>(km@S&7H{GrofYu7%G64w6-@D2stsjVC^fBM?~0yYTEft& z*+gp6oP2tMT2?xCuuLZ}oKGap3sY0iP0pEfiSx-x3{~8Fht0|VVUJ6uS~2rhIYX^5 zyBl7DO)f?uld;Rv+tRJ)C7~i5Y?_hCPF*mU=AIuu)2&3HN5-7^;?s%L%y@^B{ojZ1 zvQa%ua=()Mu;>^CpwNAohH{^j`!hWs&^=G`<-RD||9yv{s!zqQ$xruC$(Q@6=r{$V z(EJo5-?xO3oWPg($d=~-(O)JIg%6{hFLfSAAKkiT{wCkYMay#r)gX~No&`XSN>rI_TVyeED5ewDj%L zcLa&#$>;J5#OVB!eCZPuJxWPFk$i#P#sOiH4{`PkdpZ~|G;o}HcUpe?K&-v@@q`!OUZ!cMoe1E@>SbOc9OKNn)GydemXejdv{0AI#%0GVA Mi}-6GAz;Ek0GQ(qnE(I) literal 0 HcmV?d00001 diff --git a/test/tools/llvm-elfabi/binary-read-add-soname.test b/test/tools/llvm-elfabi/binary-read-add-soname.test index dbc5a00bb57..49c37ef754d 100644 --- a/test/tools/llvm-elfabi/binary-read-add-soname.test +++ b/test/tools/llvm-elfabi/binary-read-add-soname.test @@ -18,11 +18,12 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x0008 AddressAlign: 8 - Content: "0a0000000000000001000000000000000500000000000000000000000000000000000000000000000000000000000000" + Content: "0a000000000000000100000000000000050000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000" # DT_STRSZ 1 (0x1) # DT_STRTAB 0x0 + # DT_SYMTAB 0x0 # DT_NULL 0x0 - Size: 48 + Size: 64 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-arch.test b/test/tools/llvm-elfabi/binary-read-arch.test index f2c2d8bd56d..23f99a3721f 100644 --- a/test/tools/llvm-elfabi/binary-read-arch.test +++ b/test/tools/llvm-elfabi/binary-read-arch.test @@ -18,11 +18,12 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x0008 AddressAlign: 8 - Content: "0a0000000000000001000000000000000500000000000000000000000000000000000000000000000000000000000000" + Content: "0a000000000000000100000000000000050000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000" # DT_STRSZ 1 (0x1) # DT_STRTAB 0x0 + # DT_SYMTAB 0x0 # DT_NULL 0x0 - Size: 48 + Size: 64 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-bad-soname.test b/test/tools/llvm-elfabi/binary-read-bad-soname.test index cc7205b15eb..e0e6ac27859 100644 --- a/test/tools/llvm-elfabi/binary-read-bad-soname.test +++ b/test/tools/llvm-elfabi/binary-read-bad-soname.test @@ -18,12 +18,13 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x0008 AddressAlign: 8 - Content: "0e000000000000000d000000000000000a0000000000000001000000000000000500000000000000000000000000000000000000000000000000000000000000" + Content: "0e000000000000000d000000000000000a000000000000000100000000000000050000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000" # DT_SONAME 13 (0x0d) # DT_STRSZ 1 (0x01) # DT_STRTAB 0x0 + # DT_SYMTAB 0x0 # DT_NULL 0x0 - Size: 64 + Size: 80 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-bad-vaddr.test b/test/tools/llvm-elfabi/binary-read-bad-vaddr.test index 5dc96df38a2..03d453c4dd1 100644 --- a/test/tools/llvm-elfabi/binary-read-bad-vaddr.test +++ b/test/tools/llvm-elfabi/binary-read-bad-vaddr.test @@ -18,12 +18,13 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x1008 AddressAlign: 8 - Content: "0e0000000000000000000000000000000a0000000000000001000000000000000500000000000000600200000000000000000000000000000000000000000000" + Content: "0e0000000000000000000000000000000a000000000000000100000000000000050000000000000060020000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_SONAME 0 # DT_STRSZ 1 # DT_STRTAB 0x0260 # Bad vaddr (no PT_LOAD for 0x0000 to 0x0FFF) + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 64 + Size: 80 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-neededlibs-bad-offset.test b/test/tools/llvm-elfabi/binary-read-neededlibs-bad-offset.test index 439ca5ce9dc..e76aa5d8423 100644 --- a/test/tools/llvm-elfabi/binary-read-neededlibs-bad-offset.test +++ b/test/tools/llvm-elfabi/binary-read-neededlibs-bad-offset.test @@ -18,15 +18,16 @@ Sections: Type: SHT_DYNAMIC Flags: [ SHF_ALLOC ] Address: 0x1024 - Content: "010000000000000001000000000000000e0000000000000015000000000000000100000000000000ffff0000000000000a0000000000000024000000000000000500000000000000001000000000000000000000000000000000000000000000" + Content: "010000000000000001000000000000000e0000000000000015000000000000000100000000000000ffff0000000000000a000000000000002400000000000000050000000000000000100000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_NEEDED 1 (0x01) # DT_SONAME 21 (0x15) # Bad DT_NEEDED entry (offset outside string table): # DT_NEEDED 65535 (0xffff) # DT_STRSZ 36 (0x24) # DT_STRTAB 0x1000 + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 96 + Size: 112 ProgramHeaders: - Type: PT_LOAD Flags: [ PF_R ] diff --git a/test/tools/llvm-elfabi/binary-read-neededlibs.test b/test/tools/llvm-elfabi/binary-read-neededlibs.test index 4245d19d796..481499501c4 100644 --- a/test/tools/llvm-elfabi/binary-read-neededlibs.test +++ b/test/tools/llvm-elfabi/binary-read-neededlibs.test @@ -18,14 +18,15 @@ Sections: Type: SHT_DYNAMIC Flags: [ SHF_ALLOC ] Address: 0x1024 - Content: "010000000000000001000000000000000e00000000000000150000000000000001000000000000000b000000000000000a0000000000000024000000000000000500000000000000001000000000000000000000000000000000000000000000" + Content: "010000000000000001000000000000000e00000000000000150000000000000001000000000000000b000000000000000a000000000000002400000000000000050000000000000000100000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_NEEDED 1 (0x01) # DT_SONAME 21 (0x15) # DT_NEEDED 11 (0x0b) # DT_STRSZ 36 (0x24) # DT_STRTAB 0x1000 + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 96 + Size: 112 ProgramHeaders: - Type: PT_LOAD Flags: [ PF_R ] diff --git a/test/tools/llvm-elfabi/binary-read-replace-soname.test b/test/tools/llvm-elfabi/binary-read-replace-soname.test index 23f0ba3308e..da3427d95ae 100644 --- a/test/tools/llvm-elfabi/binary-read-replace-soname.test +++ b/test/tools/llvm-elfabi/binary-read-replace-soname.test @@ -20,12 +20,13 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x1018 AddressAlign: 8 - Content: "0e0000000000000005000000000000000a0000000000000014000000000000000500000000000000001000000000000000000000000000000000000000000000" + Content: "0e0000000000000005000000000000000a000000000000001400000000000000050000000000000000100000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_SONAME 5 (0x05) # DT_STRSZ 20 (0x14) # DT_STRTAB 0x1000 + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 64 + Size: 80 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-soname-no-null.test b/test/tools/llvm-elfabi/binary-read-soname-no-null.test index 224407d9310..f474878348d 100644 --- a/test/tools/llvm-elfabi/binary-read-soname-no-null.test +++ b/test/tools/llvm-elfabi/binary-read-soname-no-null.test @@ -19,12 +19,13 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x1018 AddressAlign: 8 - Content: "0e0000000000000005000000000000000a000000000000000f000000000000000500000000000000001000000000000000000000000000000000000000000000" + Content: "0e0000000000000005000000000000000a000000000000000f00000000000000050000000000000000100000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_SONAME 5 (0x05) # DT_STRSZ 15 (0x0F) # DT_STRTAB 0x1000 + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 64 + Size: 80 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-soname.test b/test/tools/llvm-elfabi/binary-read-soname.test index b6877c64bdc..61e8fcf2855 100644 --- a/test/tools/llvm-elfabi/binary-read-soname.test +++ b/test/tools/llvm-elfabi/binary-read-soname.test @@ -19,12 +19,13 @@ Sections: Flags: [ SHF_ALLOC ] Address: 0x1018 AddressAlign: 8 - Content: "0e0000000000000005000000000000000a0000000000000014000000000000000500000000000000001000000000000000000000000000000000000000000000" + Content: "0e0000000000000005000000000000000a000000000000001400000000000000050000000000000000100000000000000600000000000000001000000000000000000000000000000000000000000000" # DT_SONAME 5 (0x05) # DT_STRSZ 20 (0x14) # DT_STRTAB 0x1000 + # DT_SYMTAB 0x1000 # DT_NULL 0x0 - Size: 64 + Size: 80 Link: .dynstr ProgramHeaders: - Type: PT_LOAD diff --git a/test/tools/llvm-elfabi/binary-read-syms-gnu-hash.test b/test/tools/llvm-elfabi/binary-read-syms-gnu-hash.test new file mode 100644 index 00000000000..777f2714b28 --- /dev/null +++ b/test/tools/llvm-elfabi/binary-read-syms-gnu-hash.test @@ -0,0 +1,22 @@ +# RUN: llvm-elfabi --elf %p/Inputs/gnu_hash.so --emit-tbe=- | FileCheck %s + +# CHECK: --- !tapi-tbe +# CHECK-NEXT: TbeVersion: 1.0 +# CHECK-NEXT: SoName: libsomething.so +# CHECK-NEXT: Arch: x86_64 +# CHECK-NEXT: NeededLibs: +# CHECK-NEXT: - libm.so.6 +# CHECK-NEXT: - libc.so.6 +# CHECK-NEXT: - ld-linux-x86-64.so.2 +# CHECK-NEXT: Symbols: +# CHECK-NEXT: AGlobalInteger: { Type: Object, Size: 4 } +# CHECK-NEXT: AThreadLocalLongInteger: { Type: TLS, Size: 8 } +# CHECK-NEXT: _ITM_deregisterTMCloneTable: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: _ITM_registerTMCloneTable: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: _Z11rotateArrayPii: { Type: Func } +# CHECK-NEXT: __cxa_finalize: { Type: Func, Undefined: true, Weak: true } +# CHECK-NEXT: __gmon_start__: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: __tls_get_addr: { Type: Func, Undefined: true } +# CHECK-NEXT: _fini: { Type: Func } +# CHECK-NEXT: _init: { Type: Func } +# CHECK-NEXT: ... diff --git a/test/tools/llvm-elfabi/binary-read-syms-sysv-hash.test b/test/tools/llvm-elfabi/binary-read-syms-sysv-hash.test new file mode 100644 index 00000000000..0f290852998 --- /dev/null +++ b/test/tools/llvm-elfabi/binary-read-syms-sysv-hash.test @@ -0,0 +1,22 @@ +# RUN: llvm-elfabi --elf %p/Inputs/sysv_hash.so --emit-tbe=- | FileCheck %s + +# CHECK: --- !tapi-tbe +# CHECK-NEXT: TbeVersion: 1.0 +# CHECK-NEXT: SoName: libsomething.so +# CHECK-NEXT: Arch: x86_64 +# CHECK-NEXT: NeededLibs: +# CHECK-NEXT: - libm.so.6 +# CHECK-NEXT: - libc.so.6 +# CHECK-NEXT: - ld-linux-x86-64.so.2 +# CHECK-NEXT: Symbols: +# CHECK-NEXT: AGlobalInteger: { Type: Object, Size: 4 } +# CHECK-NEXT: AThreadLocalLongInteger: { Type: TLS, Size: 8 } +# CHECK-NEXT: _ITM_deregisterTMCloneTable: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: _ITM_registerTMCloneTable: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: _Z11rotateArrayPii: { Type: Func } +# CHECK-NEXT: __cxa_finalize: { Type: Func, Undefined: true, Weak: true } +# CHECK-NEXT: __gmon_start__: { Type: NoType, Undefined: true, Weak: true } +# CHECK-NEXT: __tls_get_addr: { Type: Func, Undefined: true } +# CHECK-NEXT: _fini: { Type: Func } +# CHECK-NEXT: _init: { Type: Func } +# CHECK-NEXT: ... diff --git a/tools/llvm-elfabi/ELFObjHandler.cpp b/tools/llvm-elfabi/ELFObjHandler.cpp index 187a5910665..8f3b76ccc89 100644 --- a/tools/llvm-elfabi/ELFObjHandler.cpp +++ b/tools/llvm-elfabi/ELFObjHandler.cpp @@ -31,6 +31,11 @@ struct DynamicEntries { uint64_t StrSize = 0; Optional SONameOffset; std::vector NeededLibNames; + // Symbol table: + uint64_t DynSymAddr = 0; + // Hash tables: + Optional ElfHash; + Optional GnuHash; }; /// This function behaves similarly to StringRef::substr(), but attempts to @@ -81,6 +86,7 @@ static Error populateDynamic(DynamicEntries &Dyn, // Search .dynamic for relevant entries. bool FoundDynStr = false; bool FoundDynStrSz = false; + bool FoundDynSym = false; for (auto &Entry : DynTable) { switch (Entry.d_tag) { case DT_SONAME: @@ -97,6 +103,15 @@ static Error populateDynamic(DynamicEntries &Dyn, case DT_NEEDED: Dyn.NeededLibNames.push_back(Entry.d_un.d_val); break; + case DT_SYMTAB: + Dyn.DynSymAddr = Entry.d_un.d_ptr; + FoundDynSym = true; + break; + case DT_HASH: + Dyn.ElfHash = Entry.d_un.d_ptr; + break; + case DT_GNU_HASH: + Dyn.GnuHash = Entry.d_un.d_ptr; } } @@ -108,6 +123,10 @@ static Error populateDynamic(DynamicEntries &Dyn, return createError( "Couldn't determine dynamic string table size (no DT_STRSZ entry)"); } + if (!FoundDynSym) { + return createError( + "Couldn't locate dynamic symbol table (no DT_SYMTAB entry)"); + } if (Dyn.SONameOffset.hasValue() && *Dyn.SONameOffset >= Dyn.StrSize) { return createStringError( object_error::parse_failed, @@ -126,6 +145,142 @@ static Error populateDynamic(DynamicEntries &Dyn, return Error::success(); } +/// This function finds the number of dynamic symbols using a GNU hash table. +/// +/// @param Table The GNU hash table for .dynsym. +template +static uint64_t getDynSymtabSize(const typename ELFT::GnuHash &Table) { + using Elf_Word = typename ELFT::Word; + if (Table.nbuckets == 0) + return Table.symndx + 1; + uint64_t LastSymIdx = 0; + uint64_t BucketVal = 0; + // Find the index of the first symbol in the last chain. + for (Elf_Word Val : Table.buckets()) { + BucketVal = std::max(BucketVal, (uint64_t)Val); + } + LastSymIdx += BucketVal; + const Elf_Word *It = + reinterpret_cast(Table.values(BucketVal).end()); + // Locate the end of the chain to find the last symbol index. + while ((*It & 1) == 0) { + LastSymIdx++; + It++; + } + return LastSymIdx + 1; +} + +/// This function determines the number of dynamic symbols. +/// Without access to section headers, the number of symbols must be determined +/// by parsing dynamic hash tables. +/// +/// @param Dyn Entries with the locations of hash tables. +/// @param ElfFile The ElfFile that the section contents reside in. +template +static Expected getNumSyms(DynamicEntries &Dyn, + const ELFFile &ElfFile) { + using Elf_Hash = typename ELFT::Hash; + using Elf_GnuHash = typename ELFT::GnuHash; + // Search GNU hash table to try to find the upper bound of dynsym. + if (Dyn.GnuHash.hasValue()) { + Expected TablePtr = ElfFile.toMappedAddr(*Dyn.GnuHash); + if (!TablePtr) + return TablePtr.takeError(); + const Elf_GnuHash *Table = + reinterpret_cast(TablePtr.get()); + return getDynSymtabSize(*Table); + } + // Search SYSV hash table to try to find the upper bound of dynsym. + if (Dyn.ElfHash.hasValue()) { + Expected TablePtr = ElfFile.toMappedAddr(*Dyn.ElfHash); + if (!TablePtr) + return TablePtr.takeError(); + const Elf_Hash *Table = reinterpret_cast(TablePtr.get()); + return Table->nchain; + } + return 0; +} + +/// This function extracts symbol type from a symbol's st_info member and +/// maps it to an ELFSymbolType enum. +/// Currently, STT_NOTYPE, STT_OBJECT, STT_FUNC, and STT_TLS are supported. +/// Other symbol types are mapped to ELFSymbolType::Unknown. +/// +/// @param Info Binary symbol st_info to extract symbol type from. +static ELFSymbolType convertInfoToType(uint8_t Info) { + Info = Info & 0xf; + switch (Info) { + case ELF::STT_NOTYPE: + return ELFSymbolType::NoType; + case ELF::STT_OBJECT: + return ELFSymbolType::Object; + case ELF::STT_FUNC: + return ELFSymbolType::Func; + case ELF::STT_TLS: + return ELFSymbolType::TLS; + default: + return ELFSymbolType::Unknown; + } +} + +/// This function creates an ELFSymbol and populates all members using +/// information from a binary ELFT::Sym. +/// +/// @param SymName The desired name of the ELFSymbol. +/// @param RawSym ELFT::Sym to extract symbol information from. +template +static ELFSymbol createELFSym(StringRef SymName, + const typename ELFT::Sym &RawSym) { + ELFSymbol TargetSym(SymName); + uint8_t Binding = RawSym.getBinding(); + if (Binding == STB_WEAK) + TargetSym.Weak = true; + else + TargetSym.Weak = false; + + TargetSym.Undefined = RawSym.isUndefined(); + TargetSym.Type = convertInfoToType(RawSym.st_info); + + if (TargetSym.Type == ELFSymbolType::Func) { + TargetSym.Size = 0; + } else { + TargetSym.Size = RawSym.st_size; + } + return TargetSym; +} + +/// This function populates an ELFStub with symbols using information read +/// from an ELF binary. +/// +/// @param TargetStub ELFStub to add symbols to. +/// @param DynSym Range of dynamic symbols to add to TargetStub. +/// @param DynStr StringRef to the dynamic string table. +template +static Error populateSymbols(ELFStub &TargetStub, + const typename ELFT::SymRange DynSym, + StringRef DynStr) { + // Skips the first symbol since it's the NULL symbol. + for (auto RawSym : DynSym.drop_front(1)) { + // If a symbol does not have global or weak binding, ignore it. + uint8_t Binding = RawSym.getBinding(); + if (!(Binding == STB_GLOBAL || Binding == STB_WEAK)) + continue; + // If a symbol doesn't have default or protected visibility, ignore it. + uint8_t Visibility = RawSym.getVisibility(); + if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED)) + continue; + // Create an ELFSymbol and populate it with information from the symbol + // table entry. + Expected SymName = terminatedSubstr(DynStr, RawSym.st_name); + if (!SymName) + return SymName.takeError(); + ELFSymbol Sym = createELFSym(*SymName, RawSym); + TargetStub.Symbols.insert(std::move(Sym)); + // TODO: Populate symbol warning. + } + return Error::success(); +} + /// Returns a new ELFStub with all members populated from an ELFObjectFile. /// @param ElfObj Source ELFObjectFile. template @@ -133,6 +288,8 @@ static Expected> buildStub(const ELFObjectFile &ElfObj) { using Elf_Dyn_Range = typename ELFT::DynRange; using Elf_Phdr_Range = typename ELFT::PhdrRange; + using Elf_Sym_Range = typename ELFT::SymRange; + using Elf_Sym = typename ELFT::Sym; std::unique_ptr DestStub = make_unique(); const ELFFile *ElfFile = ElfObj.getELFFile(); // Fetch .dynamic table. @@ -152,7 +309,7 @@ buildStub(const ELFObjectFile &ElfObj) { if (Error Err = populateDynamic(DynEnt, *DynTable)) return std::move(Err); - // Convert .dynstr address to an offset. + // Get pointer to in-memory location of .dynstr section. Expected DynStrPtr = ElfFile->toMappedAddr(DynEnt.StrTabAddr); if (!DynStrPtr) @@ -185,7 +342,25 @@ buildStub(const ELFObjectFile &ElfObj) { DestStub->NeededLibs.push_back(*LibNameOrErr); } - // TODO: Populate Symbols from .dynsym table and linked string table. + // Populate Symbols from .dynsym table and dynamic string table. + Expected SymCount = getNumSyms(DynEnt, *ElfFile); + if (!SymCount) + return SymCount.takeError(); + if (*SymCount > 0) { + // Get pointer to in-memory location of .dynsym section. + Expected DynSymPtr = + ElfFile->toMappedAddr(DynEnt.DynSymAddr); + if (!DynSymPtr) + return appendToError(DynSymPtr.takeError(), + "when locating .dynsym section contents"); + Elf_Sym_Range DynSyms = + ArrayRef(reinterpret_cast(*DynSymPtr), + *SymCount); + Error SymReadError = populateSymbols(*DestStub, DynSyms, DynStr); + if (SymReadError) + return appendToError(std::move(SymReadError), + "when reading dynamic symbols"); + } return std::move(DestStub); }