[08/14] libdw: Parse DWARF package file index sections

Message ID bc7c6a6153083bd096de852f45568042eb0303d1.1695837512.git.osandov@fb.com
State Superseded
Delegated to: Mark Wielaard
Headers
Series elfutils: DWARF package (.dwp) file support |

Commit Message

Omar Sandoval Sept. 27, 2023, 6:20 p.m. UTC
  From: Omar Sandoval <osandov@fb.com>

The .debug_cu_index and .debug_tu_index sections in DWARF package files
are basically hash tables mapping a unit's 8 byte signature to an offset
and size in each section used by that unit [1].  Add support for parsing
and doing lookups in the index sections.

We look up a unit in the index when we intern it and cache its hash
table row in Dwarf_CU.  Then, a new function, dwarf_cu_dwp_section_info,
can be used to look up the section offsets and sizes for a unit.  This
will mostly be used internally in libdw, but it will also be needed in
static inline functions shared with eu-readelf.  Additionally, making it
public it makes dwp support much easier for external tools that do their
own low-level parsing of DWARF information, like drgn [2].

1: https://gcc.gnu.org/wiki/DebugFissionDWP#Format_of_the_CU_and_TU_Index_Sections
2: https://github.com/osandov/drgn

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libdw/ChangeLog                     |  18 +-
 libdw/Makefile.am                   |   2 +-
 libdw/dwarf_cu_dwp_section_info.c   | 371 ++++++++++++++++++++++++++++
 libdw/dwarf_end.c                   |   3 +
 libdw/dwarf_error.c                 |   1 +
 libdw/libdw.h                       |  23 ++
 libdw/libdw.map                     |   5 +
 libdw/libdwP.h                      |  36 +++
 libdw/libdw_findcu.c                |   8 +
 tests/.gitignore                    |   1 +
 tests/ChangeLog                     |  13 +
 tests/Makefile.am                   |  11 +-
 tests/cu-dwp-section-info.c         |  74 ++++++
 tests/run-cu-dwp-section-info.sh    | 168 +++++++++++++
 tests/testfile-dwp-4-strict.bz2     | Bin 0 -> 4169 bytes
 tests/testfile-dwp-4-strict.dwp.bz2 | Bin 0 -> 6871 bytes
 tests/testfile-dwp-4.bz2            | Bin 0 -> 4194 bytes
 tests/testfile-dwp-4.dwp.bz2        | Bin 0 -> 10098 bytes
 tests/testfile-dwp-5.bz2            | Bin 0 -> 4223 bytes
 tests/testfile-dwp-5.dwp.bz2        | Bin 0 -> 10313 bytes
 tests/testfile-dwp.source           | 102 ++++++++
 21 files changed, 831 insertions(+), 5 deletions(-)
 create mode 100644 libdw/dwarf_cu_dwp_section_info.c
 create mode 100644 tests/cu-dwp-section-info.c
 create mode 100755 tests/run-cu-dwp-section-info.sh
 create mode 100755 tests/testfile-dwp-4-strict.bz2
 create mode 100644 tests/testfile-dwp-4-strict.dwp.bz2
 create mode 100755 tests/testfile-dwp-4.bz2
 create mode 100644 tests/testfile-dwp-4.dwp.bz2
 create mode 100755 tests/testfile-dwp-5.bz2
 create mode 100644 tests/testfile-dwp-5.dwp.bz2
 create mode 100644 tests/testfile-dwp.source

new file mode 100644
index 0000000000000000000000000000000000000000..8cc1bbe2a8042bd06c9119c276326371261afbe0
GIT binary patch
literal 6871
zcmY*-dpHwp`2W^=H)&=<&CFp4A#EC)V{CHhb?UIS(qWE`cgb<cp=r#SNe7WKyd|TY
z5=oIW(MHNSgqEDoht#+C`~Cg%yRZBDJlB2y^E}smf9}t7JrD6b-4JVWPW|+`o|T{F
zfb(L%|FgNv|GWGzY%ekjqCbAdha?!-YF;E}CDGJ)E9_Cgd2vn6e`)f&Sl%Z9yUYP6
zPlyBdXo)+i{3WF&mQN<zSeOGed1v}S<^bTClX32r(v|~u(x<%~yRYB3`*{88>5r>e
z$<`#{E&GD&S*KU63+O9R+#V}VmZcmH)NM*qZI4VaHD6V<w=R%4c1HP~D@ffQByHZt
zOD9=8mdZ27Lhpm6m3yReTIpC2NmUup_V_*)D*=GJV1eoCQSmwMf;L`GkC-qA!LeiS
z;d;dh+;NB~UK$tR-eZ@M!C{GEGFWZt=8Xb9y1PK&6-^gFN$M8St++<I9ujIUXl(`P
zc|EqXBbfthPbiy5Ia+#Ow|+W(fBIpxo;`>zwUq<<h@%7Obl?_O$VJk@Y&#YJ0L%fz
ztL)m7BW;e{N29x^yX)D-EhS5;EvgTwTsw8`Ok@H`-SOkc0Y`h1d5^R=qyPYZ_~dDh
zd$93k!s+I(#V=L1tZ$QJDqGT~Y*j4xYxhRfKfOXa74cr1&o`UCt5kbUCA@Uw{MRUq
zsoaiww)?&;Ph!@2)6fqD^I9=+9&+9El~qD^;*O^gYcJOD4LH^6&0P(3kSdg7^L~t3
zeK=!Er7NPE9%_JXyt_xHnUS`xOu))4LbTMO#WR>(l{1mI0f`UDDr<X$r+jrmA+a6k
z8OQ3iUze;`Qonz@Cnp*GZ76(vtkLfE*2bbg>u(F)zo~mQm*$pFmFLL$8<-9om1+h=
zD!mMq_dZj(o0MK@U>K(BapLm4*WcUt4X^FJLY5-HI<6w3@y7;fU_9RD_VvDZb^Vh-
zBQ=q^#w^rHbft|wI7keK?l#srAg8SK-fi8Z??9C58sc-$_Hk-`@|w&GN(n+pY}YEu
z=-XuGIXl_FD%IZL_e!C4&KDG_MSIK2sqNXd&pgSd^p@kGGUdmKyEkf%pO@~?bm#6O
zlBlt78ZXDJDwX~E`^T|6N==cq<>kkz<5o)1!_NB2h#S|jNJE`#s!XN#XOE|TtWCo7
z=1F!L>$|+BOn1OK`}~={jj6MGGc%?6DP2C^y+w3kVN=@qYQCr2m5uw<0jt^UI}Oy~
zwzB3Wz5b1asb2NjPxv%Wkv22!ykXHlDlY=g8n|5v5TBW9h|cgH?!D-2ReXn{Hg<}l
z;;?oBNA{0^qz7F?gc8C<n;*JLgylMVy_}RJtSpt$kY|N(i7U>0aYJgTHx`Od=wLp(
zdf_fs6}BeZnQO0r8no$Ps*VME504kS<z=O3#admIuP(ZVG}UULHe@!JVP(l%q0{O7
z`jL)uCe^!Ruk5?B{yeQ>*-;^tQ7k*Lp7r{?e#lFY@rB_0QSvRVR;}C+Epc-brQF3D
zD$`9lqE9~SG`GbdqdT^G{=4kCm<j>763XH3cE>2h05H6LD~xgzjSp1|@Akx++L2U1
zDVQ9CfS&cI-x&PY#>X`*Epx*bP1Du~;F}BPGj))Iv$mJ=P2JedC+VcR=m>gbjie_e
zSa74vdsxpr5zeycuX!?mQPu+Uxk)sr6jsVmJso&7uxoH-s`MiUGug=>9wCXv4l1Wu
zIrg=`J^gd|GUUhgTSRH{F*jyaq!pm4cbb0sGCtXthP*m$8O-i2K40vFTdjEB5^&|T
zcI>C+4F$sOJ%D{0K?`Ke4YP+O<tvcSUhfp<Pb6MSU&Q4N-LBhk@7!|i)_l9EP;aFJ
zX{3ccvZ!fkvTxpvo*i6xAvx)i-t&9V+0*1@r2DQB(`x(??=b9PNWm4;6V`MmY3cji
zwnKH_ofubOqFb?A7GzC8Oz^?jTB6?iu=94dl35vlk{SOfwPMaE*2Sy6YsS-m-Mzs3
z#0%JO&{0Nal0JI7gn1mB?0@!9XG`4Rftb>8h>@I2&7NiY_LCb&yS|tn_~D)<=lb$Y
zX$5+Hr%>Y8zbE6#PEXNo4zg#?|Bn3rfW39q``wV>MOL++a~;y-(`ord_|UaXLP_;z
zQZnlx^N8#8oAI&PcZ0F7{7H)g7oj%Vha}9u4<Mk%c@i~{_N`5gXqg*bep~ZX+3Clc
z6y}fS){ZJ!Y@1`Q@(*GhP0p^S9*Vt=fU~SAsM8-r&(w{l>Xfd1`02a$AY0wi==;L4
z%o96t{`P{2Iy2R2zZ%%D%b!?5yhS>Rt0Q^0Y&L-XgsB=6Ncg;dv~#8SM;Bmor_Nkc
zWX`zbb;~hQW-fS6hbApSo%E6rgKUf8T&2Nh23xFJpBVoHPWdRPVbb!t3v7R<?|gXQ
z666%6;1RHXlbvMl$LSL`MXUt9+uZ|Onv41RnDP0k{8S=vA$u#*e7k)osw~bvO6hvT
zm!H4ChAFh+K4?s1t+$kBXlWW>bcm5&OD1x`#ztjRWtdX_A~jN_tRPd%?oAUl;H>4H
z!j`#D`1B>H%`pX=l?vBmsTNDtt&O(1ZzkP#hsj3q*QRe3I{p!h^oesyk?kb|I^ItW
z&X$thj*OIaT#>prq2-{^eNyOdxmpx|%rn=n$@ZWVGvcyH@*kVAFBxW2YMc59+dQeb
zS=qZ(Ia}8vY6D`!|CTyZd#_5#zki*$XD>_&WB7v&cH-Yg-x@o3I}MYapIZaYGR~82
z3UK?ieAs$jSu;fTC-%H#&h~SW<injO#p2<elJ{4seO_x9<grb>$JobFJ60E$Qqp!>
zw}}<=lK;GH`DNyGc2e%=?^Yo*H-ySL(Wo5znbAX|)0OUca^uVqeK$(wVNjnwKi<lT
zi=@u)JCbsn$1Z<2i|~6`7yJ%Aq)0(8Q9+;3BNPrOP<gl{))~e9;bZ>9A-8aJmsG&q
zv)xlwbQx>*NtV%)sZDiM<E7S{>bp>5i;qOnw2OOLF(&Ur$y11)#n=*23>04HT7l)U
zwBrbDQa2QKTbH9t;u2D6C_$rw)tqv(M2n1$HYxW&pnL_#Ri+2&hv<X1l{8}fiLIWE
zm_S-HD9imQNuPm*UWnFotqkxba=zMFY7#}>Mre{>9XtsN(I7LPX<EILS8AZk)-=zN
z0J7s|==B-Ri8=^bjhsd~er}(>eb4YjZ?BMpaC!WI1p=m~rH-TFO)zUb-mQK-6p7O3
z`U=bJ2tjFiA<HSU>f{)I+o`eJ*{6KdZzn5RQn$o6CL(Snl%7mNVB8=j#>3Bw(+FI4
zIuXfp*9=bnyfVn@e+5@~hm?rMl7I^--rZ`$o#j<zTZ)#t$WctV7hNdFcE|4m?mULl
zmI%CQk#W=z+99vq#LdQ+6yR>iZ6(R*E8;ZeYc7DJ^=YHVD5o|7ZBZgq$)3GNHFeM>
zA@Z`Wd=6$en&!3TOf3NOF|I)KW&$rYxPLOlq|0rX)>5Fk+W-xe21XSgP$)Pj@tlaF
zU(E3%N(flp3StZ*w^y9lYSnr{4_-&)w$;fS!EzC>BDcZ={v>qC1a_}>rc}KIypzYy
zJF&z|kmGR;leuxUQFIpT8OY^5f`Gm$BLP!Qh;zp<`mHi<u2EUnXLj4PjPt$(Da6q^
zu(5<?l*6}wn4Pg5!9Vckx)N!sgT&Tz;$dtAmYs`=szO21#FlvOI_R8k`9>`KCY;#d
zH%T#;ZEKPcIn9!Qof<ava^bDF9K91t$@_}PWOCXvMXTj$=f@xyjZB9&!xn>Q&xDVo
zh9rSdlv5K-64-QeKfLV;{&<+!RnmboI289i6TKTpI^ZowK!dq61RN+AQI(SC;HcRQ
zrA_xN2dBW+3`RiBTh5TSg{_}%?K8|uhDmZp48{%K8FwxP>I<IL-i&2;Z+;7jk39nB
zPBTQvROfc%*b;O-)S5NTDR7Tz)bk~N6>tD6l?u-sR6nAbtv*2tU=bY+js*($CJ!g0
z6Imh&2{F0}yi6Y4P>4uzpu5h34di)#L$jzXn0ft8d5E%q5XoO^8d0C50OvIlGkpUC
z7~@7TRy0%PDW9K}=h<*mrXC86^M)0;#&An!i6WXI0q@1D$-vX*kwlh{Av;&<0<e|F
zNs05sN`Sd3W37HDMVOEc7wgGsv@dYtxFQTC?#JV83muA2(M8{pI(~BhsPG8BhBbXt
zFc>Teo&+DlN*jUaRQ;d>Bx^c4&>mhQFyi$G1~9d&<F3Lx2e1-Obzb76lY2qtQ)_oD
zvyX9h_Ex`hSb2!;)krL#0!<YZM!Txf>e&cT7N1{zA(|lQ<E$A35z);H^6b{`{S@v9
zx(;QDOIAFhUfM4HNsH)UmoMtb<3KAA&3GRP&S(q{7wxNHdB{1%fm8~O1u!@&2t11W
zs#?rqFFGAw59O9bqpVslK1P>z=Ih`HmGB8yq!?|6m<_?<DA;^GxNyKgY5T9}qe^hr
zjU(&7H)QL-hV&U#6nVrZ>Xak3niI?p{JZgNUFvlT;+7ehI|a!Iy|CMW*M%n?NQ1&B
zgCGxrP)=2%Vr^DG)Y{w+N$di_S=g#R2FHB)0ttUo2>sL7&R!_|y_eXO0eG}NS<Fem
znL=l#j8(<&R!IVm2&Q5YQ)_Ex>4<nmnD}%ItT~GZLdW@#Sc^5l@@5Vw&s`S{=Othm
zu@D)Y0tBz7YM%vTlZY~$#a4eGL-b&<5u0ByjgI3q`51!<=wRRgfdING59f7W5IRbN
z*fWri+*I2$kms{MzucQL^Na1>dPFy1&4xj5Fg7m-ESPXemsu+L__vsMdD3g4hOdYE
zzG@!&Wh??Mevw`o@Y(t2?W=KY`S(&lzKY_&0m>c8c6sr}V+E3v>>0L$!5LiA@kkHy
zLEAqw=h{S0C@kd5QnU*B*gcz~{<SVIW5O*o+taXGj5c%}ls+h$4ZbW03XvSVv1i$g
z4`Hz`{w!T*fzd8MpWA)t_0blsia}OnO;c;UtvkXfYR~@(-4`bCwhj+bYpC-jJ_@7*
zrxX{^zy**!wmk7fpKs&Im~(Pu%6_Z;SlJktSdp2tp02QMCUL(GnM^JHe@B{8TQM8E
zoIy{MKMu(}7!*-MJ{$7Msw8cEVj>+oN1s$~uk<k$iJsSOT@o!*SMcjY4{{o2-HzZZ
z<Wu9fuQ?|U`NLt|e9v8|`M^+?JZ|+xLW!piIWST6*T{C?HW=J3Npw_Gm)mz8m7O^+
zk2{ks>zcn-GO;&hk-Aa?A6+TUmr+3h`?}>(iqE>9F-rPhsk_bW$6rM@f0x?2T)eKJ
z7Je;%^M}&ed*NY^<j1L-BwZ9%(T7n%9X?;=LZxmDnN_=e;f8-}Ty1SQ5(%|_@2>D;
zzgFQNE292~g@rj<^{bEQjiJ`Uv{$Kj%XG+Rd2>52`*%_&zhNT6nuHlko@NhiHfRQC
z@29BRWRx(>0(PI`NPeA)xO7<Y4tQ4*Z5jPd0w<`0FL~^e3U9(ZmUy(U7W5qun?{~9
zVwt#Bu|0(XmkgV_^rCo^p^?ftCl|FlLbuHS+x|b~e@3|csd(WZ`AsM+CI4YT6Pfyl
z4u4XG4yAv{;RVU93-nKOwt5~xTd4|umH!^$r{GrGrIwy|_|oP4^k&$4-Yt~k#&R+g
zY}TOovbdhjPGmbKg>`1>ziY`lTfMn%Euc>{P~==bY4HzJlQ8dHarBPD5+R@2N#@A8
z>Qj=A2b&g|sV6GF{CBATzFLqgR(b3B<6E_K>jGbxXio8KrUI172Ev${8+XJEoPZ<s
zZEftrQn5OO_7|E28W?BJlni*iQMpTjeih*MS4eZbW`T5lm+_tQ3h(raw1d0vQ{QP+
z8W}cfoXeZG&nv87FEegA**sTAj1m*G4p|_MSKX{4pk-`h;<EPWsMsB6q!rQh6P!{&
zNI362k_bigfH_#U)FCz^2Fr$Ad}MqZNqcUD%*_U6z20r2nfZG2rhFD2A#n$$K5B-~
zhTRo=JeCx$Dj07`GfC4|tRuiAOV)r^q6;A$llMd|R1sbAB#e7qp?%6$QA=$O&wk)f
zaf?Ud!=G3>+|m{*+B<6(?Z*&qIV6v%O#fG|0qy84eMXs9p<R=!wp5%IJ_JwV4yL!S
zf*WzDX70!uI7{!8arFJ9ea1S;xi!7xc}@9Tn(b(B?`XPZ#yM4v;Ge&^p?V9!CZjMI
z-HCPXG1QG%;+5no0}wRs%A_^pigD5Z7jQEE3!t?Ad`&lS5jl-Yvn9uzg(=2Hr^MrY
zdf6qieG4eg+~O-_t{)yKeii}`N@2hlxDdT`Jt^8J<2-+@)<$J*Agg!?NoT}lNuUvP
zyYFIUIjY*I+-8JJkx%JL?ehe)gP~SC8aZA96B{3SF+W_-R&LF0u;jBj?;_jpA=QR2
zBP(_Ok-Lr>xk@>nqUa~P#=h5h6;d`KZdet$-g@w154<-nCpdE<OxBfeqo1fGEow@X
zy=$4_L8kms5sFHwESaK>%}5GAf)I%Jj07^YnVDzss4E^5kUDWQe3jt2wPC5na!!nr
zYJK_V`L}_MAvcwkA--YHLwT3dE)BMi_Dol-1C*3|60bZ>HU5q*F<K@Vb3pL8U~V!R
zobgF5ZB_BH;>+WVj`F^(wAdFl$6YwZcP_d?VO~1n4#_ISD&L~Asc)#^*-JWl!>_7*
z#?G6JPKYLv1mBKBdd0o!ZZL$$LEAPf)!M10z%$vIhLt{JZNfiq`+UZL(JLd&@ED|0
ztq3e3E{LXKe8$DcS6`FtTz5ML{Nje_-3)(>NaI%zExPoMKR@8vNiLUg*7oeo<a;73
zDV<p+7jE0m+h}JUQcD+KNa)b@aX>xDM!pE7uT;0G#)J3yQ;^zaO_Yns$|P+vMRuqo
zE03R-rJGVr<`idVP|oXn_tr=_SGqXk82y}1X9JINe)%Eokg+04u{^=0+%x6bd(1>S
z^^+m~s)B4=`ISVi>1QX}D=F<0#l3ft2J{hYvTtuqFqGgupzWEQh3V&ezOVF|>eYbB
z@^AZ&-BUm3QgiZEv+Mw2pSQ4FV(^7;gQwf~s(7Q<*<IiKYX&hB!VKXhOhDNEusH?U
z(Uliz_dEhTr0Mxd+B5fEG_JQH`9JPxq>qGFw~kxQpRUiVZXk!6LJeQg8-vR_4jEQ5
z^73svZ{KG$=;-_Y*n|3d#98$)BG99)0cK~dp<b+bcg{ZBYED>Q3Pp@huYqneu%mHs
zdU)#9)sf}ANqpLCn}F*>nTC2DIyGho-mTz$hHG@nI{trO_MYkGQ$C!Y&#J}VRfMfu
zzgc+$PFWwWE(*FhR0L<$xd~#OdTc!V6cGI*Ci8Cf<%dr(2=UtKoqStlMQ~+2&Nky#
zbG%`)s@D62;(QPFeI7CU^^?ka51u(8Jl~RN`Cz~*@y~68zlKQ4?^iLkxoH-DQe!|f
zzV<h0Yd(3YN=#P=08s~Qzb%ub{zglNmd7?1KE>?Ze6xD@{afF0125%tgcp*ZZZBD-
z6veBbujt`BB;7D%Z(FqoW1(`|(8=6aud0m<?)wVK?k>el4|SE4gsn9yWqH2eQ;W}^
zm-D9&zS33clQ|-zdmFs}=G~tc8q!Y|HMNed-@mVlI{O6>yw4Ql8E^4Zf%8?cY6ye5
zIEJPi9=gEYfK(Au>T8M=1v|$jBYt{`^Pbk0H!fnod<nZ``t|3wANvY>T=7cC@YWGM
zMRdYqvdMGII2y9@1|}T%Eu1zxHu2%_2ct?2L&nCcq(MqUSHSwme8WrKuUEP&x5i&i
zOAHgVD;U3gXN~QYoV+^Hy*WN#4}aw{P=8t8yH@Axinwuh<slmmm_oz`v6fQ<aj-M`
z5k4-OsGAafi^g5(a>oZI3B5zH?M!_TX6Be9Z#nkPWsKx2$$V4VPJO;+q^peNIFI=u
zI+BQhIfnkK+0+bN|5-Yd^!lM)tycftBrCUmCDUbR+}VFS`5_w2I^dPcU(3unhA-6W
zy*Lqy%2Zld8v^($j5Qtz42j*Jos4F`W^xvmYJGHll0h%nSK`M53}Cv?DE?I!8+#XO
zzL?{X!MqVSFg7j%p@n>1-`vtLPWL@4B<@nDTR8Flb%j#^vnw~`t<eLEX=^_pK1wIG
zfDVL*nf*O2w!Y~BCHbS8=6<E55*8EwGF_~1OY+yng+EeO1fVV`3PQrBdrY2O)2?-!
zGqxxftlZt#pd>bj6!`{M6KYtyvNORh?>8?2c@7r*?3F+T1!s1ml2$I0h!es7&H|<;
z>6qB1{%v!*lr`Ev8$`00TlME0xmp)Ks<gKY%{(fD8EBzM^i9I+VU&tcfIo|Xu6b|*
z)uMArqnc&Be5t|R$Qqv)2QhWz4?Uk}45%rulze1}FnBEfL@EuSD$fDAHv!p+@J1R|
zY<MpZ+@1__2i9SHhWva+B^#$ffQ?b>oAB@G?z$*JZ0K}IX;_%Ku@zkconZ(&(#?&I
z_cR7}URrktJqf=07p!m5<U<mv2ptClR>|&74OER=v(DZgWMX{sg6YC{;JTP0K`n=y
zV&|1?`cj@bVr2x@WVT>ynDZ1HBVL@1RLbG(<cwymWP>wz)RV#$mq&vMwihowo)$Jz
zpMW1w$NRiGYwTOxEFN^McPidvvOffe@!Rh42TXf4b{T}={S<^_Pk)Gng^m%AFM_8|
zelO{Xa8`Np$kCX8oifbFtzv80*9o~+*?>Sl5?*Sg88*`r>DE0pVd&j+#r!es(}zo%
zw9#gx`@G!-bH481m$nFhREB((zVh{L<g2#|QyY*%<L<!tg{SQF{=flaswpv<KBRyG
zoBsKASmG5JF@B-V95bfAjw_5#UF`jOdYr#PZuzjLXR5e7jM$9Ys6+pt6JRjMhDly4
zusQ8>-ID2*K(X7dY~8!_du}f1OT3W|WqHKWkw%lYjXL6RrKM>VTFb9piVZ2d9mHf1
zlb9nu(_5O&fyfli>jDdJ)3~SI>syj%_GHR@_%J9PVIA{)wMbg&S&`0baY*A)u+wJ=
u-EDD=rUKAknm=t6Mb4u-*IS@BqPEeT77ryQqhr5xClY*l=qKMdJ^u&wK)Buj

literal 0
HcmV?d00001

new file mode 100644
index 0000000000000000000000000000000000000000..1a63b42d987cf1f64f8c994f8f16b7a5d0af029b
GIT binary patch
literal 10098
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
U5*}Ari~r*8NT&)C8Q1qQpcbj`Pyhe`

literal 0
HcmV?d00001

new file mode 100644
index 0000000000000000000000000000000000000000..ba28e5010c3b7bb4a848ba143d01786aa80ba269
GIT binary patch
literal 10313
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
WM7|mb`|m1%%`ln(+h>5R`2PSJoD2*A

literal 0
HcmV?d00001
  

Comments

Mark Wielaard Nov. 1, 2023, 11:07 p.m. UTC | #1
Hi Omar,

On Wed, Sep 27, 2023 at 11:20:57AM -0700, Omar Sandoval wrote:
> The .debug_cu_index and .debug_tu_index sections in DWARF package files
> are basically hash tables mapping a unit's 8 byte signature to an offset
> and size in each section used by that unit [1].  Add support for parsing
> and doing lookups in the index sections.
> 
> We look up a unit in the index when we intern it and cache its hash
> table row in Dwarf_CU.

This looks good. Thanks for the various testcases.

Do I understand correctly that binutils dwp only does the DWARF4 GNU
extension and llvm-dwp only does the DWARF5 standardized format? Maybe
we should create a eu-dwp that does both.

It looks like you are very careful checking boundaries, but it would
be bad to do some fuzzing on this.

> Then, a new function, dwarf_cu_dwp_section_info,
> can be used to look up the section offsets and sizes for a unit.  This
> will mostly be used internally in libdw, but it will also be needed in
> static inline functions shared with eu-readelf.  Additionally, making it
> public it makes dwp support much easier for external tools that do their
> own low-level parsing of DWARF information, like drgn [2].

Although I am not against this new interface, I am not super
enthousiastic about it.

There is one odd part, DW_SECT_TYPES should be defined in dwarf.h with
the other DW_SECT constants, otherwise it cannot be used (unless you
define it yourself to 2).

For eu-readelf we don't really need it being public, it already cheats
a little and uses some (non-public) libdwP.h functions. That is
actually not great either. So if there is a public function that is
available that is actually preferred. But if it is just for
eu-readelf, I don't think it is really needed. And it seems you
haven't actually added the support to eu-readelf to print these
offsets.

It is fine to expose these offsets and sizes, but how exactly are you
using them in drgn? It seems we don't have any other interfaces in
libdw that you can then use these with.

Can we split off this public interface from the rest of this patch?
But then we also need to split off the tests. So maybe keep them together?

> 1: https://gcc.gnu.org/wiki/DebugFissionDWP#Format_of_the_CU_and_TU_Index_Sections
> 2: https://github.com/osandov/drgn
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  libdw/ChangeLog                     |  18 +-
>  libdw/Makefile.am                   |   2 +-
>  libdw/dwarf_cu_dwp_section_info.c   | 371 ++++++++++++++++++++++++++++
>  libdw/dwarf_end.c                   |   3 +
>  libdw/dwarf_error.c                 |   1 +
>  libdw/libdw.h                       |  23 ++
>  libdw/libdw.map                     |   5 +
>  libdw/libdwP.h                      |  36 +++
>  libdw/libdw_findcu.c                |   8 +
>  tests/.gitignore                    |   1 +
>  tests/ChangeLog                     |  13 +
>  tests/Makefile.am                   |  11 +-
>  tests/cu-dwp-section-info.c         |  74 ++++++
>  tests/run-cu-dwp-section-info.sh    | 168 +++++++++++++
>  tests/testfile-dwp-4-strict.bz2     | Bin 0 -> 4169 bytes
>  tests/testfile-dwp-4-strict.dwp.bz2 | Bin 0 -> 6871 bytes
>  tests/testfile-dwp-4.bz2            | Bin 0 -> 4194 bytes
>  tests/testfile-dwp-4.dwp.bz2        | Bin 0 -> 10098 bytes
>  tests/testfile-dwp-5.bz2            | Bin 0 -> 4223 bytes
>  tests/testfile-dwp-5.dwp.bz2        | Bin 0 -> 10313 bytes
>  tests/testfile-dwp.source           | 102 ++++++++
>  21 files changed, 831 insertions(+), 5 deletions(-)
>  create mode 100644 libdw/dwarf_cu_dwp_section_info.c
>  create mode 100644 tests/cu-dwp-section-info.c
>  create mode 100755 tests/run-cu-dwp-section-info.sh
>  create mode 100755 tests/testfile-dwp-4-strict.bz2
>  create mode 100644 tests/testfile-dwp-4-strict.dwp.bz2
>  create mode 100755 tests/testfile-dwp-4.bz2
>  create mode 100644 tests/testfile-dwp-4.dwp.bz2
>  create mode 100755 tests/testfile-dwp-5.bz2
>  create mode 100644 tests/testfile-dwp-5.dwp.bz2
>  create mode 100644 tests/testfile-dwp.source
> 
> diff --git a/libdw/ChangeLog b/libdw/ChangeLog
> index 52327688..1d229094 100644
> --- a/libdw/ChangeLog
> +++ b/libdw/ChangeLog
> @@ -20,7 +20,14 @@
>  	instead of dbg parameter, which is now unused.
>  	* libdwP.h (Dwarf_Macro_Op_Table): Replace is_64bit with address_size
>  	and offset_size.  Add dbg.
> -	Add IDX_debug_cu_index and IDX_debug_tu_index.
> +	(Dwarf): Add cu_index and tu_index.
> +	(Dwarf_CU): Add dwp_row.
> +	(Dwarf_Package_Index): New type.
> +	(DW_SECT_TYPES): New macro.
> +	(__libdw_dwp_find_unit): New declaration.
> +	(dwarf_cu_dwp_section_info): New INTDECL.
> +	Add IDX_debug_cu_index and IDX_debug_tu_index.  Add
> +	DWARF_E_UNKNOWN_SECTION.
>  	* dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_cu_index and
>  	IDX_debug_tu_index.
>  	(scn_to_string_section_idx): Ditto.
> @@ -28,6 +35,15 @@
>  	.zdebug_cu_index, and .zdebug_tu_index.
>  	(check_section): Change .dwo suffix matching to account for
>  	.debug_cu_index and .debug_tu_index.
> +	* Makefile.am (libdw_a_SOURCES): Add dwarf_cu_dwp_section_info.c.
> +	* dwarf_end.c (dwarf_end): Free dwarf->cu_index and dwarf->tu_index.
> +	* dwarf_error.c (errmsgs): Add DWARF_E_UNKNOWN_SECTION.
> +	* libdw.h (dwarf_cu_dwp_section_info): New declaration.
> +	* libdw.map (ELFUTILS_0.190): Add dwarf_cu_dwp_section_info.
> +	* libdw_findcu.c (__libdw_intern_next_unit): Call
> +	__libdw_dwp_find_unit, and use it to adjust abbrev_offset and assign
> +	newp->dwp_row.
> +	* dwarf_cu_dwp_section_info.c: New file.
>  
>  2023-02-22  Mark Wielaard  <mark@klomp.org>
>  
> diff --git a/libdw/Makefile.am b/libdw/Makefile.am
> index e548f38c..5363c02a 100644
> --- a/libdw/Makefile.am
> +++ b/libdw/Makefile.am
> @@ -93,7 +93,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
>  		  dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \
>  		  dwarf_die_addr_die.c dwarf_get_units.c \
>  		  libdw_find_split_unit.c dwarf_cu_info.c \
> -		  dwarf_next_lines.c
> +		  dwarf_next_lines.c dwarf_cu_dwp_section_info.c
>  
>  if MAINTAINER_MODE
>  BUILT_SOURCES = $(srcdir)/known-dwarf.h
> diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c
> new file mode 100644
> index 00000000..6766fb9a
> --- /dev/null
> +++ b/libdw/dwarf_cu_dwp_section_info.c
> @@ -0,0 +1,371 @@
> +/* Read DWARF package file index sections.
> +   Copyright (c) Meta Platforms, Inc. and affiliates.

Please do add a year (2023) here.

> +   This file is part of elfutils.
> +
> +   This file is free software; you can redistribute it and/or modify
> +   it under the terms of either
> +
> +     * the GNU Lesser General Public License as published by the Free
> +       Software Foundation; either version 3 of the License, or (at
> +       your option) any later version
> +
> +   or
> +
> +     * the GNU General Public License as published by the Free
> +       Software Foundation; either version 2 of the License, or (at
> +       your option) any later version
> +
> +   or both in parallel, as here.
> +
> +   elfutils is distributed in the hope that it will be useful, but
> +   WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   General Public License for more details.
> +
> +   You should have received copies of the GNU General Public License and
> +   the GNU Lesser General Public License along with this program.  If
> +   not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +
> +#include "libdwP.h"
> +
> +static Dwarf_Package_Index *
> +__libdw_read_package_index (Dwarf *dbg, bool tu)
> +{
> +  Elf_Data *data;
> +  if (tu)
> +    data = dbg->sectiondata[IDX_debug_tu_index];
> +  else
> +    data = dbg->sectiondata[IDX_debug_cu_index];
> +
> +  /* We need at least 16 bytes for the header.  */
> +  if (data == NULL || data->d_size < 16)
> +    {
> +    invalid:
> +      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +      return NULL;
> +    }
> +
> +  const unsigned char *datap = data->d_buf;
> +  const unsigned char *endp = datap + data->d_size;
> +  uint16_t version;
> +  /* In GNU DebugFission for DWARF 4, the version is 2 as a uword.  In the
> +     standardized DWARF 5 format, it is a uhalf followed by a padding uhalf.
> +     Check for both.  */
> +  if (read_4ubyte_unaligned (dbg, datap) == 2)
> +    version = 2;
> +  else
> +    {
> +      version = read_2ubyte_unaligned (dbg, datap);
> +      if (version != 5)
> +	{
> +	  __libdw_seterrno (DWARF_E_VERSION);
> +	  return NULL;
> +	}
> +    }
> +  datap += 4;

OK. the spec says padding should be zero, but we can ignore that.

> +  uint32_t section_count = read_4ubyte_unaligned_inc (dbg, datap);
> +  uint32_t unit_count = read_4ubyte_unaligned_inc (dbg, datap);
> +  uint32_t slot_count = read_4ubyte_unaligned_inc (dbg, datap);

OK, checked d_size >= 16 above.

> +  /* The specification has a stricter requirement that
> +     slot_count > 3 * unit_count / 2, but this is enough for us.  */
> +  if (slot_count < unit_count)
> +    goto invalid;
> +
> +  /* After the header, the section must contain:
> +
> +       8 byte signature per hash table slot
> +     + 4 byte index per hash table slot
> +     + Section offset table with 1 header row, 1 row per unit, 1 column per
> +       section, 4 bytes per field
> +     + Section size table with 1 row per unit, 1 column per section, 4 bytes
> +       per field
> +
> +     We have to be careful about overflow when checking this.  */
> +  const unsigned char *hash_table = datap;
> +  if ((size_t) (endp - hash_table) < (uint64_t) slot_count * 12)
> +    goto invalid;
> +  const unsigned char *indices = hash_table + (size_t) slot_count * 8;
> +  const unsigned char *sections = indices + (size_t) slot_count * 4;
> +  if ((size_t) (endp - sections) < (uint64_t) section_count * 4)
> +    goto invalid;
> +  const unsigned char *section_offsets = sections + (size_t) section_count * 4;
> +  if ((uint64_t) unit_count * section_count > UINT64_MAX / 8
> +      || ((size_t) (endp - section_offsets)
> +	  < (uint64_t) unit_count * section_count * 8))
> +    goto invalid;
> +  const unsigned char *section_sizes
> +    = section_offsets + (uint64_t) unit_count * section_count * 4;

OK.

> +  Dwarf_Package_Index *index = malloc (sizeof (*index));
> +  if (index == NULL)
> +    {
> +      __libdw_seterrno (DWARF_E_NOMEM);
> +      return NULL;
> +    }
> +
> +  index->dbg = dbg;
> +  /* Set absent sections to UINT32_MAX.  */
> +  memset (index->sections, 0xff, sizeof (index->sections));

OK, although I would have preferred a simple for loop and let the
compiler optimize it.

> +  for (size_t i = 0; i < section_count; i++)
> +    {
> +      uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4);
> +      /* 2 is DW_SECT_TYPES in version 2 and reserved in version 5.  We ignore
> +         it for version 5.
> +	 5 is DW_SECT_LOC in version 2 and DW_SECT_LOCLISTS in version 5.  We
> +	 use the same index for both.
> +	 7 is DW_SECT_MACINFO in version 2 and DW_SECT_MACRO in version 5.  We
> +	 use the same index for both.
> +	 8 is DW_SECT_MACRO in version 2 and DW_SECT_RNGLISTS in version 5.  We
> +	 use the same index for version 2's DW_SECT_MACRO as version 2's
> +	 DW_SECT_MACINFO/version 5's DW_SECT_MACRO.
> +	 We ignore unknown sections.  */
> +      if (section == 0)
> +	continue;
> +      if (version == 2)
> +	{
> +	  if (section > 8)
> +	    continue;
> +	  else if (section == 8)
> +	    section = DW_SECT_MACRO;
> +	}
> +      else if (section == 2
> +	       || (section
> +		   > sizeof (index->sections) / sizeof (index->sections[0])))
> +	continue;
> +      index->sections[section - 1] = i;
> +    }
> +
> +  /* DW_SECT_INFO (or DW_SECT_TYPES for DWARF 4 type units) and DW_SECT_ABBREV
> +     are required.  */
> +  if (((!tu || dbg->sectiondata[IDX_debug_types] == NULL)
> +       && index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
> +      || (tu && dbg->sectiondata[IDX_debug_types] != NULL
> +	  && index->sections[DW_SECT_TYPES - 1] == UINT32_MAX)
> +      || index->sections[DW_SECT_ABBREV - 1] == UINT32_MAX)
> +    {
> +      free (index);
> +      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +      return NULL;
> +    }
> +
> +  index->section_count = section_count;
> +  index->unit_count = unit_count;
> +  index->slot_count = slot_count;
> +  index->last_unit_found = 0;
> +  index->hash_table = hash_table;
> +  index->indices = indices;
> +  index->section_offsets = section_offsets;
> +  index->section_sizes = section_sizes;
> +
> +  return index;
> +}

OK.

> +static Dwarf_Package_Index *
> +__libdw_package_index (Dwarf *dbg, bool tu)
> +{
> +  if (tu && dbg->tu_index != NULL)
> +    return dbg->tu_index;
> +  else if (!tu && dbg->cu_index != NULL)
> +    return dbg->cu_index;
> +
> +  Dwarf_Package_Index *index = __libdw_read_package_index (dbg, tu);
> +  if (index == NULL)
> +    return NULL;
> +
> +  if (tu)
> +    dbg->tu_index = index;
> +  else
> +    dbg->cu_index = index;
> +  return index;
> +}

OK. Might need locking for thread-safety patch Heather is working on.

> +static int
> +__libdw_dwp_unit_row (Dwarf_Package_Index *index, uint64_t unit_id,
> +		      uint32_t *unit_rowp)
> +{
> +  if (index == NULL)
> +    return -1;
> +
> +  uint32_t hash = unit_id;
> +  uint32_t hash2 = (unit_id >> 32) | 1;
> +  /* Only check each slot once.  */
> +  for (uint32_t n = index->slot_count; n-- > 0; )
> +    {
> +      size_t slot = hash & (index->slot_count - 1);
> +      uint64_t sig = read_8ubyte_unaligned (index->dbg,
> +					    index->hash_table + slot * 8);
> +      if (sig == unit_id)
> +	{
> +	  uint32_t row = read_4ubyte_unaligned (index->dbg,
> +						index->indices + slot * 4);
> +	  if (row > index->unit_count)
> +	    {
> +	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +	      return -1;
> +	    }
> +	  *unit_rowp = row;
> +	  return 0;
> +	}
> +      else if (sig == 0
> +	       && read_4ubyte_unaligned (index->dbg,
> +					 index->indices + slot * 4) == 0)
> +	break;
> +      hash += hash2;
> +    }
> +  *unit_rowp = 0;
> +  return 0;
> +}

OK.

> +static int
> +__libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row,
> +			  unsigned int section, Dwarf_Off *offsetp,
> +			  Dwarf_Off *sizep)
> +{
> +  if (index == NULL)
> +    return -1;
> +  if (unit_row == 0)
> +    {
> +      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +      return -1;
> +    }

OK, but why isn't the caller checking this?

> +  if (index->sections[section - 1] == UINT32_MAX)
> +    {
> +      if (offsetp != NULL)
> +	*offsetp = 0;
> +      if (sizep != NULL)
> +	*sizep = 0;
> +      return 0;
> +    }
> +  size_t i = (size_t)(unit_row - 1) * index->section_count
> +	     + index->sections[section - 1];
> +  if (offsetp != NULL)
> +    *offsetp = read_4ubyte_unaligned (index->dbg,
> +				      index->section_offsets + i * 4);
> +  if (sizep != NULL)
> +    *sizep = read_4ubyte_unaligned (index->dbg,
> +				    index->section_sizes + i * 4);
> +  return 0;
> +}

OK.

> +int
> +internal_function
> +__libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
> +		       uint16_t version, uint8_t unit_type, uint64_t unit_id8,
> +		       uint32_t *unit_rowp, Dwarf_Off *abbrev_offsetp)
> +{
> +  if (version >= 5
> +      && unit_type != DW_UT_split_compile && unit_type != DW_UT_split_type)
> +    {
> +    not_dwp:
> +      *unit_rowp = 0;
> +      *abbrev_offsetp = 0;
> +      return 0;
> +    }
> +  bool tu = unit_type == DW_UT_split_type || debug_types;
> +  if (dbg->sectiondata[tu ? IDX_debug_tu_index : IDX_debug_cu_index] == NULL)
> +    goto not_dwp;

OK. So this is for "normal" dwo, where the offset is zero,

> +  Dwarf_Package_Index *index = __libdw_package_index (dbg, tu);
> +  if (index == NULL)
> +    return -1;
> +
> +  /* This is always called for ascending offsets.  The most obvious way for a
> +     producer to generate the section offset table is sorted by offset; both
> +     GNU dwp and llvm-dwp do this.  In this common case, we can avoid the full
> +     lookup.  */
> +  if (index->last_unit_found < index->unit_count)
> +    {
> +      Dwarf_Off offset, size;
> +      if (__libdw_dwp_section_info (index, index->last_unit_found + 1,
> +				    debug_types ? DW_SECT_TYPES : DW_SECT_INFO,
> +				    &offset, &size) != 0)
> +	return -1;
> +      if (offset <= off && off - offset < size)
> +	{
> +	  *unit_rowp = ++index->last_unit_found;
> +	  goto done;
> +	}
> +      else
> +	/* The units are not sorted. Don't try again.  */
> +	index->last_unit_found = index->unit_count;
> +    }

OK. Again another point to have some locking for the thread-safety case.

> +  if (version >= 5 || debug_types)
> +    {
> +      /* In DWARF 5 and in type units, the unit signature is available in the
> +         unit header.  */
> +      if (__libdw_dwp_unit_row (index, unit_id8, unit_rowp) != 0)
> +	return -1;
> +    }
> +  else
> +    {
> +      /* In DWARF 4 compilation units, the unit signature is an attribute.  We
> +	 can't parse attributes in the split unit until we get the abbreviation
> +	 table offset from the package index, which is a chicken-and-egg
> +	 problem.  We could get the signature from the skeleton unit, but that
> +	 may not be available.
> +
> +	 Instead, we resort to a linear scan through the section offset table.
> +	 Finding all units is therefore quadratic in the number of units.
> +	 However, this will likely never be needed in practice because of the
> +	 sorted fast path above.  If this ceases to be the case, we can try to
> +	 plumb through the skeleton unit's signature when it is available, or
> +	 build a sorted lookup table for binary search.  */
> +      if (index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
> +	{
> +	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +	  return -1;
> +	}
> +      for (uint32_t i = 0; i < index->unit_count; i++)
> +	{
> +	  Dwarf_Off offset, size;
> +	  __libdw_dwp_section_info (index, i + 1, DW_SECT_INFO, &offset,
> +				    &size);
> +	  if (offset <= off && off - offset < size)
> +	    {
> +	      *unit_rowp = i + 1;
> +	      goto done;
> +	    }
> +	}
> +      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> +      return -1;
> +    }
> +
> + done:
> +  return __libdw_dwp_section_info (index, *unit_rowp, DW_SECT_ABBREV,
> +				   abbrev_offsetp, NULL);
> +}

OK.

> +int
> +dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
> +			   Dwarf_Off *offsetp, Dwarf_Off *sizep)
> +{
> +  if (cu == NULL)
> +    return -1;
> +  if (section < DW_SECT_INFO || section > DW_SECT_RNGLISTS)
> +    {
> +      __libdw_seterrno (DWARF_E_UNKNOWN_SECTION);
> +      return -1;
> +    }
> +  if (cu->dwp_row == 0)
> +    {
> +      if (offsetp != NULL)
> +	*offsetp = 0;
> +      if (sizep != NULL)
> +	*sizep = 0;
> +      return 0;
> +    }
> +  else
> +    {
> +      Dwarf_Package_Index *index
> +	= cu->unit_type == DW_UT_split_compile
> +	? cu->dbg->cu_index : cu->dbg->tu_index;
> +      return __libdw_dwp_section_info (index, cu->dwp_row, section, offsetp,
> +				       sizep);
> +    }
> +}
> +INTDEF(dwarf_cu_dwp_section_info)

OK. Although I would like to see this public function as a separate
patch if possible.

> diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
> index 8dd075cf..18a419ce 100644
> --- a/libdw/dwarf_end.c
> +++ b/libdw/dwarf_end.c
> @@ -77,6 +77,9 @@ dwarf_end (Dwarf *dwarf)
>  {
>    if (dwarf != NULL)
>      {
> +      free (dwarf->tu_index);
> +      free (dwarf->cu_index);
> +
>        if (dwarf->cfi != NULL)
>  	/* Clean up the CFI cache.  */
>  	__libdw_destroy_frame_cache (dwarf->cfi);

OK.

> diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
> index 46ea16b3..0123cfa2 100644
> --- a/libdw/dwarf_error.c
> +++ b/libdw/dwarf_error.c
> @@ -102,6 +102,7 @@ static const char *errmsgs[] =
>      [DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"),
>      [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"),
>      [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"),
> +    [DWARF_E_UNKNOWN_SECTION] = N_("unknown section"),
>    };
>  #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))

OK

> diff --git a/libdw/libdw.h b/libdw/libdw.h
> index 64d1689a..545ad043 100644
> --- a/libdw/libdw.h
> +++ b/libdw/libdw.h
> @@ -1081,6 +1081,29 @@ extern int dwarf_frame_register (Dwarf_Frame *frame, int regno,
>    __nonnull_attribute__ (3, 4, 5);
>  
>  
> +/* Return offset and/or size of CU's contribution to SECTION in a DWARF package
> +   file.
> +
> +   If CU is not from a DWARF package file, the file does not have SECTION, or CU
> +   does not contribute to SECTION, then *SIZEP is set to 0.
> +
> +   SECTION is a DW_SECT section identifier.  Note that the original GNU DWARF
> +   package file extension for DWARF 4 used slightly different section
> +   identifiers.  This function uses the standardized section identifiers and
> +   maps the GNU DWARF 4 identifiers to their standard DWARF 5 analogues:
> +   DW_SECT_LOCLISTS (5) refers to .debug_locs.dwo for DWARF 4.
> +   DW_SECT_MACRO (7) refers to .debug_macinfo.dwo for DWARF 4 or
> +   .debug_macro.dwo for the GNU .debug_macro extension for DWARF 4 (section
> +   identifier 8 is DW_SECT_RNGLISTS in DWARF 5, NOT DW_SECT_MACRO like in the
> +   GNU extension.)
> +   .debug_types.dwo does not have a DWARF 5 equivalent, so this function accepts
> +   the original DW_SECT_TYPES (2).
> +
> +   Returns 0 for success or -1 for errors.  OFFSETP and SIZEP may be NULL.  */
> +extern int dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
> +				      Dwarf_Off *offsetp, Dwarf_Off *sizep);
> +
> +
>  /* Return error code of last failing function call.  This value is kept
>     separately for each thread.  */
>  extern int dwarf_errno (void);

OK, but see above (rather have this as a separate patch) and below
(DW_SECT_TYPES not publicly defined).

> diff --git a/libdw/libdw.map b/libdw/libdw.map
> index 5331ad45..2104b007 100644
> --- a/libdw/libdw.map
> +++ b/libdw/libdw.map
> @@ -373,3 +373,8 @@ ELFUTILS_0.188 {
>      dwfl_frame_reg;
>      dwfl_report_offline_memory;
>  } ELFUTILS_0.186;
> +
> +ELFUTILS_0.190 {
> +  global:
> +    dwarf_cu_dwp_section_info;
> +} ELFUTILS_0.188;

OK, but ,,, etc,

> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index 0c44d40c..13ca58ce 100644
> --- a/libdw/libdwP.h
> +++ b/libdw/libdwP.h
> @@ -147,6 +147,7 @@ enum
>    DWARF_E_NOT_CUDIE,
>    DWARF_E_UNKNOWN_LANGUAGE,
>    DWARF_E_NO_DEBUG_ADDR,
> +  DWARF_E_UNKNOWN_SECTION,
>  };
>  

OK.

> @@ -227,6 +228,11 @@ struct Dwarf
>    /* Cached info from the CFI section.  */
>    struct Dwarf_CFI_s *cfi;
>  
> +  /* DWARF package file CU index section.  */
> +  struct Dwarf_Package_Index_s *cu_index;
> +  /* DWARF package file TU index section.  */
> +  struct Dwarf_Package_Index_s *tu_index;
> +
>    /* Fake loc CU.  Used when synthesizing attributes for Dwarf_Ops that
>       came from a location list entry in dwarf_getlocation_attr.
>       Depending on version this is the .debug_loc or .debug_loclists
> @@ -339,6 +345,26 @@ struct Dwarf_Aranges_s
>    } info[0];
>  };

OK.
 
> +/* DWARF package file unit index.  */
> +typedef struct Dwarf_Package_Index_s
> +{
> +  Dwarf *dbg;
> +  uint32_t section_count;
> +  uint32_t unit_count;
> +  uint32_t slot_count;
> +  /* Mapping from DW_SECT_* - 1 to column number in the section tables, or
> +     UINT32_MAX if not present.  */
> +  uint32_t sections[DW_SECT_RNGLISTS];
> +  /* Row number of last unit found in the index.  */
> +  uint32_t last_unit_found;
> +  const unsigned char *hash_table;
> +  const unsigned char *indices;
> +  const unsigned char *section_offsets;
> +  const unsigned char *section_sizes;
> +} Dwarf_Package_Index;

OK.

> +/* Used in GNU DebugFission for DWARF 4.  Reserved in DWARF 5.  */
> +#define DW_SECT_TYPES 2

Should be in dwarf.h.

>  /* CU representation.  */
>  struct Dwarf_CU
> @@ -346,6 +372,8 @@ struct Dwarf_CU
>    Dwarf *dbg;
>    Dwarf_Off start;
>    Dwarf_Off end;
> +  /* Row number of this unit in DWARF package file index.  */
> +  uint32_t dwp_row;
>    uint8_t address_size;
>    uint8_t offset_size;
>    uint16_t version;

OK.

> @@ -680,6 +708,13 @@ extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
>  extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu)
>       internal_function;
>  
> +/* Find a unit in a DWARF package file for __libdw_intern_next_unit.  */
> +extern int __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
> +				  uint16_t version, uint8_t unit_type,
> +				  uint64_t unit_id8, uint32_t *unit_rowp,
> +				  Dwarf_Off *abbrev_offsetp)
> +     __nonnull_attribute__ (1, 7, 8) internal_function;
> +
>  /* Get abbreviation with given code.  */
>  extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
>  					 unsigned int code)

OK.

> @@ -1380,6 +1415,7 @@ INTDECL (dwarf_attr_integrate)
>  INTDECL (dwarf_begin)
>  INTDECL (dwarf_begin_elf)
>  INTDECL (dwarf_child)
> +INTDECL (dwarf_cu_dwp_section_info)
>  INTDECL (dwarf_default_lower_bound)
>  INTDECL (dwarf_dieoffset)
>  INTDECL (dwarf_diename)

OM, but etc.

> diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
> index ed744231..6c7dcfb5 100644
> --- a/libdw/libdw_findcu.c
> +++ b/libdw/libdw_findcu.c
> @@ -143,6 +143,13 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
>    if (unlikely (*offsetp > data->d_size))
>      *offsetp = data->d_size;
>  
> +  uint32_t dwp_row;
> +  Dwarf_Off dwp_abbrev_offset;
> +  if (__libdw_dwp_find_unit (dbg, debug_types, oldoff, version, unit_type,
> +			     unit_id8, &dwp_row, &dwp_abbrev_offset) != 0)
> +    return NULL;
> +  abbrev_offset += dwp_abbrev_offset;
> +
>    /* Create an entry for this CU.  */
>    struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
>  
> @@ -150,6 +157,7 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
>    newp->sec_idx = sec_idx;
>    newp->start = oldoff;
>    newp->end = *offsetp;
> +  newp->dwp_row = dwp_row;
>    newp->address_size = address_size;
>    newp->offset_size = offset_size;
>    newp->version = version;

OK.

> diff --git a/tests/.gitignore b/tests/.gitignore
> index b9aa22ba..e9a61a2d 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -28,6 +28,7 @@
>  /backtrace-dwarf
>  /buildid
>  /core-dump-backtrace.lock
> +/cu-dwp-section-info
>  /debugaltlink
>  /debuginfod_build_id_find
>  /debuglink
> diff --git a/tests/ChangeLog b/tests/ChangeLog
> index f934c114..687a9f32 100644
> --- a/tests/ChangeLog
> +++ b/tests/ChangeLog
> @@ -1,6 +1,19 @@
>  2023-09-27  Omar Sandoval  <osandov@fb.com>
>  
>  	* run-varlocs.sh: Add entry PC to split units.
> +	* Makefile.am (check_PROGRAMS): Add cu-dwp-section-info.
> +	(TESTS): Add run-cu-dwp-section-info.sh
> +	(EXTRA_DIST): Add run-cu-dwp-section-info.sh and new test files.
> +	(cu_dwp_section_info_LDADD): New variable.
> +	* cu-dwp-section-info.c: New test.
> +	* run-cu-dwp-section-info.sh: New test.
> +	* testfile-dwp-4-strict.bz2: New test file.
> +	* testfile-dwp-4-strict.dwp.bz2: New test file.
> +	* testfile-dwp-4.bz2: New test file.
> +	* testfile-dwp-4.dwp.bz2: New test file.
> +	* testfile-dwp-5.bz2: New test file.
> +	* testfile-dwp-5.dwp.bz2: New test file.
> +	* testfile-dwp.source: New file.
>  
>  2023-04-21  Frank Ch. Eigler <fche@redhat.com>
>  
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 32b18e6e..0938882b 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -62,7 +62,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
>  		  dwelf_elf_e_machine_string \
>  		  getphdrnum leb128 read_unaligned \
>  		  msg_tst system-elf-libelf-test system-elf-gelf-test \
> -		  nvidia_extended_linemap_libdw \
> +		  nvidia_extended_linemap_libdw cu-dwp-section-info \
>  		  $(asm_TESTS)
>  
>  asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
> @@ -211,7 +211,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
>  	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
>  	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
>  	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
> -	run-readelf-Dd.sh
> +	run-readelf-Dd.sh run-cu-dwp-section-info.sh
>  
>  if !BIARCH
>  export ELFUTILS_DISABLE_BIARCH = 1
> @@ -629,7 +629,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
>  	     run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
>  	     testfile_nvidia_linemap.bz2 \
>  	     testfile-largealign.o.bz2 run-strip-largealign.sh \
> -	     run-funcretval++11.sh
> +	     run-funcretval++11.sh \
> +	     testfile-dwp-4.bz2 testfile-dwp-4.dwp.bz2 \
> +	     testfile-dwp-4-strict.bz2 testfile-dwp-4-strict.dwp.bz2 \
> +	     testfile-dwp-5.bz2 testfile-dwp-5.dwp.bz2 testfile-dwp.source \
> +	     run-cu-dwp-section-info.sh
>  
>  
>  if USE_VALGRIND
> @@ -804,6 +808,7 @@ getphdrnum_LDADD = $(libelf) $(libdw)
>  leb128_LDADD = $(libelf) $(libdw)
>  read_unaligned_LDADD = $(libelf) $(libdw)
>  nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
> +cu_dwp_section_info_LDADD = $(libdw)
>  
>  # We want to test the libelf headers against the system elf.h header.
>  # Don't include any -I CPPFLAGS. Except when we install our own elf.h.

OK.

> diff --git a/tests/cu-dwp-section-info.c b/tests/cu-dwp-section-info.c
> new file mode 100644
> index 00000000..e92f35b6
> --- /dev/null
> +++ b/tests/cu-dwp-section-info.c
> @@ -0,0 +1,74 @@
> +/* Test program for dwarf_cu_dwp_section_info
> +   Copyright (c) Meta Platforms, Inc. and affiliates.

Should have a year (2023).

> +   This file is part of elfutils.
> +
> +   This file is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   elfutils is distributed in the hope that it will be useful, but
> +   WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif
> +#include <stdio.h>
> +#include <inttypes.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#include <dwarf.h>
> +#include ELFUTILS_HEADER(dw)
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  for (int i = 1; i < argc; i++)
> +    {
> +      printf ("file: %s\n", argv[i]);
> +      int fd = open (argv[i], O_RDONLY);
> +      Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ);
> +      if (dbg == NULL)
> +	{
> +	  printf ("%s not usable: %s\n", argv[i], dwarf_errmsg (-1));
> +	  return -1;
> +	}
> +
> +      Dwarf_CU *cu = NULL;
> +      while (dwarf_get_units (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0)
> +	{
> +#define SECTION_INFO(section) do {					\
> +	    printf (#section ": ");					\
> +	    Dwarf_Off offset, size;					\
> +	    if (dwarf_cu_dwp_section_info (cu, DW_SECT_##section,	\
> +					   &offset, &size) == 0)	\
> +	      printf ("0x%" PRIx64 " 0x%" PRIx64 "\n", offset, size);	\
> +	    else							\
> +	      printf ("%s\n", dwarf_errmsg (-1));			\
> +	  } while (0)
> +	  SECTION_INFO (INFO);
> +#define DW_SECT_TYPES 2
> +	  SECTION_INFO (TYPES);
> +	  SECTION_INFO (ABBREV);
> +	  SECTION_INFO (LINE);
> +	  SECTION_INFO (LOCLISTS);
> +	  SECTION_INFO (STR_OFFSETS);
> +	  SECTION_INFO (MACRO);
> +	  SECTION_INFO (RNGLISTS);
> +	  printf ("\n");
> +	}
> +
> +      dwarf_end (dbg);
> +      close (fd);
> +    }
> +
> +  return 0;
> +}

OK. But now I finally see the actual use of the new public function.
So

> diff --git a/tests/run-cu-dwp-section-info.sh b/tests/run-cu-dwp-section-info.sh
> new file mode 100755
> index 00000000..f8596f2c
> --- /dev/null
> +++ b/tests/run-cu-dwp-section-info.sh
> @@ -0,0 +1,168 @@
> +#! /bin/sh
> +# Copyright (c) Meta Platforms, Inc. and affiliates.
> +# This file is part of elfutils.
> +#
> +# This file is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# elfutils is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +. $srcdir/test-subr.sh
> +
> +# See testfile-dwp.source.
> +testfiles testfile-dwp-5.dwp testfile-dwp-4.dwp testfile-dwp-4-strict.dwp
> +
> +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-5.dwp << EOF
> +file: testfile-dwp-5.dwp
> +INFO: 0x0 0x70
> +TYPES: 0x0 0x0
> +ABBREV: 0x0 0x160
> +LINE: 0x0 0x7f
> +LOCLISTS: 0x0 0xdb
> +STR_OFFSETS: 0x0 0x75c
> +MACRO: 0x0 0x6c6
> +RNGLISTS: 0x0 0x22
> +
> +INFO: 0x70 0x108
> +TYPES: 0x0 0x0
> +ABBREV: 0x0 0x160
> +LINE: 0x0 0x7f
> +LOCLISTS: 0x0 0xdb
> +STR_OFFSETS: 0x0 0x75c
> +MACRO: 0x0 0x6c6
> +RNGLISTS: 0x0 0x22
> +
> +INFO: 0x178 0x6e
> +TYPES: 0x0 0x0
> +ABBREV: 0x160 0xca
> +LINE: 0x7f 0x7f
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x75c 0x758
> +MACRO: 0x6c6 0x6c5
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x1e6 0x78
> +TYPES: 0x0 0x0
> +ABBREV: 0x160 0xca
> +LINE: 0x7f 0x7f
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x75c 0x758
> +MACRO: 0x6c6 0x6c5
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x25e 0x193
> +TYPES: 0x0 0x0
> +ABBREV: 0x22a 0x18a
> +LINE: 0xfe 0x81
> +LOCLISTS: 0xdb 0xc9
> +STR_OFFSETS: 0xeb4 0x77c
> +MACRO: 0xd8b 0x6c6
> +RNGLISTS: 0x22 0x43
> +
> +EOF
> +
> +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4.dwp << EOF
> +file: testfile-dwp-4.dwp
> +INFO: 0x0 0x11e
> +TYPES: 0x0 0x0
> +ABBREV: 0x0 0x172
> +LINE: 0x0 0x52
> +LOCLISTS: 0x0 0x11b
> +STR_OFFSETS: 0x0 0x754
> +MACRO: 0x0 0x6c7
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x11e 0x76
> +TYPES: 0x0 0x0
> +ABBREV: 0x172 0xd7
> +LINE: 0x52 0x52
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x754 0x750
> +MACRO: 0x6c7 0x6c6
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x194 0x1c5
> +TYPES: 0x0 0x0
> +ABBREV: 0x249 0x19e
> +LINE: 0xa4 0x53
> +LOCLISTS: 0x11b 0xf1
> +STR_OFFSETS: 0xea4 0x774
> +MACRO: 0xd8d 0x6c7
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x0 0x0
> +TYPES: 0x0 0x6f
> +ABBREV: 0x0 0x172
> +LINE: 0x0 0x52
> +LOCLISTS: 0x0 0x11b
> +STR_OFFSETS: 0x0 0x754
> +MACRO: 0x0 0x6c7
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x0 0x0
> +TYPES: 0x6f 0x6d
> +ABBREV: 0x172 0xd7
> +LINE: 0x52 0x52
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x754 0x750
> +MACRO: 0x6c7 0x6c6
> +RNGLISTS: 0x0 0x0
> +
> +EOF
> +
> +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4-strict.dwp << EOF
> +file: testfile-dwp-4-strict.dwp
> +INFO: 0x0 0x105
> +TYPES: 0x0 0x0
> +ABBREV: 0x0 0x15f
> +LINE: 0x0 0x52
> +LOCLISTS: 0x0 0xe2
> +STR_OFFSETS: 0x0 0x24
> +MACRO: 0x0 0x38e4
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x105 0x72
> +TYPES: 0x0 0x0
> +ABBREV: 0x15f 0xd3
> +LINE: 0x52 0x52
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x24 0x20
> +MACRO: 0x38e4 0x38db
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x177 0x17b
> +TYPES: 0x0 0x0
> +ABBREV: 0x232 0x157
> +LINE: 0xa4 0x53
> +LOCLISTS: 0xe2 0xb1
> +STR_OFFSETS: 0x44 0x44
> +MACRO: 0x71bf 0x38f5
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x0 0x0
> +TYPES: 0x0 0x6e
> +ABBREV: 0x0 0x15f
> +LINE: 0x0 0x52
> +LOCLISTS: 0x0 0xe2
> +STR_OFFSETS: 0x0 0x24
> +MACRO: 0x0 0x38e4
> +RNGLISTS: 0x0 0x0
> +
> +INFO: 0x0 0x0
> +TYPES: 0x6e 0x6b
> +ABBREV: 0x15f 0xd3
> +LINE: 0x52 0x52
> +LOCLISTS: 0x0 0x0
> +STR_OFFSETS: 0x24 0x20
> +MACRO: 0x38e4 0x38db
> +RNGLISTS: 0x0 0x0
> +
> +EOF

OK
 
> diff --git a/tests/testfile-dwp.source b/tests/testfile-dwp.source
> new file mode 100644
> index 00000000..b0b0a97c
> --- /dev/null
> +++ b/tests/testfile-dwp.source
> @@ -0,0 +1,102 @@
> +# Nonsensical program used to generate example DWARF package files with type
> +# units, location lists, range lists, and macros.
> +
> +# = foobar.h =
> +
> +struct Foo
> +{
> +  int a, b;
> +  int foo ();
> +};
> +
> +struct Bar
> +{
> +  long a, b;
> +  long bar ();
> +};
> +
> +#define FROB(x) ((x) ^ 0x2a2a2a2a)
> +#define FRY(x) ((x) * 0x100000001b3)
> +
> +inline long
> +fibonacci (unsigned int n)
> +{
> +  if (n == 0)
> +    return 0;
> +  else
> +    {
> +      long a = 0;
> +      long b = 1;
> +      for (unsigned int i = 2; i < n; i++)
> +	{
> +	  long tmp = a + b;
> +	  a = b;
> +	  b = tmp;
> +	}
> +      return b;
> +    }
> +}
> +
> +# = foo.cc =
> +
> +#include "foobar.h"
> +
> +#define ZERO() (1 - 1)
> +
> +int
> +x_x (int x)
> +{
> +  for (int i = x; i > ZERO(); i--)
> +    x *= x;
> +  return x;
> +}
> +
> +int
> +Foo::foo ()
> +{
> +  int x = a;
> +  if (a > b)
> +    x -= b;
> +  return FROB (x_x (x));
> +}
> +
> +# = bar.cc =
> +
> +#include "foobar.h"
> +
> +#define ONE 1
> +
> +long
> +Bar::bar ()
> +{
> +  if (a == b)
> +    return ONE;
> +  else
> +    return a > b ? b : a;
> +}
> +
> +# = main.cc =
> +
> +#include "foobar.h"
> +
> +#define MAIN_ARGS int argc, char **argv
> +
> +int
> +main(MAIN_ARGS)
> +{
> +  struct Foo myfoo { argc, FROB (argc) };
> +  struct Bar mybar { fibonacci (argc), FRY (argc) };
> +  return myfoo.foo() + mybar.bar();
> +}
> +
> +# Built with GCC at commit 80048aa13a6b ("debug/111409 - don't generate COMDAT
> +# macro sections for split DWARF").
> +$ g++ -gdwarf-5 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-5
> +# GNU dwp as of binutils 2.41 only supports DWARF 4.
> +$ llvm-dwp -e testfile-dwp-5 -o testfile-dwp-5.dwp
> +
> +$ g++ -gdwarf-4 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4
> +$ dwp -e testfile-dwp-4
> +
> +$ g++ -gdwarf-4 -gstrict-dwarf -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4-strict
> +$ dwp -e testfile-dwp-4-strict

Thanks for including this.
Added to EXTRA_DIST. OK.
  
Omar Sandoval Nov. 7, 2023, 6:45 a.m. UTC | #2
On Thu, Nov 02, 2023 at 12:07:04AM +0100, Mark Wielaard wrote:
> Hi Omar,
> 
> On Wed, Sep 27, 2023 at 11:20:57AM -0700, Omar Sandoval wrote:
> > The .debug_cu_index and .debug_tu_index sections in DWARF package files
> > are basically hash tables mapping a unit's 8 byte signature to an offset
> > and size in each section used by that unit [1].  Add support for parsing
> > and doing lookups in the index sections.
> > 
> > We look up a unit in the index when we intern it and cache its hash
> > table row in Dwarf_CU.
> 
> This looks good. Thanks for the various testcases.
> 
> Do I understand correctly that binutils dwp only does the DWARF4 GNU
> extension and llvm-dwp only does the DWARF5 standardized format? Maybe
> we should create a eu-dwp that does both.

llvm-dwp can do both the DWARF4 GNU format and the DWARF5 format.
binutils dwp can only do DWARF4.

> It looks like you are very careful checking boundaries, but it would
> be bad to do some fuzzing on this.
> 
> > Then, a new function, dwarf_cu_dwp_section_info,
> > can be used to look up the section offsets and sizes for a unit.  This
> > will mostly be used internally in libdw, but it will also be needed in
> > static inline functions shared with eu-readelf.  Additionally, making it
> > public it makes dwp support much easier for external tools that do their
> > own low-level parsing of DWARF information, like drgn [2].
> 
> Although I am not against this new interface, I am not super
> enthousiastic about it.
> 
> There is one odd part, DW_SECT_TYPES should be defined in dwarf.h with
> the other DW_SECT constants, otherwise it cannot be used (unless you
> define it yourself to 2).

Yeah, I wasn't sure about adding it or not since it's not technically
defined in the DWARF5 standard. But if we can count on future DWARF
standards not reusing the value 2 (which we probably can), I agree that
it seems better to add it to dwarf.h.

> For eu-readelf we don't really need it being public, it already cheats
> a little and uses some (non-public) libdwP.h functions. That is
> actually not great either. So if there is a public function that is
> available that is actually preferred. But if it is just for
> eu-readelf, I don't think it is really needed. And it seems you
> haven't actually added the support to eu-readelf to print these
> offsets.

Right, eu-readelf only uses it indirectly via the static inline
functions modified in patch 13. It looks like eu-readelf dynamically
links to libdw (on Fedora, at least), so either some part of the dwp
implementation needs to be exported, or a big chunk of it needs to be
inlined in libdwP.h, right?

Either way, I'd still love to have this interface for drgn.

> It is fine to expose these offsets and sizes, but how exactly are you
> using them in drgn? It seems we don't have any other interfaces in
> libdw that you can then use these with.

Here's the branch I linked to in my cover letter:
https://github.com/osandov/drgn/tree/dwp. I need this interface for the
couple of places where drgn parses DWARF itself.

One of those places is in the global name indexing step drgn does at
startup. We do this by enumerating all CUs using libdw, then using our
own purpose-built DWARF parser to do a fast, parallelized scan.

Our bespoke parser needs to know the base of the abbreviation table and
the string offsets table, which is easy without dwp. This interface also
makes it easy with dwp. Without this interface, I'd need to reimplement
the CU index parser in drgn :(

> Can we split off this public interface from the rest of this patch?
> But then we also need to split off the tests. So maybe keep them together?

Yeah, I included the interface in this patch exactly so that I could
test the implementation in the same patch. I'm happy to split that up
however you'd prefer.

[snip]

> > +  Dwarf_Package_Index *index = malloc (sizeof (*index));
> > +  if (index == NULL)
> > +    {
> > +      __libdw_seterrno (DWARF_E_NOMEM);
> > +      return NULL;
> > +    }
> > +
> > +  index->dbg = dbg;
> > +  /* Set absent sections to UINT32_MAX.  */
> > +  memset (index->sections, 0xff, sizeof (index->sections));
> 
> OK, although I would have preferred a simple for loop and let the
> compiler optimize it.

Ok, I can fix that.

[snip]

> > +static int
> > +__libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row,
> > +			  unsigned int section, Dwarf_Off *offsetp,
> > +			  Dwarf_Off *sizep)
> > +{
> > +  if (index == NULL)
> > +    return -1;
> > +  if (unit_row == 0)
> > +    {
> > +      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> > +      return -1;
> > +    }
> 
> OK, but why isn't the caller checking this?

Partially becase it simplified callers like __libdw_dwp_findcu_id to not
require a separate check, and partially just to be defensive.

[snip]

Thanks for taking a look!
  

Patch

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 52327688..1d229094 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -20,7 +20,14 @@ 
 	instead of dbg parameter, which is now unused.
 	* libdwP.h (Dwarf_Macro_Op_Table): Replace is_64bit with address_size
 	and offset_size.  Add dbg.
-	Add IDX_debug_cu_index and IDX_debug_tu_index.
+	(Dwarf): Add cu_index and tu_index.
+	(Dwarf_CU): Add dwp_row.
+	(Dwarf_Package_Index): New type.
+	(DW_SECT_TYPES): New macro.
+	(__libdw_dwp_find_unit): New declaration.
+	(dwarf_cu_dwp_section_info): New INTDECL.
+	Add IDX_debug_cu_index and IDX_debug_tu_index.  Add
+	DWARF_E_UNKNOWN_SECTION.
 	* dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_cu_index and
 	IDX_debug_tu_index.
 	(scn_to_string_section_idx): Ditto.
@@ -28,6 +35,15 @@ 
 	.zdebug_cu_index, and .zdebug_tu_index.
 	(check_section): Change .dwo suffix matching to account for
 	.debug_cu_index and .debug_tu_index.
+	* Makefile.am (libdw_a_SOURCES): Add dwarf_cu_dwp_section_info.c.
+	* dwarf_end.c (dwarf_end): Free dwarf->cu_index and dwarf->tu_index.
+	* dwarf_error.c (errmsgs): Add DWARF_E_UNKNOWN_SECTION.
+	* libdw.h (dwarf_cu_dwp_section_info): New declaration.
+	* libdw.map (ELFUTILS_0.190): Add dwarf_cu_dwp_section_info.
+	* libdw_findcu.c (__libdw_intern_next_unit): Call
+	__libdw_dwp_find_unit, and use it to adjust abbrev_offset and assign
+	newp->dwp_row.
+	* dwarf_cu_dwp_section_info.c: New file.
 
 2023-02-22  Mark Wielaard  <mark@klomp.org>
 
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index e548f38c..5363c02a 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -93,7 +93,7 @@  libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
 		  dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \
 		  dwarf_die_addr_die.c dwarf_get_units.c \
 		  libdw_find_split_unit.c dwarf_cu_info.c \
-		  dwarf_next_lines.c
+		  dwarf_next_lines.c dwarf_cu_dwp_section_info.c
 
 if MAINTAINER_MODE
 BUILT_SOURCES = $(srcdir)/known-dwarf.h
diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c
new file mode 100644
index 00000000..6766fb9a
--- /dev/null
+++ b/libdw/dwarf_cu_dwp_section_info.c
@@ -0,0 +1,371 @@ 
+/* Read DWARF package file index sections.
+   Copyright (c) Meta Platforms, Inc. and affiliates.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwP.h"
+
+static Dwarf_Package_Index *
+__libdw_read_package_index (Dwarf *dbg, bool tu)
+{
+  Elf_Data *data;
+  if (tu)
+    data = dbg->sectiondata[IDX_debug_tu_index];
+  else
+    data = dbg->sectiondata[IDX_debug_cu_index];
+
+  /* We need at least 16 bytes for the header.  */
+  if (data == NULL || data->d_size < 16)
+    {
+    invalid:
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return NULL;
+    }
+
+  const unsigned char *datap = data->d_buf;
+  const unsigned char *endp = datap + data->d_size;
+  uint16_t version;
+  /* In GNU DebugFission for DWARF 4, the version is 2 as a uword.  In the
+     standardized DWARF 5 format, it is a uhalf followed by a padding uhalf.
+     Check for both.  */
+  if (read_4ubyte_unaligned (dbg, datap) == 2)
+    version = 2;
+  else
+    {
+      version = read_2ubyte_unaligned (dbg, datap);
+      if (version != 5)
+	{
+	  __libdw_seterrno (DWARF_E_VERSION);
+	  return NULL;
+	}
+    }
+  datap += 4;
+  uint32_t section_count = read_4ubyte_unaligned_inc (dbg, datap);
+  uint32_t unit_count = read_4ubyte_unaligned_inc (dbg, datap);
+  uint32_t slot_count = read_4ubyte_unaligned_inc (dbg, datap);
+
+  /* The specification has a stricter requirement that
+     slot_count > 3 * unit_count / 2, but this is enough for us.  */
+  if (slot_count < unit_count)
+    goto invalid;
+
+  /* After the header, the section must contain:
+
+       8 byte signature per hash table slot
+     + 4 byte index per hash table slot
+     + Section offset table with 1 header row, 1 row per unit, 1 column per
+       section, 4 bytes per field
+     + Section size table with 1 row per unit, 1 column per section, 4 bytes
+       per field
+
+     We have to be careful about overflow when checking this.  */
+  const unsigned char *hash_table = datap;
+  if ((size_t) (endp - hash_table) < (uint64_t) slot_count * 12)
+    goto invalid;
+  const unsigned char *indices = hash_table + (size_t) slot_count * 8;
+  const unsigned char *sections = indices + (size_t) slot_count * 4;
+  if ((size_t) (endp - sections) < (uint64_t) section_count * 4)
+    goto invalid;
+  const unsigned char *section_offsets = sections + (size_t) section_count * 4;
+  if ((uint64_t) unit_count * section_count > UINT64_MAX / 8
+      || ((size_t) (endp - section_offsets)
+	  < (uint64_t) unit_count * section_count * 8))
+    goto invalid;
+  const unsigned char *section_sizes
+    = section_offsets + (uint64_t) unit_count * section_count * 4;
+
+  Dwarf_Package_Index *index = malloc (sizeof (*index));
+  if (index == NULL)
+    {
+      __libdw_seterrno (DWARF_E_NOMEM);
+      return NULL;
+    }
+
+  index->dbg = dbg;
+  /* Set absent sections to UINT32_MAX.  */
+  memset (index->sections, 0xff, sizeof (index->sections));
+  for (size_t i = 0; i < section_count; i++)
+    {
+      uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4);
+      /* 2 is DW_SECT_TYPES in version 2 and reserved in version 5.  We ignore
+         it for version 5.
+	 5 is DW_SECT_LOC in version 2 and DW_SECT_LOCLISTS in version 5.  We
+	 use the same index for both.
+	 7 is DW_SECT_MACINFO in version 2 and DW_SECT_MACRO in version 5.  We
+	 use the same index for both.
+	 8 is DW_SECT_MACRO in version 2 and DW_SECT_RNGLISTS in version 5.  We
+	 use the same index for version 2's DW_SECT_MACRO as version 2's
+	 DW_SECT_MACINFO/version 5's DW_SECT_MACRO.
+	 We ignore unknown sections.  */
+      if (section == 0)
+	continue;
+      if (version == 2)
+	{
+	  if (section > 8)
+	    continue;
+	  else if (section == 8)
+	    section = DW_SECT_MACRO;
+	}
+      else if (section == 2
+	       || (section
+		   > sizeof (index->sections) / sizeof (index->sections[0])))
+	continue;
+      index->sections[section - 1] = i;
+    }
+
+  /* DW_SECT_INFO (or DW_SECT_TYPES for DWARF 4 type units) and DW_SECT_ABBREV
+     are required.  */
+  if (((!tu || dbg->sectiondata[IDX_debug_types] == NULL)
+       && index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
+      || (tu && dbg->sectiondata[IDX_debug_types] != NULL
+	  && index->sections[DW_SECT_TYPES - 1] == UINT32_MAX)
+      || index->sections[DW_SECT_ABBREV - 1] == UINT32_MAX)
+    {
+      free (index);
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return NULL;
+    }
+
+  index->section_count = section_count;
+  index->unit_count = unit_count;
+  index->slot_count = slot_count;
+  index->last_unit_found = 0;
+  index->hash_table = hash_table;
+  index->indices = indices;
+  index->section_offsets = section_offsets;
+  index->section_sizes = section_sizes;
+
+  return index;
+}
+
+static Dwarf_Package_Index *
+__libdw_package_index (Dwarf *dbg, bool tu)
+{
+  if (tu && dbg->tu_index != NULL)
+    return dbg->tu_index;
+  else if (!tu && dbg->cu_index != NULL)
+    return dbg->cu_index;
+
+  Dwarf_Package_Index *index = __libdw_read_package_index (dbg, tu);
+  if (index == NULL)
+    return NULL;
+
+  if (tu)
+    dbg->tu_index = index;
+  else
+    dbg->cu_index = index;
+  return index;
+}
+
+static int
+__libdw_dwp_unit_row (Dwarf_Package_Index *index, uint64_t unit_id,
+		      uint32_t *unit_rowp)
+{
+  if (index == NULL)
+    return -1;
+
+  uint32_t hash = unit_id;
+  uint32_t hash2 = (unit_id >> 32) | 1;
+  /* Only check each slot once.  */
+  for (uint32_t n = index->slot_count; n-- > 0; )
+    {
+      size_t slot = hash & (index->slot_count - 1);
+      uint64_t sig = read_8ubyte_unaligned (index->dbg,
+					    index->hash_table + slot * 8);
+      if (sig == unit_id)
+	{
+	  uint32_t row = read_4ubyte_unaligned (index->dbg,
+						index->indices + slot * 4);
+	  if (row > index->unit_count)
+	    {
+	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+	      return -1;
+	    }
+	  *unit_rowp = row;
+	  return 0;
+	}
+      else if (sig == 0
+	       && read_4ubyte_unaligned (index->dbg,
+					 index->indices + slot * 4) == 0)
+	break;
+      hash += hash2;
+    }
+  *unit_rowp = 0;
+  return 0;
+}
+
+static int
+__libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row,
+			  unsigned int section, Dwarf_Off *offsetp,
+			  Dwarf_Off *sizep)
+{
+  if (index == NULL)
+    return -1;
+  if (unit_row == 0)
+    {
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return -1;
+    }
+  if (index->sections[section - 1] == UINT32_MAX)
+    {
+      if (offsetp != NULL)
+	*offsetp = 0;
+      if (sizep != NULL)
+	*sizep = 0;
+      return 0;
+    }
+  size_t i = (size_t)(unit_row - 1) * index->section_count
+	     + index->sections[section - 1];
+  if (offsetp != NULL)
+    *offsetp = read_4ubyte_unaligned (index->dbg,
+				      index->section_offsets + i * 4);
+  if (sizep != NULL)
+    *sizep = read_4ubyte_unaligned (index->dbg,
+				    index->section_sizes + i * 4);
+  return 0;
+}
+
+int
+internal_function
+__libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
+		       uint16_t version, uint8_t unit_type, uint64_t unit_id8,
+		       uint32_t *unit_rowp, Dwarf_Off *abbrev_offsetp)
+{
+  if (version >= 5
+      && unit_type != DW_UT_split_compile && unit_type != DW_UT_split_type)
+    {
+    not_dwp:
+      *unit_rowp = 0;
+      *abbrev_offsetp = 0;
+      return 0;
+    }
+  bool tu = unit_type == DW_UT_split_type || debug_types;
+  if (dbg->sectiondata[tu ? IDX_debug_tu_index : IDX_debug_cu_index] == NULL)
+    goto not_dwp;
+  Dwarf_Package_Index *index = __libdw_package_index (dbg, tu);
+  if (index == NULL)
+    return -1;
+
+  /* This is always called for ascending offsets.  The most obvious way for a
+     producer to generate the section offset table is sorted by offset; both
+     GNU dwp and llvm-dwp do this.  In this common case, we can avoid the full
+     lookup.  */
+  if (index->last_unit_found < index->unit_count)
+    {
+      Dwarf_Off offset, size;
+      if (__libdw_dwp_section_info (index, index->last_unit_found + 1,
+				    debug_types ? DW_SECT_TYPES : DW_SECT_INFO,
+				    &offset, &size) != 0)
+	return -1;
+      if (offset <= off && off - offset < size)
+	{
+	  *unit_rowp = ++index->last_unit_found;
+	  goto done;
+	}
+      else
+	/* The units are not sorted. Don't try again.  */
+	index->last_unit_found = index->unit_count;
+    }
+
+  if (version >= 5 || debug_types)
+    {
+      /* In DWARF 5 and in type units, the unit signature is available in the
+         unit header.  */
+      if (__libdw_dwp_unit_row (index, unit_id8, unit_rowp) != 0)
+	return -1;
+    }
+  else
+    {
+      /* In DWARF 4 compilation units, the unit signature is an attribute.  We
+	 can't parse attributes in the split unit until we get the abbreviation
+	 table offset from the package index, which is a chicken-and-egg
+	 problem.  We could get the signature from the skeleton unit, but that
+	 may not be available.
+
+	 Instead, we resort to a linear scan through the section offset table.
+	 Finding all units is therefore quadratic in the number of units.
+	 However, this will likely never be needed in practice because of the
+	 sorted fast path above.  If this ceases to be the case, we can try to
+	 plumb through the skeleton unit's signature when it is available, or
+	 build a sorted lookup table for binary search.  */
+      if (index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
+	{
+	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
+	  return -1;
+	}
+      for (uint32_t i = 0; i < index->unit_count; i++)
+	{
+	  Dwarf_Off offset, size;
+	  __libdw_dwp_section_info (index, i + 1, DW_SECT_INFO, &offset,
+				    &size);
+	  if (offset <= off && off - offset < size)
+	    {
+	      *unit_rowp = i + 1;
+	      goto done;
+	    }
+	}
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return -1;
+    }
+
+ done:
+  return __libdw_dwp_section_info (index, *unit_rowp, DW_SECT_ABBREV,
+				   abbrev_offsetp, NULL);
+}
+
+int
+dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
+			   Dwarf_Off *offsetp, Dwarf_Off *sizep)
+{
+  if (cu == NULL)
+    return -1;
+  if (section < DW_SECT_INFO || section > DW_SECT_RNGLISTS)
+    {
+      __libdw_seterrno (DWARF_E_UNKNOWN_SECTION);
+      return -1;
+    }
+  if (cu->dwp_row == 0)
+    {
+      if (offsetp != NULL)
+	*offsetp = 0;
+      if (sizep != NULL)
+	*sizep = 0;
+      return 0;
+    }
+  else
+    {
+      Dwarf_Package_Index *index
+	= cu->unit_type == DW_UT_split_compile
+	? cu->dbg->cu_index : cu->dbg->tu_index;
+      return __libdw_dwp_section_info (index, cu->dwp_row, section, offsetp,
+				       sizep);
+    }
+}
+INTDEF(dwarf_cu_dwp_section_info)
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 8dd075cf..18a419ce 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -77,6 +77,9 @@  dwarf_end (Dwarf *dwarf)
 {
   if (dwarf != NULL)
     {
+      free (dwarf->tu_index);
+      free (dwarf->cu_index);
+
       if (dwarf->cfi != NULL)
 	/* Clean up the CFI cache.  */
 	__libdw_destroy_frame_cache (dwarf->cfi);
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 46ea16b3..0123cfa2 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -102,6 +102,7 @@  static const char *errmsgs[] =
     [DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"),
     [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"),
     [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"),
+    [DWARF_E_UNKNOWN_SECTION] = N_("unknown section"),
   };
 #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
 
diff --git a/libdw/libdw.h b/libdw/libdw.h
index 64d1689a..545ad043 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -1081,6 +1081,29 @@  extern int dwarf_frame_register (Dwarf_Frame *frame, int regno,
   __nonnull_attribute__ (3, 4, 5);
 
 
+/* Return offset and/or size of CU's contribution to SECTION in a DWARF package
+   file.
+
+   If CU is not from a DWARF package file, the file does not have SECTION, or CU
+   does not contribute to SECTION, then *SIZEP is set to 0.
+
+   SECTION is a DW_SECT section identifier.  Note that the original GNU DWARF
+   package file extension for DWARF 4 used slightly different section
+   identifiers.  This function uses the standardized section identifiers and
+   maps the GNU DWARF 4 identifiers to their standard DWARF 5 analogues:
+   DW_SECT_LOCLISTS (5) refers to .debug_locs.dwo for DWARF 4.
+   DW_SECT_MACRO (7) refers to .debug_macinfo.dwo for DWARF 4 or
+   .debug_macro.dwo for the GNU .debug_macro extension for DWARF 4 (section
+   identifier 8 is DW_SECT_RNGLISTS in DWARF 5, NOT DW_SECT_MACRO like in the
+   GNU extension.)
+   .debug_types.dwo does not have a DWARF 5 equivalent, so this function accepts
+   the original DW_SECT_TYPES (2).
+
+   Returns 0 for success or -1 for errors.  OFFSETP and SIZEP may be NULL.  */
+extern int dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
+				      Dwarf_Off *offsetp, Dwarf_Off *sizep);
+
+
 /* Return error code of last failing function call.  This value is kept
    separately for each thread.  */
 extern int dwarf_errno (void);
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 5331ad45..2104b007 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -373,3 +373,8 @@  ELFUTILS_0.188 {
     dwfl_frame_reg;
     dwfl_report_offline_memory;
 } ELFUTILS_0.186;
+
+ELFUTILS_0.190 {
+  global:
+    dwarf_cu_dwp_section_info;
+} ELFUTILS_0.188;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 0c44d40c..13ca58ce 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -147,6 +147,7 @@  enum
   DWARF_E_NOT_CUDIE,
   DWARF_E_UNKNOWN_LANGUAGE,
   DWARF_E_NO_DEBUG_ADDR,
+  DWARF_E_UNKNOWN_SECTION,
 };
 
 
@@ -227,6 +228,11 @@  struct Dwarf
   /* Cached info from the CFI section.  */
   struct Dwarf_CFI_s *cfi;
 
+  /* DWARF package file CU index section.  */
+  struct Dwarf_Package_Index_s *cu_index;
+  /* DWARF package file TU index section.  */
+  struct Dwarf_Package_Index_s *tu_index;
+
   /* Fake loc CU.  Used when synthesizing attributes for Dwarf_Ops that
      came from a location list entry in dwarf_getlocation_attr.
      Depending on version this is the .debug_loc or .debug_loclists
@@ -339,6 +345,26 @@  struct Dwarf_Aranges_s
   } info[0];
 };
 
+/* DWARF package file unit index.  */
+typedef struct Dwarf_Package_Index_s
+{
+  Dwarf *dbg;
+  uint32_t section_count;
+  uint32_t unit_count;
+  uint32_t slot_count;
+  /* Mapping from DW_SECT_* - 1 to column number in the section tables, or
+     UINT32_MAX if not present.  */
+  uint32_t sections[DW_SECT_RNGLISTS];
+  /* Row number of last unit found in the index.  */
+  uint32_t last_unit_found;
+  const unsigned char *hash_table;
+  const unsigned char *indices;
+  const unsigned char *section_offsets;
+  const unsigned char *section_sizes;
+} Dwarf_Package_Index;
+
+/* Used in GNU DebugFission for DWARF 4.  Reserved in DWARF 5.  */
+#define DW_SECT_TYPES 2
 
 /* CU representation.  */
 struct Dwarf_CU
@@ -346,6 +372,8 @@  struct Dwarf_CU
   Dwarf *dbg;
   Dwarf_Off start;
   Dwarf_Off end;
+  /* Row number of this unit in DWARF package file index.  */
+  uint32_t dwp_row;
   uint8_t address_size;
   uint8_t offset_size;
   uint16_t version;
@@ -680,6 +708,13 @@  extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
 extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu)
      internal_function;
 
+/* Find a unit in a DWARF package file for __libdw_intern_next_unit.  */
+extern int __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
+				  uint16_t version, uint8_t unit_type,
+				  uint64_t unit_id8, uint32_t *unit_rowp,
+				  Dwarf_Off *abbrev_offsetp)
+     __nonnull_attribute__ (1, 7, 8) internal_function;
+
 /* Get abbreviation with given code.  */
 extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
 					 unsigned int code)
@@ -1380,6 +1415,7 @@  INTDECL (dwarf_attr_integrate)
 INTDECL (dwarf_begin)
 INTDECL (dwarf_begin_elf)
 INTDECL (dwarf_child)
+INTDECL (dwarf_cu_dwp_section_info)
 INTDECL (dwarf_default_lower_bound)
 INTDECL (dwarf_dieoffset)
 INTDECL (dwarf_diename)
diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
index ed744231..6c7dcfb5 100644
--- a/libdw/libdw_findcu.c
+++ b/libdw/libdw_findcu.c
@@ -143,6 +143,13 @@  __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
   if (unlikely (*offsetp > data->d_size))
     *offsetp = data->d_size;
 
+  uint32_t dwp_row;
+  Dwarf_Off dwp_abbrev_offset;
+  if (__libdw_dwp_find_unit (dbg, debug_types, oldoff, version, unit_type,
+			     unit_id8, &dwp_row, &dwp_abbrev_offset) != 0)
+    return NULL;
+  abbrev_offset += dwp_abbrev_offset;
+
   /* Create an entry for this CU.  */
   struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
 
@@ -150,6 +157,7 @@  __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
   newp->sec_idx = sec_idx;
   newp->start = oldoff;
   newp->end = *offsetp;
+  newp->dwp_row = dwp_row;
   newp->address_size = address_size;
   newp->offset_size = offset_size;
   newp->version = version;
diff --git a/tests/.gitignore b/tests/.gitignore
index b9aa22ba..e9a61a2d 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -28,6 +28,7 @@ 
 /backtrace-dwarf
 /buildid
 /core-dump-backtrace.lock
+/cu-dwp-section-info
 /debugaltlink
 /debuginfod_build_id_find
 /debuglink
diff --git a/tests/ChangeLog b/tests/ChangeLog
index f934c114..687a9f32 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,6 +1,19 @@ 
 2023-09-27  Omar Sandoval  <osandov@fb.com>
 
 	* run-varlocs.sh: Add entry PC to split units.
+	* Makefile.am (check_PROGRAMS): Add cu-dwp-section-info.
+	(TESTS): Add run-cu-dwp-section-info.sh
+	(EXTRA_DIST): Add run-cu-dwp-section-info.sh and new test files.
+	(cu_dwp_section_info_LDADD): New variable.
+	* cu-dwp-section-info.c: New test.
+	* run-cu-dwp-section-info.sh: New test.
+	* testfile-dwp-4-strict.bz2: New test file.
+	* testfile-dwp-4-strict.dwp.bz2: New test file.
+	* testfile-dwp-4.bz2: New test file.
+	* testfile-dwp-4.dwp.bz2: New test file.
+	* testfile-dwp-5.bz2: New test file.
+	* testfile-dwp-5.dwp.bz2: New test file.
+	* testfile-dwp.source: New file.
 
 2023-04-21  Frank Ch. Eigler <fche@redhat.com>
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 32b18e6e..0938882b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -62,7 +62,7 @@  check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  dwelf_elf_e_machine_string \
 		  getphdrnum leb128 read_unaligned \
 		  msg_tst system-elf-libelf-test system-elf-gelf-test \
-		  nvidia_extended_linemap_libdw \
+		  nvidia_extended_linemap_libdw cu-dwp-section-info \
 		  $(asm_TESTS)
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
@@ -211,7 +211,7 @@  TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
 	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
 	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
-	run-readelf-Dd.sh
+	run-readelf-Dd.sh run-cu-dwp-section-info.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -629,7 +629,11 @@  EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
 	     testfile_nvidia_linemap.bz2 \
 	     testfile-largealign.o.bz2 run-strip-largealign.sh \
-	     run-funcretval++11.sh
+	     run-funcretval++11.sh \
+	     testfile-dwp-4.bz2 testfile-dwp-4.dwp.bz2 \
+	     testfile-dwp-4-strict.bz2 testfile-dwp-4-strict.dwp.bz2 \
+	     testfile-dwp-5.bz2 testfile-dwp-5.dwp.bz2 testfile-dwp.source \
+	     run-cu-dwp-section-info.sh
 
 
 if USE_VALGRIND
@@ -804,6 +808,7 @@  getphdrnum_LDADD = $(libelf) $(libdw)
 leb128_LDADD = $(libelf) $(libdw)
 read_unaligned_LDADD = $(libelf) $(libdw)
 nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
+cu_dwp_section_info_LDADD = $(libdw)
 
 # We want to test the libelf headers against the system elf.h header.
 # Don't include any -I CPPFLAGS. Except when we install our own elf.h.
diff --git a/tests/cu-dwp-section-info.c b/tests/cu-dwp-section-info.c
new file mode 100644
index 00000000..e92f35b6
--- /dev/null
+++ b/tests/cu-dwp-section-info.c
@@ -0,0 +1,74 @@ 
+/* Test program for dwarf_cu_dwp_section_info
+   Copyright (c) Meta Platforms, Inc. and affiliates.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <dwarf.h>
+#include ELFUTILS_HEADER(dw)
+
+int
+main (int argc, char *argv[])
+{
+  for (int i = 1; i < argc; i++)
+    {
+      printf ("file: %s\n", argv[i]);
+      int fd = open (argv[i], O_RDONLY);
+      Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ);
+      if (dbg == NULL)
+	{
+	  printf ("%s not usable: %s\n", argv[i], dwarf_errmsg (-1));
+	  return -1;
+	}
+
+      Dwarf_CU *cu = NULL;
+      while (dwarf_get_units (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0)
+	{
+#define SECTION_INFO(section) do {					\
+	    printf (#section ": ");					\
+	    Dwarf_Off offset, size;					\
+	    if (dwarf_cu_dwp_section_info (cu, DW_SECT_##section,	\
+					   &offset, &size) == 0)	\
+	      printf ("0x%" PRIx64 " 0x%" PRIx64 "\n", offset, size);	\
+	    else							\
+	      printf ("%s\n", dwarf_errmsg (-1));			\
+	  } while (0)
+	  SECTION_INFO (INFO);
+#define DW_SECT_TYPES 2
+	  SECTION_INFO (TYPES);
+	  SECTION_INFO (ABBREV);
+	  SECTION_INFO (LINE);
+	  SECTION_INFO (LOCLISTS);
+	  SECTION_INFO (STR_OFFSETS);
+	  SECTION_INFO (MACRO);
+	  SECTION_INFO (RNGLISTS);
+	  printf ("\n");
+	}
+
+      dwarf_end (dbg);
+      close (fd);
+    }
+
+  return 0;
+}
diff --git a/tests/run-cu-dwp-section-info.sh b/tests/run-cu-dwp-section-info.sh
new file mode 100755
index 00000000..f8596f2c
--- /dev/null
+++ b/tests/run-cu-dwp-section-info.sh
@@ -0,0 +1,168 @@ 
+#! /bin/sh
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# See testfile-dwp.source.
+testfiles testfile-dwp-5.dwp testfile-dwp-4.dwp testfile-dwp-4-strict.dwp
+
+testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-5.dwp << EOF
+file: testfile-dwp-5.dwp
+INFO: 0x0 0x70
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x160
+LINE: 0x0 0x7f
+LOCLISTS: 0x0 0xdb
+STR_OFFSETS: 0x0 0x75c
+MACRO: 0x0 0x6c6
+RNGLISTS: 0x0 0x22
+
+INFO: 0x70 0x108
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x160
+LINE: 0x0 0x7f
+LOCLISTS: 0x0 0xdb
+STR_OFFSETS: 0x0 0x75c
+MACRO: 0x0 0x6c6
+RNGLISTS: 0x0 0x22
+
+INFO: 0x178 0x6e
+TYPES: 0x0 0x0
+ABBREV: 0x160 0xca
+LINE: 0x7f 0x7f
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x75c 0x758
+MACRO: 0x6c6 0x6c5
+RNGLISTS: 0x0 0x0
+
+INFO: 0x1e6 0x78
+TYPES: 0x0 0x0
+ABBREV: 0x160 0xca
+LINE: 0x7f 0x7f
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x75c 0x758
+MACRO: 0x6c6 0x6c5
+RNGLISTS: 0x0 0x0
+
+INFO: 0x25e 0x193
+TYPES: 0x0 0x0
+ABBREV: 0x22a 0x18a
+LINE: 0xfe 0x81
+LOCLISTS: 0xdb 0xc9
+STR_OFFSETS: 0xeb4 0x77c
+MACRO: 0xd8b 0x6c6
+RNGLISTS: 0x22 0x43
+
+EOF
+
+testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4.dwp << EOF
+file: testfile-dwp-4.dwp
+INFO: 0x0 0x11e
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x172
+LINE: 0x0 0x52
+LOCLISTS: 0x0 0x11b
+STR_OFFSETS: 0x0 0x754
+MACRO: 0x0 0x6c7
+RNGLISTS: 0x0 0x0
+
+INFO: 0x11e 0x76
+TYPES: 0x0 0x0
+ABBREV: 0x172 0xd7
+LINE: 0x52 0x52
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x754 0x750
+MACRO: 0x6c7 0x6c6
+RNGLISTS: 0x0 0x0
+
+INFO: 0x194 0x1c5
+TYPES: 0x0 0x0
+ABBREV: 0x249 0x19e
+LINE: 0xa4 0x53
+LOCLISTS: 0x11b 0xf1
+STR_OFFSETS: 0xea4 0x774
+MACRO: 0xd8d 0x6c7
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x0 0x6f
+ABBREV: 0x0 0x172
+LINE: 0x0 0x52
+LOCLISTS: 0x0 0x11b
+STR_OFFSETS: 0x0 0x754
+MACRO: 0x0 0x6c7
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x6f 0x6d
+ABBREV: 0x172 0xd7
+LINE: 0x52 0x52
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x754 0x750
+MACRO: 0x6c7 0x6c6
+RNGLISTS: 0x0 0x0
+
+EOF
+
+testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4-strict.dwp << EOF
+file: testfile-dwp-4-strict.dwp
+INFO: 0x0 0x105
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x15f
+LINE: 0x0 0x52
+LOCLISTS: 0x0 0xe2
+STR_OFFSETS: 0x0 0x24
+MACRO: 0x0 0x38e4
+RNGLISTS: 0x0 0x0
+
+INFO: 0x105 0x72
+TYPES: 0x0 0x0
+ABBREV: 0x15f 0xd3
+LINE: 0x52 0x52
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x24 0x20
+MACRO: 0x38e4 0x38db
+RNGLISTS: 0x0 0x0
+
+INFO: 0x177 0x17b
+TYPES: 0x0 0x0
+ABBREV: 0x232 0x157
+LINE: 0xa4 0x53
+LOCLISTS: 0xe2 0xb1
+STR_OFFSETS: 0x44 0x44
+MACRO: 0x71bf 0x38f5
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x0 0x6e
+ABBREV: 0x0 0x15f
+LINE: 0x0 0x52
+LOCLISTS: 0x0 0xe2
+STR_OFFSETS: 0x0 0x24
+MACRO: 0x0 0x38e4
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x6e 0x6b
+ABBREV: 0x15f 0xd3
+LINE: 0x52 0x52
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x24 0x20
+MACRO: 0x38e4 0x38db
+RNGLISTS: 0x0 0x0
+
+EOF
diff --git a/tests/testfile-dwp-4-strict.bz2 b/tests/testfile-dwp-4-strict.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..4f419c07d45aea608e02da002e8c09bc35b3553b
GIT binary patch
literal 4169
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
TiAwT$ul!xf6yZWcuZr*_TcUC^

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4-strict.dwp.bz2 b/tests/testfile-dwp-4-strict.dwp.bz2
diff --git a/tests/testfile-dwp-4.bz2 b/tests/testfile-dwp-4.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..ece5817bd88a5cf195bdaa109f8f41a8d4595093
GIT binary patch
literal 4194
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

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4.dwp.bz2 b/tests/testfile-dwp-4.dwp.bz2
diff --git a/tests/testfile-dwp-5.bz2 b/tests/testfile-dwp-5.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..2c768eacb441e0d516f8b0d5ed327893c129246b
GIT binary patch
literal 4223
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
V;WDbQ*Tw(h?ntK!5)wFoa$u}k#e@I=

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-5.dwp.bz2 b/tests/testfile-dwp-5.dwp.bz2
diff --git a/tests/testfile-dwp.source b/tests/testfile-dwp.source
new file mode 100644
index 00000000..b0b0a97c
--- /dev/null
+++ b/tests/testfile-dwp.source
@@ -0,0 +1,102 @@ 
+# Nonsensical program used to generate example DWARF package files with type
+# units, location lists, range lists, and macros.
+
+# = foobar.h =
+
+struct Foo
+{
+  int a, b;
+  int foo ();
+};
+
+struct Bar
+{
+  long a, b;
+  long bar ();
+};
+
+#define FROB(x) ((x) ^ 0x2a2a2a2a)
+#define FRY(x) ((x) * 0x100000001b3)
+
+inline long
+fibonacci (unsigned int n)
+{
+  if (n == 0)
+    return 0;
+  else
+    {
+      long a = 0;
+      long b = 1;
+      for (unsigned int i = 2; i < n; i++)
+	{
+	  long tmp = a + b;
+	  a = b;
+	  b = tmp;
+	}
+      return b;
+    }
+}
+
+# = foo.cc =
+
+#include "foobar.h"
+
+#define ZERO() (1 - 1)
+
+int
+x_x (int x)
+{
+  for (int i = x; i > ZERO(); i--)
+    x *= x;
+  return x;
+}
+
+int
+Foo::foo ()
+{
+  int x = a;
+  if (a > b)
+    x -= b;
+  return FROB (x_x (x));
+}
+
+# = bar.cc =
+
+#include "foobar.h"
+
+#define ONE 1
+
+long
+Bar::bar ()
+{
+  if (a == b)
+    return ONE;
+  else
+    return a > b ? b : a;
+}
+
+# = main.cc =
+
+#include "foobar.h"
+
+#define MAIN_ARGS int argc, char **argv
+
+int
+main(MAIN_ARGS)
+{
+  struct Foo myfoo { argc, FROB (argc) };
+  struct Bar mybar { fibonacci (argc), FRY (argc) };
+  return myfoo.foo() + mybar.bar();
+}
+
+# Built with GCC at commit 80048aa13a6b ("debug/111409 - don't generate COMDAT
+# macro sections for split DWARF").
+$ g++ -gdwarf-5 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-5
+# GNU dwp as of binutils 2.41 only supports DWARF 4.
+$ llvm-dwp -e testfile-dwp-5 -o testfile-dwp-5.dwp
+
+$ g++ -gdwarf-4 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4
+$ dwp -e testfile-dwp-4
+
+$ g++ -gdwarf-4 -gstrict-dwarf -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4-strict
+$ dwp -e testfile-dwp-4-strict