pax_global_header00006660000000000000000000000064145650451140014517gustar00rootroot0000000000000052 comment=775c4998dbc191d531ec9c5ad60c323bb84a1e33 golang-github-cilium-ebpf-0.11.0/000077500000000000000000000000001456504511400165175ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/.clang-format000066400000000000000000000010641456504511400210730ustar00rootroot00000000000000--- Language: Cpp BasedOnStyle: LLVM AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: true AlignEscapedNewlines: DontAlign AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: false BreakBeforeBraces: Attach IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false TabWidth: 4 UseTab: ForContinuationAndIndentation ColumnLimit: 1000 # Go compiler comments need to stay unindented. CommentPragmas: '^go:.*' ... golang-github-cilium-ebpf-0.11.0/.github/000077500000000000000000000000001456504511400200575ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001456504511400222425ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000005721456504511400247400ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior. Please include: ```sh go version uname -a cat /etc/issue ``` **Expected behavior** A clear and concise description of what you expected to happen. golang-github-cilium-ebpf-0.11.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003651456504511400242360ustar00rootroot00000000000000contact_links: - name: Questions url: https://github.com/cilium/ebpf/discussions/categories/q-a about: Please ask and answer questions here. - name: Slack url: https://cilium.slack.com/messages/ebpf-go about: Join our slack. golang-github-cilium-ebpf-0.11.0/.github/images/000077500000000000000000000000001456504511400213245ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/.github/images/cilium-ebpf.png000066400000000000000000001151661456504511400242400ustar00rootroot00000000000000PNG  IHDR,#+< pHYsP7dtEXtSoftwarewww.inkscape.org< IDATxw\UǟN.pP+͑ܙff,-5eff{oMsV\doy~88ǃrg3ޟ$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$wU!!(!ja~R G I*@6 xb]FHHզdʆNl՚3d&I/W.ed|8Uē+d;k x2CBB޾CmKϥw}m:|Ffdש-ɲf@8ЮJB@a_oP#63.qT$_h% l*Y)KHH<Ռ u6V7/vyWOl.Q݈n>mD=L~'xlIxrDw'o#&Cqd7bĖO/Ŋ-VliK~빪_ F*7$ObP)u,3O!O3|S!Q{ڈGiU4"ΰt'D%J f@LOנThشb3Jo}9T-UI֭/Ǫ6Q@[NOrJ^ú+$X;<]w7ջw'-D"e2|+nFGyqU=P_nPgr(*Z-L&Ve2Y^c0>G^oFbdTiiiRRR*~$p!ԃD2E[%uGاc;A#d!GT dEWٿ7W]\\:ժUkdZZZVkۨQ#e@@ߟ_j: T JsrrtRٳz?v"m7cr ŔfV   oW= +7VOŹKSd:= .(O6fe=T3{gY`]U?6lp|vvv@VZ5kF P*VѨKpy^~UuFZZZ5ٛ17MFxmJ.0nPcO5'*~h(/jر/z)lllJ\9''˗/s5%3# L&3vv8;9qqBC1p@Z-W\aʕH`״iӹ(T͚y-[Ʈ]hҤ O=ZBu6 dٛŋPߘaĈLݺ=U\~#F}uV`t ^<2LH2 \ՉTVWF.0V'N+Iju7__߿Ǐ4```ʕ_x{{3p@:vXdD.b|Dի|嗄5n]4xG*rロq111[RؒL&RU{#+o7H%2-K6'qٰj<;$X6ꐐ jc޼yܹ^z1|p +{7ֺ/|dmСhժj3g0{,-cѢE1e܋2,>!!qdzzTq|E[CZSilU%`=d@jodeY?HRVZ9}ܸqe֬Y:uqѽ{wHS(U%;6tԅ?a8x̌ k3 hd¿~nG*ҶTztʥSS/aJx+K\&l=…0ǠopSټ-#!%1a|JJ*~ IA\\\.3GZjϩSxҥUd6US˂?bxt~[l>Mօ\C!7&Y5{;u):_ښM_x06xYYĢ7#Fhh蔆 j|be09s&#G}YjPVN_~W %, F'?q\z2 NV{_N<󜧗T.^C}YdPA Ru̹Fy8X}AͤooGAhDի… &޼y3O?4 fvZNdrY. O0jH쵥O_}vً={ "Sgo/&| 5g /| Lṑ?~v8}4~h]v?,H 䁁[۶na„ ݣ7n0aprr*)΅~z=;ח6mZҋkn&NP_~nw ~t^<*BѩeN#sqvޮZxipu 6;X0WGe̘1<E 0n8&L_|QfedͲs! Oql?j4<3p015{m[^YբMjA1ECf4 O4ahXf!uLt%! ÍPnC>^Sׯ3h Yz5aaaҙ53炅ynt7 ;oꫯлW_.P`?Z=WWn}8ulٱM̼@t.^cqN\9+XO_\m% ^Ѷ>HS‡=zoƍiذa%ˬXb%]v)Uqt~3|,YxkmLJJ:NSg o¶fWNy 56 N:8g-FUdXF1g$zHQƤ6m 0a[+.]?cFU(OaۯXr Xl Ç/>e'uвx">oi*yN1Ɇ*4l'FFMk~2O}=G֯=qR^ns3!*5wF>xzz>W_}%\gy___ϟ_b%;NBԕ+^^^/&Ov_n]//eeCv\rdD/1>|7g8wG/-7k;Ve%nIIL|;<_{[;\Q's&=@kWOV] `=\/>|gOԬYڥV``ZZ`Ϊ N~>4hp=NG]@'@  7Eqȍ@ Z6=2HQv/RaÆ K.eɒ%/%:\Zr6Ub޼1Rg+h>;Ij~=Q#צ+ %Ni !յ:uz 6LXhk׮eѢE*Vr+׮gkJ/Gj۶CëȼĪ$z8prqq?m4EذakRBaG8:8Tb{Q4jԐ> c_D=ӝq_|JT:uY9q۶m4e(-8g.;#$$@Μ9˩SiѢyuZ͡CD +FX8r6n?\)bn޼ݻU5gٰa#zNN*1Lj$z,:tbƌ%JdZ^Xy4MYT: @zzzuA1wߪ< +v 5HnCwP;] \.QUZUM\4e2uIT} 5k2`@++Wb̚}o@ҧꑼ=]-}HU@mV5k|l6Z,G;;;e@@fJOO/WWW\]]j ׯ*iӦZ J9*+C<ٹ+Vгeeࡼ9{,} ա,( YnGifgڵUM6U !!!h%۔ꫯprrd.V`ULL NNNX|׌>[6PVpHTTTd|pxWTj߭[7Uquu-f$$$зo_BCCu1'/Xu΢%e9t9 }vh$Xeýn:?3fM^ey„ؽ?oߎ])*\J#F'S #BlS'Yl=bZMfoxJBBB&5o|3om6X<ޞ9~ cw'11mzFA&M5j={a0ڷo?[΃(KFjUYE u8_~)fA̞>߿`V@zq-.0K֮ZZ6?Nƍi۶-L0K5 ?~7oX@}-WմkߎK.w1mިB$@,+puu5fۊ8cXLo{nWW VgUd;nnnDG tƍXzK/ٳշv:I/[]Aqc;N7ñ !aYӭ[*,dddX^#(%/FN"c_|"/00 |M|}}9u .`[hQy2dnnnȑ#G0 nӪi]Fcr2/]bEz])j߾}1;Ə{Mƍu?¼/@$XV`2\*m{{{bSdfeMl~<]_ҽG|L˗III>sLHHF@z?/bcc3%׬(QlTt03#CTT4 ؟{1|HJ%4+*Cqryf ˖-cQCMk?dʉkEͬ,ݖM!UeիW߷}֖4ov s`.G)ZqL-͚KvZV\ɻKdd$+W&<<%KGn:3yw;Ŷ:Y6-.aVV66lΎ=u6Aff4ӨJͰ7h &~>'N(>;6]>l$SLVZ@tz}-[gI^8Qi ñ ϣ }5߆CI6G/,j/ճð86vaիd̙?~  syIMK+'NA%*{?t$/]fK8~2AFff+V.AM^~rlݲϏuZ*;quuԩSyzzubܟ~t=ugს}7.];hcB bccW/B!GVZut|Tq5j9-EpE9cyKsY`ѣ?| ...nݚ]vuV9̙36bm5dR}a clj֬&w˄&99c^k={,_OOONDiݺժUږ#诿KgoO坜4٪+Ν;wΨÇpvss < 1)(罽=g?O"1`W^kcuYc5*ߖt_|RMC0d ~~~KIIaɈé[nuΜ9s3'ncIh߮#X,>39x:>u\]]xZiӦ#/E˖O0i C~8 gJe֭q(|?2YiV^a z֭[/~V7n$%%M6e1gΏ4iҘgϡR8s GcNywNKB`xyye9{,Z6_֝ک[7DxѶ}JLH`"R'4%,9vJRhѢ;jϞ56J4j=KBغ2tɎ%Kb;&&3{#7בW4Μ9KGb1u/4#ޟ0.]1`f͜M.OаaC<=}zW||<9nFӞ𻇇qc=BHS’%,,ltxx[lqyWg߰Υz{KL&3 L~s 1/mȜ; IDATjfq ;"dht,z&$m ,3g}uoڵ밵K.&Ow7745k ""l7oVܹޓ Tb*ш+ѣx~?c$X%DR綾^M>Ӄl斯dh0U֖;?ҍE77RliѲ5wExyo־]GvY, ǎgܶsbY8ك&sp:2Jd?\_c$X%fu~СC^VΏ>ʕ+_~w& F sq}`Ÿ7+ZYx P8sl{9p^dhު-hIYɡolڼRglݰѸLFLY+徹N_e٦xxl]2߿ H)a$*!DFFƤ!->EF&9_Rϲp =!>f試SEVި*8cf5#m Kͱw`Mbܿ{rL4TS9_֥GK3ӷO۷v:u4>>5P VIqpvvޜeA77l|:y$ρ (6Ռdl`6[U֯y?|VմĮbhܝ  g9wCp<2'G{kQ+4uWOHKK ;38ujWӪ'**v5#B7wM^3l og0{O+4 ܹsV͛^zRfh슏wd4c2kcU._ SPZRqu(QMG!z87n5gTDD%0r,(psu/脽Z4lmptt@`61s2#!>܌Fb|,112pXS] @j q֖nhuNo3Mԯ_Tmly'n;w> N!$*ڷoeŞ3fsܹUd4c6[Mt:sĖ+fbX;%+Q1cT;ڣ(A>ɿ&ѹ-0!LL7D3dd!#[ C Y9MA'b#bg62x:v$IM1.eܴx4Q9{1Je` &fv V P( :e޼yE\~}ҍ޷GRݴ _fsl`}6sNi-Uи9"3w8>Boe,fHe=ۏjoݫBɡ~Fbbb*G)L P*:z٤M}-)+cАa [wjwT1W^g՚\.uDUNob)WF9o#o?^y@ Sh+ Z,sPi&qwzJdd[~4e\1tܩAR,c$X%/((XA7o|8[mz5 ܽҡZ%|bZvxƍeV~=WD,f3K:5;t}NX,>̲~r@Af2Z7Ѡ>Oϲ`pO1Tw:IAPd<]VnzB^A)a h4N>mo|8ivH 'Z.YYog[AX,"fK5belZ.]Q]#7PҤ wFTm^b%?cWդgY8g'܈7hU)=zWKe9u ƍUbC$X%@vc=? 94Kv2d`ƍyC[b3yߗvɮ9TӪ h^ TciRW  r,2Lû:cA4lؠMcAI۶nm2U-:'hzz_wMy S\ N_KiӢ9"z[DnQDShlBD];Obb<@z5*^ [ԒħO1s3$1"Qo?BDȷ|[|HN{fW!`q_RRG|1pG"&mIV!]࠵G%!E͛9| N6:vЍ $sOwtt'n׮݋:cUpKWjBa(fm]^To B'-81A H߹ rAQD*-{ i]3Y$'`'_;gϧ?Yz_ H?P+G# ]_0`@d3[m=k"@lwPyA +:pOP0L.ukj`ܨm6/ădbɒo6~ˏ;FVN \j&O{("DO\ݲQ]6c"WWŒJ5yf0^J3hҲ-'Os%JAo5}"u嫗,ŋ-V.\t E QƴȹE2L}^m2O?¶-jU_Qą=9~-9 yfGiGtt4?){͚Zjm1c򠠠6kcǎd3Z$*Zj}t̙y%)<9x 6͟T2AMUCÉ#Fst"6o-D {os^1 lݺM_ӣ[j7|_vʱGy"$$SN-(iH̟}3%=GJ&͸{%\e nn4ST0gӻ{7Cp2)q={Y`a֡C9nȑ%Eiff``O:uwV}ЧOnF>Lp,X͘x{{Ѹqc7 #,`|0 DG͍7 \rQVuʠACQǏϛ7i~)o{"""|Y$FX,r߿?{.3DJA.WDKxYSzY*޽;ݻwϻ+b_HHH@VHPpիWϏjiejj*YYYdgeDrr2I$%'HҭkIIɀBZGX 4BmnԨPnݚf믗[xevv /6$*sy ѵ55AjEH*>i-mBZ __߼Q:E^xsu=AT bc0Z-NN89&&UTn8defb4dee67ƒ}ш Ȑ刀-2o-uÃ:uqm튫k[g0KrJ`ذaǏd2P(%'ԩS뉢_y! VEQ, Y =<1U+F~B;qWg? ,j5Z`0EHNæykYTֳ(JpQklQUdr4ڢX=0gYVKF~/hsǎu:ݏ$XSSŒtX$"^>daRݷfgcSEֱ htz9xxx`h駟,Xz^|r-`w-{b{2E;Zʘ"`, "D}>*G^ qm2Dse2+.s{Q СCܸq#ORSS˜lĉa($]FJ$*h,W^SJ }`ٱzyRU...tiۆۋ,gɘ1{kf?47oNjHKK (V^]bŊ6:DǼ$# Vd r$Gz(Z;ҮeȍMݻ9~8_|];ke4iSzzzM[Kۢ0M33WvvvxO"Wҿr )fI L6-ޭwd2 k8VZa2b2# V(9F}>7B "E([V\ѣG_>ZBY.l=s@0YDT%-Udg`9Rex?z~iiK"M KXCTRmd'Xݻyٴi۷筷ޢyԯ_GGG,d Ɉ<[܋ށ Xrh[1N8Ql6/m}\$*(Zz}0Gиoүdŕ{$g2c  0`{j8}tqvvƔQ}k\1ՐҾӐ&L@L45klmmK&55U@BC7n ].\vsg r+՘(r7o[ryailmm7oӧO/S_Z;-Ƭ,v|$=899%m&jmڴYf$&&Ju.** {{+Y̙3A/M]{FX%@WUbRjh\]M):h2. |Y+11s$o1L8pHJ7ى*Cϥ˗/2j__{ ȑ#Y5j(՜xuzԕIJF… @J.Dro,)_VlllkfL&222ٻw/7o,u_Z{{L9Y^Sĉ޽{VgϞc64%,7n(`efe'XX`V0 nMkJX/--/B7U,5k4t P!°BlFe !&lRR%j[W6HiUfs\LLc% ǨqP;,2Yޡre/bT* (S&A^K;ښAD9S&bY eTwQǴPRѩg;u7%--Y:EDv+­Q滛WFF;v߽{w^_v=HU2K+<<ɹ^wPeg}He8b|}},ͨQJmSSQ:} =2g7"##|.^8^z6hDDDlv IJFBJJJO_pݽ*ŔN&`*]YL:ƍxQFL:\[`Y}Q`g6X\]=N>-;v+Rx Ձr31GZt/ ^y/ rY/2 [[qpp'fY899駟r;ԨQ#j׮]vFEӚFrTai*}Az7̚5+孷7ofs`,M}< *$NWjJΩuuwҥK֤"Ujժ]m^}Z*`!ܵ Nl\M̀||=hͻNIIamڴv%xϯ!֗ϣ Xq;uLa2LddeW_5N;W`A*:pgA#OSHpϕA=za Slޱzhn||<}QZfͦ6 kR-Ḳ(̅Ql'CR6qzTbWT"bmhUCZkV+̸N.̛(5xnʵ=XE;v&O߽{)cƌ)stix E?B0DQL=}]z,+ N ;IU ""  kX#6w\qwlȲ+"^t*Ja,o)&wUб 'gΜZS/""ŵtZLv\t[!Pd9w <[h!sQ#sȻG0!5PD%.=x4kINHqW.r(9R7$/;Ȟ={^k֬Yb>-/^DVDݡeL "O~ٰõwׯ\ U öU8J`u5VIѱh|5wkEGG 2\4gպh~~}>պ.h&-8}jUcN9_ l@'2ScFR.9,`qOV(4+_x#%Vva4WTq1ev׳͜z]Y֎Ͼ;ypםs=]L̪gU=I7Anh2}mlLRLbcWjuFq=6P.4fJLLL?k%US{SYIRy IDAT9˗boEpIbFԮ[hf]6.}{1^D&L&튓MAu@o;H] z*Q./u9g2Vt?u2ז2i&Vo>^g-\/ދOt\n}T{)+`<~ْ/9t͚d[X>bzRRR~߄bH5]Ӡ=)'Gqq7˃ Zy鍢 ޚ)%?2rx3k KJMRpƍxEc6ܜ]ݐ &2RW+C^yE ]!RT#³Lq(,VA vttۭ3VmeR_2WwZ{Gq*h`=+V7333kS`Jxu;r.͚fFˡb0o{jm1[#NOdQLV(N{9weNLɩR6it:PaS<,gF4-& :h5{6"Q>GvN|1ۻ陥J?4pУ[61#"үqupӱ'O;[/}8F"|?! o=gN9,[A6ZZvY€u$",H\%IZ;7nr^*Z9XjOYXmW_OV/u;NگZvskz57߂$-Łͪ$ 1LtvE\8j; Ɗ+Bw}7>s5۝Hx"lBf3 {8m3@S98Cr-Ow-1D*)پnL_Ax_0IL!vDM4oGDXk׮MtiŪ|9Ё\El9hrYརiNH6DsI10=GEEO>\|1ɸn1 O 75Rj'YiZmfffuaaaՠAT+l߾Cp'! y^tJ~5 t9/^[CQ65էC aQnW>@|kE%33'ٹ}+|ci*z=(\.\.ҿD k{ Ykj: _.yѨirHJJZ+uؘPK|`v8|l)uQT/EQ<)Ǡ sYb!gBXWisWsC0 (: Jۄ0~&\w)qq_ vqTWU< lK7TVV󷱺:͒S{.Naϴع;S0ct=h/_}2/fz|ݝ;wf0 .6 BQAa~>@x˨PYUuTII믿FHJJڔdk ֮]4Mˁz&Kny```S9#/mOV+9ߒQa.TxOyd{EX@jnZ\.;d 7& T ,ەQq_+.| _](k`j .mlOLX=$H*G,C^~ f5ӧw~\k~_-k YdUQ HS6rʕfx<ɝV]M! Pmkm BجV.eggYlYYY[HQq:iD7ae7o8람>8r'ŞC{1@yrLl&}w>2*~[lX]ce o-CaNi5 [K9Dcnч1v(X"RZZʷkʕ4x}6 uml{J iV]WN窺-5 MID;)5W^ym<eӦMYy;w,UUyRQQqrAaTط¾GvUk'ʲiԩƍW<ҥK{t=6]+S<:vۧ:~{k/ɄQA~J|"N8ny .8.wr| .ۛ@CĤ&liu y=8HHYi_+̙`ڵi~ȑ\x~Qff{aDYN3:fA =_q]Pb3pkwlc; TWB$ 6'``(i2`/O)PY=|eE% hVkKA]שAׅ|hSfv v<.g8qbĉKicy^o)^~Zrx(+((T"n݆sν5--@ 0 /CG \GcCd'4M~LU:gefFVYs/~sBг؞ACe~6 ϧòM?/IJu7114XCPIV/yM4`0x2~uՕŹy9Ҳeyͷ#[l)OJN~qͦMCr)U$=x)* z:IEI*"IrkC6F,I ݁bsY(vz Z|&l)ݍc(43%H.U:ŰfڬUZ,VZ ץ=֥K6Eїt Kzbd*J9?3~vxM̯jyWʣ<al[_nڴh'MUfe+uq >yGFM1"I텅t]$I7o^N~8hk.83{Nn!U1"(jIi0T͊fYX7jCP5+jۡ7V1%IžARA_Lk'%F}e!uz{l闌:t]3?Y8p`ԩS۸qS|0bРA"޶m˦i~y0tM&@:wJwneI;lZSeZ9346<3yjSٮ{\>;/cBa~̫if#\Nrj\cMdHRQnʨXФܺ_|q;pE,^U駟~řguwFF (uy}c,=o$+]${ __runnn%M6XnMc)օ@n- aPxM CAfxsvvv_3RV ^/9dvbalj*/;ȿS- z x<|ݕ I}0qĪU{ץ?@=9f̘g{w P^^&%uk 1ft{TZWs~8]@W0w^r|S)z4ʭ޾\J/f#w/@Uy $Ձĥ$G_̤{# ?c6HO|i驗̛oVYYqsѣc By; ޓ9'Bj^ǒb?tUaXIQ~E!2ֵOW .^,XWæ}[/9͛7/'0r =j&=(@Eòhͅ(6)`{%'daek;Y%;(o2;lz|RE '/ʖ-[馛KZ4y½8hVԢNgg-p -up555֛,C}pq z$G%aYƮe%IfwA|y?EUh7L*"|Uy5$ܙkׯeӼ#G}̭{nus[ne~4y .8VP>XKw*++o׋=Yy6cGr'3lX]xO05‰wTk)i"ItB4kŦJxu;bͰ.$Si#JKMĠʕP\\_~GhWw|gG}t͛ ǟS駟f?V~޼gG~@0XR[S{ Qžw~M/<BD8ٳsFraҥ,Z5/ LBJVV1eI"ǾӀZ(jIBa% U@&B.Nw/^z0\eW]}}j?w%ÏF7nܨ{~5Р?}YǦ!Jt3f0#;v,K+ᔖՒ%,\%;Jv#spr$:8]ndYnSP} -X@A1:*v\Ʒ_%k2aD͘qby `|ajjjn#ًz +AH0sz1z}999L?H&Mİain:֮_ǚhh򒚟OưdFѸsr:T,JU"L)DDvSaQa`¸1L8#&O&)Us՗/[a;jkk#PCKX BcLˁmи}| c ѦF8|9#Ftk!uزmUUY&-?Ԣ~Yܟ>paRoósu۷P^ +3a1rƍG߾^:˗>wPWWGQnU^/눗HIIoZuZRRr닿܋%!c1=u@ְ󽷑v4`UU ѣ5j,4M***())%%عHNݧ>Xrg!93ll=?0uCSu% 4VV௮|7UNZZ d 2RXXe ˊ+|TʔE\rL m y}O/^HMM}yf~w֭C}mz0އ{ɌD([_UU4`T 2x O)..FӺ#P(@umeWVQ]SCǃa5}pHNbwX8Hh"!l|~"БeaجVd!?/l rr&7/׳n:]d-sWܣү{׹e7SRtm57X0rđk֬DZ-ge(1eJvx>yCzP$!??rrrׯOVF@&~?`]ױc0˅pnP˱h4ʮ]l߾[v:֬]K}]={v!#)>bfaDѣ =W UsFZds?iKV0jԨ䲊[k7ЋA/a%).=j#wdr'LaV6/;\k"H$¢i¾}#7;>}HKO#--t2IJN"%%]ÇD  QUUMee%555SQQΒʨ% ,cMqcڷq7z,.4э(E#4WK5"Aa왰[>d8\f [nQ/[dޛuKX[ۃLO@ك:Cj@m UWrW _lIx ( LUS1 WRacw8P]rTo-m BC!<H` aHaDzjВS22e>H:4 Èb:Q=Bs& kWՏVR1ӥ"h \kxS\\ijߵtM7l{ѳ% %,4}((H-ҕs@FAz$p0 i`p3c:4;0 1m{-.m "ŭǼx7Xmj˵;8(..jyMaayɌZ2.]Z]]`0\/8T IDAT^LC'h/-IRb% ,#Ir,T=WbGf,~pB)41d&BPFfA fغ<zYiUEرc~ ֭c7XS6_K3-[^F{ ^J ՖXNBD H,yIbbb D|PtЛ ,Mo*b r H#'UEU4d%x$w +^͠A۱c993yCerY1a$|^ZGN]_Z pef~T%@KNvwEӰZXmci`zjb/$uOZC%֖Lŧzٝ5w &_ozǜϚ_zR(HcgL߄ulJ$sT Mxe%畇1kh ҋvJ\yy}/o;{D ͉ٹt Dfjv0@#~cP[HwK%'#7okq[[]DgotLu_Jci) 0#Jqy:Lg!39.K?m!R5&x~aPrѲ:b={C走fKןI~5l&-!L؋;竪 XS,$@ Ir\KNu6fٗ0Ty~-~x!U1<7/غ&=%JhȜ?%=ە @QÇGeU'DLCdzckWv1MkML,R82"9*XmIG@4$2VGK Y b@X+Mo75mu>~d=^J2xW簱 ;wAXEۮaWep}ey7 okﯮjR~LÆՈ@#C9ĄXPwKLΏ29_]myJR! "A!$al)Ad[+0e@D2q0fVUMFH ftKNjFTKGhn3fm5"Q`r?k?VVt>#E/a%G-t &˝1Tq3r#&t:ú[ƛd ݎ^oҸ+e%qBQy>/Jc(O(F4 )kJ[rkzɔ1j6R>NP궹\ADn@ H 4ڐ8YqbY庿W_ l(VgcB/a%#G/tc ~Bp%&l| ns2"'sficN.2]vŗ\Fpq͘[4l`lĴ k2ѨBدY# ]ǖcI P#3j{)dj$ H9G”ę߈3+9Ij4KXs=ZGp%ƵqK,zSxIi~ @?uVgO0ai\ɝݲgl{pE%? +4%Vmtϸ2|1<3gI16O * ViCΓJ8 ZZ) s@WFDYsCjFj&!k”UKHfĶE2u8 Dn^{B * +.-4 \CE,CrmeRYq5?vDoV Do_bNғV#Wpt7ˋxڜZD:w}7{l;p\۫yx.ڧL$ *f`q$E BWJq`l6CH@~1X{&YDtHں_i!Рz,HA)jbO⫱RF 0kogڞy<)`\1WcI-Ùuy9ĭI? U\kyOu駟pϽlfԨ2VrD_#OEt]!dࠩA(![ >HbOj m`K⭵Hf ۸h-yQs,4ȜP%%!h"cKа9.Pmfq6vn+J(J@)@R$[oo%KFqJ + KH6eeZC>uƛ>0z] so0׾ȩ $Y&%5kB |q9G假T P^^΅^ /@n^6aW]yl14gkQuSp@ŞDsbwQ  :aw~R'fb6{"L% $E Ð>bj"K7㶷 ]vn]Oƚe:.&u1n/=CVuU.`EqDMMtUKX !!,I"%- 6ȈHM؏FuYr-1I,Hݐ;]wԢ(aPFu>wC@v[jm| c+P[ CF{CqS:C')K]ݷ+j qQdkxJƿE#)?#3LF=2&;i'X:ׂCyٶ +ׇA/a%F8?q%_;=G3Q>Ŝ{l~q)Spꩧnhâi٧1^BxN^R gU_DM :ɐc@H {.Bа!(@$朾r+J?"iBvBp\pB6 ɓ5~人[C{^MiqۜSV=(LīаJq6++TB >Lkjj?xЏVݲ/c|AJKK5 XmT]{Ի?KfA]IA(јY\}Ь:M64{A L!Zuh2[U*Xy U)U$H`sBȄr}SIG^Jd 0 yɞ~>yva{g¡`h]l6ٹ}RgOoJZ_KNT d (kpeqdD\O91)ԠCDFn C8Ԭr׈">-ԠW h*TZ25iYVc;UDԅ1y1wYsyOnٯVb7aJ/_`Z|l0sx)#Ţ-/M䘦|ǔ9MjHbK&SEv]AD%h[f-3Ăꌑs(qI@HA9 JL'7R7(Is.sϜ~+EG/a%f%,X. A4DcEIѩgv yO2'?{aذ[Yףn(2"i@*[$2bV4~PJʈ0RȄf)QՉF螯TC0QR{ߢ fRF pZA* i$h++c(4F~}K>+?dV`臈z%$ gr:᠏H([WH$_w\?pʬ\sU6Cc3F5hr xkԮt:@VRYiPށsE*_ڽ$#>Ġ~QdhsLF2MZ1 |{!dzrw(4IHsŏ ?!7`áa <I9HNC;WR*V3IL,ls~Ν;ylhop||Nbͷr${KAFS#(ReLLU3 Xz[B 74Ge^q2g@T9FV$!*͋ZTkrl0=(=^ +F(񿌉rvG2È"2>qsŗp9gso馛8q<3hթvcmZK(K3 qj,ՠrA[ojYc+aȱ4Y`%L=v~{s =7^(m"h/uy>ɂ<>íߺh` 8zqLtkjIL|?EHJIoWh6x(xK˙}|{cҤI(=wƝGřŭ+-&nާZp/& _R D׊pq)\cJ'd"kViaL 625#`I7I; ,Y`˗br0Jل DC$ U]:skjY1=-)LaDab+$~走#0ކ@3$ 3b#`:[/ĝwqɹrO#%t'_i:8p$k(jYDm--IH_c]+3 EBdjt &RD2Dl%PV l2 IcJO"C"]F \LGHJtqQ A͘)]{OEڇ}]#-/ΊMaTgHnAkgƴze% `C- 牄iԮ'j>O~'5<8lH"$ sf>o\1?nl/we&x>Q4AR&XgpIvݿE&\!2N&CFREͩ,!t0byYQ9vWhRayء6vmnl-n,CO=$%LC1Jr,eu32py 8sz{W%JjoOgrc'hKXXZm6})+HΛLR’ꊤ:'tv:f,Y럗_3PU&CW߯ntss iȨEKVݢaQcMzs^/O;yheK[5eKIQtΠA>DAyIr*ʤd&7> iN:HIDʺDڼ`Hq]O|{?;~Ի89(">/i8bIyY2es}ٞL`GMfh5:TW3Iy"m>S'e*G]-EKk7c\jiSOfVjѣlzE=^9z +A5)Y\I=2$ˌ26ܫaΟFQ 9y.QyP"Hv}Ow+<( ~4Iqu fI;Ik_ pX~kQ7UB/Yi Fvz=*]z~>nG$g%AVP}}QO@c(S !v%_/pD_`<ш BX.,ﶳ[r^ KdI* }p͚:U~ -~'n0Qap{<.Eb(23Y~jvVB@>\^'V ]Ʌ}{lȵO߇ j핷1,3xrW]i W┰9#ά+$鿿]ƕ>p5Gg{͇LlU{".)0W\_C;ܱ}ƭ!wKQ`>^^'z +A5zj{løvaa,ٴ35O((>[9qz( ݓUr~eIʎ1@.K/^Jv2k9ΪG$P 2{z'(q=/fonU˄BK<}'~F,~3[ȯOM> nu ?4 aIDATn3[wEȒ+24[3UO?je zKmzXC0XƲjwt/#>/ʧ _ox)dՌb6:6?`OM)!0!H,Leq(Xc֥(ymq8!hsm%rx8fpЩ#lXV,ohk6fϑ T5Y#@( 'J2کnȊ<%Ț]g$i߾&,gM t H)4zv}qzg;ssF`M! GNZ܎kz+GT $ @)(dWт[|YI Nq (*)=iRL~yz[c`pr6# IXd6gvЩ @wҀse3б" +Ĭc]4Tt32`)`@2& L  \d\;.z&lٍ؅Qo})()F*޿M!d[:$4 IYN8*IЀքխ{xlGw.zi8X>d8">qsGPQ )(ov3N \dqm_~Lk |j'6/qSNvJ0K2 ܟA(|I=USzUl;őE@iG_Ց7;e'4ՠs08=3ѝۛÇҐIzu.K}.) |z:^۸Uu"`0OD![?ăþb/穸❶WHU|^hz3Mˁ,چs']?Poݞ܎se@س׫5mh3$ V[v;cX|38kv&Vg +{}XpFsP5l-ZuUJ TY}!μuJycS so4h 6W-f6IYdO'<67ʸbPP@2Qku5HMIAذn.:zcNl>]:s}C\g`Fs!]]s9Wxs焱 DigauaWV`*D` -ވ>% =qY3)P_OO`/x Y! WaH-sȠ4 r.qʙV=n7Ը ҇:|PHsS(2x sD+WC=;}&L@; {Z凿*|S޻%X](I WXa򺭶}!UUV߅XewAQZPTV˞5n|+|-`"hH ޭs )A&67G\YP@蝣H E_]ᖠc~Z>؜u^Rn-yWAV9$aO]u!}Ui[ZO<[v 3a@V_lw ]E{ xdr>EHA$a%³>:( &qnP4D +,4 RL)T./(`)p M=>AR( !^z}. iMgz HCe Vw|I7E)PD~C!{_qĒ׏vǨ[}k [@yD$Btw]E=_b7a pbŞ_w~@dD+8B֗ZJVP6g]ͻ'yX cGvj"#B$PNESUo)tVd$<FGIXSx}KVzv9O*VUei5Օ֧fgr=%5{0?g$j:POkuVWQ`.oq7+жvfHga0H9$V?}xVc۱ nkc!N ] i`q:9L,gv) _|.hy)PPe{jjR?$ۧz$ P5 Gs$$(\u荦}yE'9Z:z^p$,G4I&V^zp<=جN[ ;F;A.^΀ q uFy֒}n߉ѭa8tQTY_5?8# +uM蚵}ٽ;NX.b3g/p5ԎI04 k-l~3X$! )9ccXFSe+-Aj8a{[8"~8fY׏N6f+@48NNE:p`Mdہ?{} ;unˉ<oLW Y7oh^g+-AIJg+馪1]ާ[,r8e "'9$aŀvq'%QB__\;Õ^PB8ZMVl ߭)7_6۳ :Ⱦ`6GK጗{Voq +F0 3ޛ~;N+ameeY @WfɆΌASd hA)UYk=E fEUUՈ-J`xkC_JB sUx}qb3Kv|_hs# +v)E_?\B'>]UUt5ҰD'ȩg<+MbGDIX1$)8k㟾a8ywI6+3rG9N*)) ^e;?n7q\sFEq'-`춚'/uz}mLǂQ|׋W^PgϞQu=$aE)MӨe˖YYYtCCt:鄄ZQ8,R$ PZfMwkWy)4mncZNۂ -e+ $SvgJ˶&79s;v˲(q$I,G/TáFRMJJR'N륝HŠ<iQ$I 9fƴsr{k/?iW;J;yhصnizvMy'nCxf+|,˪ǩGy^iZaFy^)))Qjkk]j;ʐ"k֬N'qi95d֯_oڼͅ?z[vWVŰFp762z|#ώwⳏN?MǷ4ٝ{_yaa6^/*nh4J#F$vbhZx+W33 0L,F%D2YƽMtڏAgzfqfF4MAVMS_!M "k (^W0L(Ӗ/_j*i䂡Hj\/LիYe#+> x$izl-.ogJxg\>)b8qb]Qwuz bTwFQ;_|)q'Z.\wU#Z\)z}Zq8l%A59$lC#GlTNQTTU2eNgzӃm-. Wn؆mƖF}87:NiZT:zh?}Ixrss!CE`Y;1T2k]s-i՗YъPU _M[dmsG8ܘc4! y^/iŊyyyl暛9aXYMӘH&K(QiVsu v$+IiO͙3 REQ4^/˲, IQP"K$04^lߍp7d ovI(J98i$K8\DzJӴBQ:⽦F|d{]-IͲ,msEBNNqݪlVNj4UŢfuӦM*IFс1 77ٳ'e6)BFJSNyx<4#U88n7e2 I W{H:NeYͬ1~m.I&If45I4ɤ|>iFQEQKMMU[ZZ4ŢM0A%.3.sϞ=;sB e_                    BXJ*IENDB`golang-github-cilium-ebpf-0.11.0/.gitignore000066400000000000000000000003161456504511400205070ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib *.o !*_bpf*.o # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out golang-github-cilium-ebpf-0.11.0/.golangci.yaml000066400000000000000000000006471456504511400212530ustar00rootroot00000000000000--- issues: exclude-rules: # syscall param structs will have unused fields in Go code. - path: syscall.*.go linters: - structcheck linters: disable-all: true enable: - errcheck - goimports - gosimple - govet - ineffassign - misspell - staticcheck - typecheck - unused - gofmt # Could be enabled later: # - gocyclo # - maligned # - gosec golang-github-cilium-ebpf-0.11.0/.semaphore/000077500000000000000000000000001456504511400205605ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/.semaphore/semaphore.yml000066400000000000000000000056061456504511400232750ustar00rootroot00000000000000version: v1.0 name: CI Build agent: machine: type: e1-standard-2 os_image: ubuntu2004 auto_cancel: running: when: "branch != 'master'" blocks: - name: Run tests task: prologue: commands: - sudo sh -c 'swapoff -a && fallocate -l 2G /swapfile && chmod 0600 /swapfile && mkswap /swapfile && swapon /swapfile' - sem-version go 1.20.1 - export PATH="$PATH:$(go env GOPATH)/bin" - checkout # Disabled, see https://github.com/cilium/ebpf/issues/898 # - cache restore - go mod tidy - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin" v1.51.2 - go install gotest.tools/gotestsum@v1.8.1 - sudo pip3 install https://github.com/amluto/virtme/archive/beb85146cd91de37ae455eccb6ab67c393e6e290.zip - sudo apt-get update - sudo apt-get install -y --no-install-recommends qemu-system-x86 clang-9 llvm-9 - sudo dmesg -C epilogue: always: commands: - sudo dmesg - test-results publish junit.xml env_vars: - name: TMPDIR value: /tmp - name: CI_MAX_KERNEL_VERSION value: "5.19" - name: CI_MIN_CLANG_VERSION value: "9" jobs: - name: Build and Lint execution_time_limit: minutes: 10 commands: - ( export GOOS=darwin; go build ./... && for p in $(go list ./...) ; do go test -c $p || exit ; done ) - ( export GOARCH=arm GOARM=6; go build ./... && for p in $(go list ./...) ; do go test -c $p || exit ; done ) - ( export GOARCH=arm64; go build ./... && for p in $(go list ./...) ; do go test -c $p || exit ; done ) - make clean - make container-all - git diff --exit-code || { echo "found unformatted source files, or generated files are not up to date, run 'make'" >&2; false; } - pushd ./examples - go build -v -o "$(mktemp -d)" ./... - popd - golangci-lint run - cache store - name: Run unit tests on previous stable Go execution_time_limit: minutes: 10 commands: - sem-version go 1.19.6 - go test -v ./cmd/bpf2go - gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- ./run-tests.sh $CI_MAX_KERNEL_VERSION -short -count 1 -json ./... - name: Run unit tests execution_time_limit: minutes: 10 matrix: - env_var: KERNEL_VERSION values: ["5.19", "5.15", "5.10", "5.4", "4.19", "4.14", "4.9"] commands: - gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- ./run-tests.sh $KERNEL_VERSION -short -count 1 -json ./... golang-github-cilium-ebpf-0.11.0/ARCHITECTURE.md000066400000000000000000000073371456504511400207350ustar00rootroot00000000000000Architecture of the library === ```mermaid graph RL Program --> ProgramSpec --> ELF btf.Spec --> ELF Map --> MapSpec --> ELF Links --> Map & Program ProgramSpec -.-> btf.Spec MapSpec -.-> btf.Spec subgraph Collection Program & Map end subgraph CollectionSpec ProgramSpec & MapSpec & btf.Spec end ``` ELF --- BPF is usually produced by using Clang to compile a subset of C. Clang outputs an ELF file which contains program byte code (aka BPF), but also metadata for maps used by the program. The metadata follows the conventions set by libbpf shipped with the kernel. Certain ELF sections have special meaning and contain structures defined by libbpf. Newer versions of clang emit additional metadata in [BPF Type Format](#BTF). The library aims to be compatible with libbpf so that moving from a C toolchain to a Go one creates little friction. To that end, the [ELF reader](elf_reader.go) is tested against the Linux selftests and avoids introducing custom behaviour if possible. The output of the ELF reader is a `CollectionSpec` which encodes all of the information contained in the ELF in a form that is easy to work with in Go. The returned `CollectionSpec` should be deterministic: reading the same ELF file on different systems must produce the same output. As a corollary, any changes that depend on the runtime environment like the current kernel version must happen when creating [Objects](#Objects). Specifications --- `CollectionSpec` is a very simple container for `ProgramSpec`, `MapSpec` and `btf.Spec`. Avoid adding functionality to it if possible. `ProgramSpec` and `MapSpec` are blueprints for in-kernel objects and contain everything necessary to execute the relevant `bpf(2)` syscalls. They refer to `btf.Spec` for type information such as `Map` key and value types. The [asm](asm/) package provides an assembler that can be used to generate `ProgramSpec` on the fly. Objects --- `Program` and `Map` are the result of loading specifications into the kernel. Features that depend on knowledge of the current system (e.g kernel version) are implemented at this point. Sometimes loading a spec will fail because the kernel is too old, or a feature is not enabled. There are multiple ways the library deals with that: * Fallback: older kernels don't allow naming programs and maps. The library automatically detects support for names, and omits them during load if necessary. This works since name is primarily a debug aid. * Sentinel error: sometimes it's possible to detect that a feature isn't available. In that case the library will return an error wrapping `ErrNotSupported`. This is also useful to skip tests that can't run on the current kernel. Once program and map objects are loaded they expose the kernel's low-level API, e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer wrappers on top of the low-level API, like `MapIterator`. The low-level API is useful when our higher-level API doesn't support a particular use case. Links --- Programs can be attached to many different points in the kernel and newer BPF hooks tend to use bpf_link to do so. Older hooks unfortunately use a combination of syscalls, netlink messages, etc. Adding support for a new link type should not pull in large dependencies like netlink, so XDP programs or tracepoints are out of scope. Each bpf_link_type has one corresponding Go type, e.g. `link.tracing` corresponds to BPF_LINK_TRACING. In general, these types should be unexported as long as they don't export methods outside of the Link interface. Each Go type may have multiple exported constructors. For example `AttachTracing` and `AttachLSM` create a tracing link, but are distinct functions since they may require different arguments. golang-github-cilium-ebpf-0.11.0/CODE_OF_CONDUCT.md000066400000000000000000000062551456504511400213260ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nathanjsweet at gmail dot com or i at lmb dot io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ golang-github-cilium-ebpf-0.11.0/CONTRIBUTING.md000066400000000000000000000036351456504511400207570ustar00rootroot00000000000000# How to contribute Development is on [GitHub](https://github.com/cilium/ebpf) and contributions in the form of pull requests and issues reporting bugs or suggesting new features are welcome. Please take a look at [the architecture](ARCHITECTURE.md) to get a better understanding for the high-level goals. ## Adding a new feature 1. [Join](https://ebpf.io/slack) the [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel to discuss your requirements and how the feature can be implemented. The most important part is figuring out how much new exported API is necessary. **The less new API is required the easier it will be to land the feature.** 2. (*optional*) Create a draft PR if you want to discuss the implementation or have hit a problem. It's fine if this doesn't compile or contains debug statements. 3. Create a PR that is ready to merge. This must pass CI and have tests. ### API stability The library doesn't guarantee the stability of its API at the moment. 1. If possible avoid breakage by introducing new API and deprecating the old one at the same time. If an API was deprecated in v0.x it can be removed in v0.x+1. 2. Breaking API in a way that causes compilation failures is acceptable but must have good reasons. 3. Changing the semantics of the API without causing compilation failures is heavily discouraged. ## Running the tests Many of the tests require privileges to set resource limits and load eBPF code. The easiest way to obtain these is to run the tests with `sudo`. To test the current package with your local kernel you can simply run: ``` go test -exec sudo ./... ``` To test the current package with a different kernel version you can use the [run-tests.sh](run-tests.sh) script. It requires [virtme](https://github.com/amluto/virtme) and qemu to be installed. Examples: ```bash # Run all tests on a 5.4 kernel ./run-tests.sh 5.4 # Run a subset of tests: ./run-tests.sh 5.4 ./link ``` golang-github-cilium-ebpf-0.11.0/LICENSE000066400000000000000000000021661456504511400175310ustar00rootroot00000000000000MIT License Copyright (c) 2017 Nathan Sweet Copyright (c) 2018, 2019 Cloudflare Copyright (c) 2019 Authors of Cilium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-cilium-ebpf-0.11.0/MAINTAINERS.md000066400000000000000000000002151456504511400206110ustar00rootroot00000000000000# Maintainers Maintainers can be found in the [Cilium Maintainers file](https://github.com/cilium/community/blob/main/roles/Maintainers.md) golang-github-cilium-ebpf-0.11.0/Makefile000066400000000000000000000073431456504511400201660ustar00rootroot00000000000000# The development version of clang is distributed as the 'clang' binary, # while stable/released versions have a version number attached. # Pin the default clang to a stable version. CLANG ?= clang-14 STRIP ?= llvm-strip-14 OBJCOPY ?= llvm-objcopy-14 CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/ # Obtain an absolute path to the directory of the Makefile. # Assume the Makefile is in the root of the repository. REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) UIDGID := $(shell stat -c '%u:%g' ${REPODIR}) # Prefer podman if installed, otherwise use docker. # Note: Setting the var at runtime will always override. CONTAINER_ENGINE ?= $(if $(shell command -v podman), podman, docker) CONTAINER_RUN_ARGS ?= $(if $(filter ${CONTAINER_ENGINE}, podman), --log-driver=none, --user "${UIDGID}") IMAGE := $(shell cat ${REPODIR}/testdata/docker/IMAGE) VERSION := $(shell cat ${REPODIR}/testdata/docker/VERSION) # clang <8 doesn't tag relocs properly (STT_NOTYPE) # clang 9 is the first version emitting BTF TARGETS := \ testdata/loader-clang-7 \ testdata/loader-clang-9 \ testdata/loader-$(CLANG) \ testdata/manyprogs \ testdata/btf_map_init \ testdata/invalid_map \ testdata/raw_tracepoint \ testdata/invalid_map_static \ testdata/invalid_btf_map_init \ testdata/strings \ testdata/freplace \ testdata/iproute2_map_compat \ testdata/map_spin_lock \ testdata/subprog_reloc \ testdata/fwd_decl \ testdata/kconfig \ testdata/kconfig_config \ testdata/kfunc \ testdata/invalid-kfunc \ testdata/kfunc-kmod \ btf/testdata/relocs \ btf/testdata/relocs_read \ btf/testdata/relocs_read_tgt \ cmd/bpf2go/testdata/minimal .PHONY: all clean container-all container-shell generate .DEFAULT_TARGET = container-all # Build all ELF binaries using a containerized LLVM toolchain. container-all: +${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ -v "${REPODIR}":/ebpf -w /ebpf --env MAKEFLAGS \ --env CFLAGS="-fdebug-prefix-map=/ebpf=." \ --env HOME="/tmp" \ "${IMAGE}:${VERSION}" \ make all # (debug) Drop the user into a shell inside the container as root. container-shell: ${CONTAINER_ENGINE} run --rm -ti \ -v "${REPODIR}":/ebpf -w /ebpf \ "${IMAGE}:${VERSION}" clean: -$(RM) testdata/*.elf -$(RM) btf/testdata/*.elf format: find . -type f -name "*.c" | xargs clang-format -i all: format $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) generate ln -srf testdata/loader-$(CLANG)-el.elf testdata/loader-el.elf ln -srf testdata/loader-$(CLANG)-eb.elf testdata/loader-eb.elf # $BPF_CLANG is used in go:generate invocations. generate: export BPF_CLANG := $(CLANG) generate: export BPF_CFLAGS := $(CFLAGS) generate: go generate ./... testdata/loader-%-el.elf: testdata/loader.c $* $(CFLAGS) -target bpfel -c $< -o $@ $(STRIP) -g $@ testdata/loader-%-eb.elf: testdata/loader.c $* $(CFLAGS) -target bpfeb -c $< -o $@ $(STRIP) -g $@ %-el.elf: %.c $(CLANG) $(CFLAGS) -target bpfel -c $< -o $@ $(STRIP) -g $@ %-eb.elf : %.c $(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@ $(STRIP) -g $@ .PHONY: generate-btf generate-btf: KERNEL_VERSION?=5.19 generate-btf: $(eval TMP := $(shell mktemp -d)) curl -fL "$(CI_KERNEL_URL)/linux-$(KERNEL_VERSION).bz" -o "$(TMP)/bzImage" /lib/modules/$(uname -r)/build/scripts/extract-vmlinux "$(TMP)/bzImage" > "$(TMP)/vmlinux" $(OBJCOPY) --dump-section .BTF=/dev/stdout "$(TMP)/vmlinux" /dev/null | gzip > "btf/testdata/vmlinux.btf.gz" curl -fL "$(CI_KERNEL_URL)/linux-$(KERNEL_VERSION)-selftests-bpf.tgz" -o "$(TMP)/selftests.tgz" tar -xf "$(TMP)/selftests.tgz" --to-stdout tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.ko | \ $(OBJCOPY) --dump-section .BTF="btf/testdata/btf_testmod.btf" - /dev/null $(RM) -r "$(TMP)" golang-github-cilium-ebpf-0.11.0/README.md000066400000000000000000000066601456504511400200060ustar00rootroot00000000000000# eBPF [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) ![HoneyGopher](.github/images/cilium-ebpf.png) ebpf-go is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes. See [ebpf.io](https://ebpf.io) for complementary projects from the wider eBPF ecosystem. ## Getting Started A small collection of Go and eBPF programs that serve as examples for building your own tools can be found under [examples/](examples/). [Contributions](CONTRIBUTING.md) are highly encouraged, as they highlight certain use cases of eBPF and the library, and help shape the future of the project. ## Getting Help The community actively monitors our [GitHub Discussions](https://github.com/cilium/ebpf/discussions) page. Please search for existing threads before starting a new one. Refrain from opening issues on the bug tracker if you're just starting out or if you're not sure if something is a bug in the library code. Alternatively, [join](https://ebpf.io/slack) the [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you have other questions regarding the project. Note that this channel is ephemeral and has its history erased past a certain point, which is less helpful for others running into the same problem later. ## Packages This library includes the following packages: * [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic assembler, allowing you to write eBPF assembly instructions directly within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.) * [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows compiling and embedding eBPF programs written in C within Go code. As well as compiling the C code, it auto-generates Go code for loading and manipulating the eBPF program and map objects. * [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF to various hooks * [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a `PERF_EVENT_ARRAY` * [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a `BPF_MAP_TYPE_RINGBUF` map * [features](https://pkg.go.dev/github.com/cilium/ebpf/features) implements the equivalent of `bpftool feature probe` for discovering BPF-related kernel features using native Go. * [rlimit](https://pkg.go.dev/github.com/cilium/ebpf/rlimit) provides a convenient API to lift the `RLIMIT_MEMLOCK` constraint on kernels before 5.11. * [btf](https://pkg.go.dev/github.com/cilium/ebpf/btf) allows reading the BPF Type Format. ## Requirements * A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy) * Linux >= 4.9. CI is run against kernel.org LTS releases. 4.4 should work but is not tested against. ## Regenerating Testdata Run `make` in the root of this repository to rebuild testdata in all subpackages. This requires Docker, as it relies on a standardized build environment to keep the build output stable. It is possible to regenerate data using Podman by overriding the `CONTAINER_*` variables: `CONTAINER_ENGINE=podman CONTAINER_RUN_ARGS= make`. The toolchain image build files are kept in [testdata/docker/](testdata/docker/). ## License MIT ### eBPF Gopher The eBPF honeygopher is based on the Go gopher designed by Renee French. golang-github-cilium-ebpf-0.11.0/asm/000077500000000000000000000000001456504511400172775ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/asm/alu.go000066400000000000000000000062161456504511400204140ustar00rootroot00000000000000package asm //go:generate stringer -output alu_string.go -type=Source,Endianness,ALUOp // Source of ALU / ALU64 / Branch operations // // msb lsb // +----+-+---+ // |op |S|cls| // +----+-+---+ type Source uint8 const sourceMask OpCode = 0x08 // Source bitmask const ( // InvalidSource is returned by getters when invoked // on non ALU / branch OpCodes. InvalidSource Source = 0xff // ImmSource src is from constant ImmSource Source = 0x00 // RegSource src is from register RegSource Source = 0x08 ) // The Endianness of a byte swap instruction. type Endianness uint8 const endianMask = sourceMask // Endian flags const ( InvalidEndian Endianness = 0xff // Convert to little endian LE Endianness = 0x00 // Convert to big endian BE Endianness = 0x08 ) // ALUOp are ALU / ALU64 operations // // msb lsb // +----+-+---+ // |OP |s|cls| // +----+-+---+ type ALUOp uint8 const aluMask OpCode = 0xf0 const ( // InvalidALUOp is returned by getters when invoked // on non ALU OpCodes InvalidALUOp ALUOp = 0xff // Add - addition Add ALUOp = 0x00 // Sub - subtraction Sub ALUOp = 0x10 // Mul - multiplication Mul ALUOp = 0x20 // Div - division Div ALUOp = 0x30 // Or - bitwise or Or ALUOp = 0x40 // And - bitwise and And ALUOp = 0x50 // LSh - bitwise shift left LSh ALUOp = 0x60 // RSh - bitwise shift right RSh ALUOp = 0x70 // Neg - sign/unsign signing bit Neg ALUOp = 0x80 // Mod - modulo Mod ALUOp = 0x90 // Xor - bitwise xor Xor ALUOp = 0xa0 // Mov - move value from one place to another Mov ALUOp = 0xb0 // ArSh - arithmatic shift ArSh ALUOp = 0xc0 // Swap - endian conversions Swap ALUOp = 0xd0 ) // HostTo converts from host to another endianness. func HostTo(endian Endianness, dst Register, size Size) Instruction { var imm int64 switch size { case Half: imm = 16 case Word: imm = 32 case DWord: imm = 64 default: return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)), Dst: dst, Constant: imm, } } // Op returns the OpCode for an ALU operation with a given source. func (op ALUOp) Op(source Source) OpCode { return OpCode(ALU64Class).SetALUOp(op).SetSource(source) } // Reg emits `dst (op) src`. func (op ALUOp) Reg(dst, src Register) Instruction { return Instruction{ OpCode: op.Op(RegSource), Dst: dst, Src: src, } } // Imm emits `dst (op) value`. func (op ALUOp) Imm(dst Register, value int32) Instruction { return Instruction{ OpCode: op.Op(ImmSource), Dst: dst, Constant: int64(value), } } // Op32 returns the OpCode for a 32-bit ALU operation with a given source. func (op ALUOp) Op32(source Source) OpCode { return OpCode(ALUClass).SetALUOp(op).SetSource(source) } // Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst. func (op ALUOp) Reg32(dst, src Register) Instruction { return Instruction{ OpCode: op.Op32(RegSource), Dst: dst, Src: src, } } // Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst. func (op ALUOp) Imm32(dst Register, value int32) Instruction { return Instruction{ OpCode: op.Op32(ImmSource), Dst: dst, Constant: int64(value), } } golang-github-cilium-ebpf-0.11.0/asm/alu_string.go000066400000000000000000000045071456504511400220030ustar00rootroot00000000000000// Code generated by "stringer -output alu_string.go -type=Source,Endianness,ALUOp"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidSource-255] _ = x[ImmSource-0] _ = x[RegSource-8] } const ( _Source_name_0 = "ImmSource" _Source_name_1 = "RegSource" _Source_name_2 = "InvalidSource" ) func (i Source) String() string { switch { case i == 0: return _Source_name_0 case i == 8: return _Source_name_1 case i == 255: return _Source_name_2 default: return "Source(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidEndian-255] _ = x[LE-0] _ = x[BE-8] } const ( _Endianness_name_0 = "LE" _Endianness_name_1 = "BE" _Endianness_name_2 = "InvalidEndian" ) func (i Endianness) String() string { switch { case i == 0: return _Endianness_name_0 case i == 8: return _Endianness_name_1 case i == 255: return _Endianness_name_2 default: return "Endianness(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidALUOp-255] _ = x[Add-0] _ = x[Sub-16] _ = x[Mul-32] _ = x[Div-48] _ = x[Or-64] _ = x[And-80] _ = x[LSh-96] _ = x[RSh-112] _ = x[Neg-128] _ = x[Mod-144] _ = x[Xor-160] _ = x[Mov-176] _ = x[ArSh-192] _ = x[Swap-208] } const _ALUOp_name = "AddSubMulDivOrAndLShRShNegModXorMovArShSwapInvalidALUOp" var _ALUOp_map = map[ALUOp]string{ 0: _ALUOp_name[0:3], 16: _ALUOp_name[3:6], 32: _ALUOp_name[6:9], 48: _ALUOp_name[9:12], 64: _ALUOp_name[12:14], 80: _ALUOp_name[14:17], 96: _ALUOp_name[17:20], 112: _ALUOp_name[20:23], 128: _ALUOp_name[23:26], 144: _ALUOp_name[26:29], 160: _ALUOp_name[29:32], 176: _ALUOp_name[32:35], 192: _ALUOp_name[35:39], 208: _ALUOp_name[39:43], 255: _ALUOp_name[43:55], } func (i ALUOp) String() string { if str, ok := _ALUOp_map[i]; ok { return str } return "ALUOp(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.11.0/asm/doc.go000066400000000000000000000000761456504511400203760ustar00rootroot00000000000000// Package asm is an assembler for eBPF bytecode. package asm golang-github-cilium-ebpf-0.11.0/asm/dsl_test.go000066400000000000000000000025471456504511400214570ustar00rootroot00000000000000package asm import ( "testing" ) func TestDSL(t *testing.T) { testcases := []struct { name string have Instruction want Instruction }{ {"Call", FnMapLookupElem.Call(), Instruction{OpCode: 0x85, Constant: 1}}, {"Exit", Return(), Instruction{OpCode: 0x95}}, {"LoadAbs", LoadAbs(2, Byte), Instruction{OpCode: 0x30, Constant: 2}}, {"Store", StoreMem(RFP, -4, R0, Word), Instruction{ OpCode: 0x63, Dst: RFP, Src: R0, Offset: -4, }}, {"Add.Imm", Add.Imm(R1, 22), Instruction{OpCode: 0x07, Dst: R1, Constant: 22}}, {"Add.Reg", Add.Reg(R1, R2), Instruction{OpCode: 0x0f, Dst: R1, Src: R2}}, {"Add.Imm32", Add.Imm32(R1, 22), Instruction{ OpCode: 0x04, Dst: R1, Constant: 22, }}, {"JSGT.Imm", JSGT.Imm(R1, 4, "foo"), Instruction{ OpCode: 0x65, Dst: R1, Constant: 4, Offset: -1, }.WithReference("foo")}, {"JSGT.Imm32", JSGT.Imm32(R1, -2, "foo"), Instruction{ OpCode: 0x66, Dst: R1, Constant: -2, Offset: -1, }.WithReference("foo")}, {"JSLT.Reg", JSLT.Reg(R1, R2, "foo"), Instruction{ OpCode: 0xcd, Dst: R1, Src: R2, Offset: -1, }.WithReference("foo")}, {"JSLT.Reg32", JSLT.Reg32(R1, R3, "foo"), Instruction{ OpCode: 0xce, Dst: R1, Src: R3, Offset: -1, }.WithReference("foo")}, } for _, tc := range testcases { if !tc.have.equal(tc.want) { t.Errorf("%s: have %v, want %v", tc.name, tc.have, tc.want) } } } golang-github-cilium-ebpf-0.11.0/asm/func.go000066400000000000000000000104531456504511400205640ustar00rootroot00000000000000package asm //go:generate stringer -output func_string.go -type=BuiltinFunc // BuiltinFunc is a built-in eBPF function. type BuiltinFunc int32 func (_ BuiltinFunc) Max() BuiltinFunc { return maxBuiltinFunc - 1 } // eBPF built-in functions // // You can regenerate this list using the following gawk script: // // /FN\(.+\),/ { // match($1, /\(([a-z_0-9]+),/, r) // split(r[1], p, "_") // printf "Fn" // for (i in p) { // printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2) // } // print "" // } // // The script expects include/uapi/linux/bpf.h as it's input. const ( FnUnspec BuiltinFunc = iota FnMapLookupElem FnMapUpdateElem FnMapDeleteElem FnProbeRead FnKtimeGetNs FnTracePrintk FnGetPrandomU32 FnGetSmpProcessorId FnSkbStoreBytes FnL3CsumReplace FnL4CsumReplace FnTailCall FnCloneRedirect FnGetCurrentPidTgid FnGetCurrentUidGid FnGetCurrentComm FnGetCgroupClassid FnSkbVlanPush FnSkbVlanPop FnSkbGetTunnelKey FnSkbSetTunnelKey FnPerfEventRead FnRedirect FnGetRouteRealm FnPerfEventOutput FnSkbLoadBytes FnGetStackid FnCsumDiff FnSkbGetTunnelOpt FnSkbSetTunnelOpt FnSkbChangeProto FnSkbChangeType FnSkbUnderCgroup FnGetHashRecalc FnGetCurrentTask FnProbeWriteUser FnCurrentTaskUnderCgroup FnSkbChangeTail FnSkbPullData FnCsumUpdate FnSetHashInvalid FnGetNumaNodeId FnSkbChangeHead FnXdpAdjustHead FnProbeReadStr FnGetSocketCookie FnGetSocketUid FnSetHash FnSetsockopt FnSkbAdjustRoom FnRedirectMap FnSkRedirectMap FnSockMapUpdate FnXdpAdjustMeta FnPerfEventReadValue FnPerfProgReadValue FnGetsockopt FnOverrideReturn FnSockOpsCbFlagsSet FnMsgRedirectMap FnMsgApplyBytes FnMsgCorkBytes FnMsgPullData FnBind FnXdpAdjustTail FnSkbGetXfrmState FnGetStack FnSkbLoadBytesRelative FnFibLookup FnSockHashUpdate FnMsgRedirectHash FnSkRedirectHash FnLwtPushEncap FnLwtSeg6StoreBytes FnLwtSeg6AdjustSrh FnLwtSeg6Action FnRcRepeat FnRcKeydown FnSkbCgroupId FnGetCurrentCgroupId FnGetLocalStorage FnSkSelectReuseport FnSkbAncestorCgroupId FnSkLookupTcp FnSkLookupUdp FnSkRelease FnMapPushElem FnMapPopElem FnMapPeekElem FnMsgPushData FnMsgPopData FnRcPointerRel FnSpinLock FnSpinUnlock FnSkFullsock FnTcpSock FnSkbEcnSetCe FnGetListenerSock FnSkcLookupTcp FnTcpCheckSyncookie FnSysctlGetName FnSysctlGetCurrentValue FnSysctlGetNewValue FnSysctlSetNewValue FnStrtol FnStrtoul FnSkStorageGet FnSkStorageDelete FnSendSignal FnTcpGenSyncookie FnSkbOutput FnProbeReadUser FnProbeReadKernel FnProbeReadUserStr FnProbeReadKernelStr FnTcpSendAck FnSendSignalThread FnJiffies64 FnReadBranchRecords FnGetNsCurrentPidTgid FnXdpOutput FnGetNetnsCookie FnGetCurrentAncestorCgroupId FnSkAssign FnKtimeGetBootNs FnSeqPrintf FnSeqWrite FnSkCgroupId FnSkAncestorCgroupId FnRingbufOutput FnRingbufReserve FnRingbufSubmit FnRingbufDiscard FnRingbufQuery FnCsumLevel FnSkcToTcp6Sock FnSkcToTcpSock FnSkcToTcpTimewaitSock FnSkcToTcpRequestSock FnSkcToUdp6Sock FnGetTaskStack FnLoadHdrOpt FnStoreHdrOpt FnReserveHdrOpt FnInodeStorageGet FnInodeStorageDelete FnDPath FnCopyFromUser FnSnprintfBtf FnSeqPrintfBtf FnSkbCgroupClassid FnRedirectNeigh FnPerCpuPtr FnThisCpuPtr FnRedirectPeer FnTaskStorageGet FnTaskStorageDelete FnGetCurrentTaskBtf FnBprmOptsSet FnKtimeGetCoarseNs FnImaInodeHash FnSockFromFile FnCheckMtu FnForEachMapElem FnSnprintf FnSysBpf FnBtfFindByNameKind FnSysClose FnTimerInit FnTimerSetCallback FnTimerStart FnTimerCancel FnGetFuncIp FnGetAttachCookie FnTaskPtRegs FnGetBranchSnapshot FnTraceVprintk FnSkcToUnixSock FnKallsymsLookupName FnFindVma FnLoop FnStrncmp FnGetFuncArg FnGetFuncRet FnGetFuncArgCnt FnGetRetval FnSetRetval FnXdpGetBuffLen FnXdpLoadBytes FnXdpStoreBytes FnCopyFromUserTask FnSkbSetTstamp FnImaFileHash FnKptrXchg FnMapLookupPercpuElem FnSkcToMptcpSock FnDynptrFromMem FnRingbufReserveDynptr FnRingbufSubmitDynptr FnRingbufDiscardDynptr FnDynptrRead FnDynptrWrite FnDynptrData FnTcpRawGenSyncookieIpv4 FnTcpRawGenSyncookieIpv6 FnTcpRawCheckSyncookieIpv4 FnTcpRawCheckSyncookieIpv6 FnKtimeGetTaiNs FnUserRingbufDrain FnCgrpStorageGet FnCgrpStorageDelete maxBuiltinFunc ) // Call emits a function call. func (fn BuiltinFunc) Call() Instruction { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Call), Constant: int64(fn), } } golang-github-cilium-ebpf-0.11.0/asm/func_string.go000066400000000000000000000251201456504511400221470ustar00rootroot00000000000000// Code generated by "stringer -output func_string.go -type=BuiltinFunc"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[FnUnspec-0] _ = x[FnMapLookupElem-1] _ = x[FnMapUpdateElem-2] _ = x[FnMapDeleteElem-3] _ = x[FnProbeRead-4] _ = x[FnKtimeGetNs-5] _ = x[FnTracePrintk-6] _ = x[FnGetPrandomU32-7] _ = x[FnGetSmpProcessorId-8] _ = x[FnSkbStoreBytes-9] _ = x[FnL3CsumReplace-10] _ = x[FnL4CsumReplace-11] _ = x[FnTailCall-12] _ = x[FnCloneRedirect-13] _ = x[FnGetCurrentPidTgid-14] _ = x[FnGetCurrentUidGid-15] _ = x[FnGetCurrentComm-16] _ = x[FnGetCgroupClassid-17] _ = x[FnSkbVlanPush-18] _ = x[FnSkbVlanPop-19] _ = x[FnSkbGetTunnelKey-20] _ = x[FnSkbSetTunnelKey-21] _ = x[FnPerfEventRead-22] _ = x[FnRedirect-23] _ = x[FnGetRouteRealm-24] _ = x[FnPerfEventOutput-25] _ = x[FnSkbLoadBytes-26] _ = x[FnGetStackid-27] _ = x[FnCsumDiff-28] _ = x[FnSkbGetTunnelOpt-29] _ = x[FnSkbSetTunnelOpt-30] _ = x[FnSkbChangeProto-31] _ = x[FnSkbChangeType-32] _ = x[FnSkbUnderCgroup-33] _ = x[FnGetHashRecalc-34] _ = x[FnGetCurrentTask-35] _ = x[FnProbeWriteUser-36] _ = x[FnCurrentTaskUnderCgroup-37] _ = x[FnSkbChangeTail-38] _ = x[FnSkbPullData-39] _ = x[FnCsumUpdate-40] _ = x[FnSetHashInvalid-41] _ = x[FnGetNumaNodeId-42] _ = x[FnSkbChangeHead-43] _ = x[FnXdpAdjustHead-44] _ = x[FnProbeReadStr-45] _ = x[FnGetSocketCookie-46] _ = x[FnGetSocketUid-47] _ = x[FnSetHash-48] _ = x[FnSetsockopt-49] _ = x[FnSkbAdjustRoom-50] _ = x[FnRedirectMap-51] _ = x[FnSkRedirectMap-52] _ = x[FnSockMapUpdate-53] _ = x[FnXdpAdjustMeta-54] _ = x[FnPerfEventReadValue-55] _ = x[FnPerfProgReadValue-56] _ = x[FnGetsockopt-57] _ = x[FnOverrideReturn-58] _ = x[FnSockOpsCbFlagsSet-59] _ = x[FnMsgRedirectMap-60] _ = x[FnMsgApplyBytes-61] _ = x[FnMsgCorkBytes-62] _ = x[FnMsgPullData-63] _ = x[FnBind-64] _ = x[FnXdpAdjustTail-65] _ = x[FnSkbGetXfrmState-66] _ = x[FnGetStack-67] _ = x[FnSkbLoadBytesRelative-68] _ = x[FnFibLookup-69] _ = x[FnSockHashUpdate-70] _ = x[FnMsgRedirectHash-71] _ = x[FnSkRedirectHash-72] _ = x[FnLwtPushEncap-73] _ = x[FnLwtSeg6StoreBytes-74] _ = x[FnLwtSeg6AdjustSrh-75] _ = x[FnLwtSeg6Action-76] _ = x[FnRcRepeat-77] _ = x[FnRcKeydown-78] _ = x[FnSkbCgroupId-79] _ = x[FnGetCurrentCgroupId-80] _ = x[FnGetLocalStorage-81] _ = x[FnSkSelectReuseport-82] _ = x[FnSkbAncestorCgroupId-83] _ = x[FnSkLookupTcp-84] _ = x[FnSkLookupUdp-85] _ = x[FnSkRelease-86] _ = x[FnMapPushElem-87] _ = x[FnMapPopElem-88] _ = x[FnMapPeekElem-89] _ = x[FnMsgPushData-90] _ = x[FnMsgPopData-91] _ = x[FnRcPointerRel-92] _ = x[FnSpinLock-93] _ = x[FnSpinUnlock-94] _ = x[FnSkFullsock-95] _ = x[FnTcpSock-96] _ = x[FnSkbEcnSetCe-97] _ = x[FnGetListenerSock-98] _ = x[FnSkcLookupTcp-99] _ = x[FnTcpCheckSyncookie-100] _ = x[FnSysctlGetName-101] _ = x[FnSysctlGetCurrentValue-102] _ = x[FnSysctlGetNewValue-103] _ = x[FnSysctlSetNewValue-104] _ = x[FnStrtol-105] _ = x[FnStrtoul-106] _ = x[FnSkStorageGet-107] _ = x[FnSkStorageDelete-108] _ = x[FnSendSignal-109] _ = x[FnTcpGenSyncookie-110] _ = x[FnSkbOutput-111] _ = x[FnProbeReadUser-112] _ = x[FnProbeReadKernel-113] _ = x[FnProbeReadUserStr-114] _ = x[FnProbeReadKernelStr-115] _ = x[FnTcpSendAck-116] _ = x[FnSendSignalThread-117] _ = x[FnJiffies64-118] _ = x[FnReadBranchRecords-119] _ = x[FnGetNsCurrentPidTgid-120] _ = x[FnXdpOutput-121] _ = x[FnGetNetnsCookie-122] _ = x[FnGetCurrentAncestorCgroupId-123] _ = x[FnSkAssign-124] _ = x[FnKtimeGetBootNs-125] _ = x[FnSeqPrintf-126] _ = x[FnSeqWrite-127] _ = x[FnSkCgroupId-128] _ = x[FnSkAncestorCgroupId-129] _ = x[FnRingbufOutput-130] _ = x[FnRingbufReserve-131] _ = x[FnRingbufSubmit-132] _ = x[FnRingbufDiscard-133] _ = x[FnRingbufQuery-134] _ = x[FnCsumLevel-135] _ = x[FnSkcToTcp6Sock-136] _ = x[FnSkcToTcpSock-137] _ = x[FnSkcToTcpTimewaitSock-138] _ = x[FnSkcToTcpRequestSock-139] _ = x[FnSkcToUdp6Sock-140] _ = x[FnGetTaskStack-141] _ = x[FnLoadHdrOpt-142] _ = x[FnStoreHdrOpt-143] _ = x[FnReserveHdrOpt-144] _ = x[FnInodeStorageGet-145] _ = x[FnInodeStorageDelete-146] _ = x[FnDPath-147] _ = x[FnCopyFromUser-148] _ = x[FnSnprintfBtf-149] _ = x[FnSeqPrintfBtf-150] _ = x[FnSkbCgroupClassid-151] _ = x[FnRedirectNeigh-152] _ = x[FnPerCpuPtr-153] _ = x[FnThisCpuPtr-154] _ = x[FnRedirectPeer-155] _ = x[FnTaskStorageGet-156] _ = x[FnTaskStorageDelete-157] _ = x[FnGetCurrentTaskBtf-158] _ = x[FnBprmOptsSet-159] _ = x[FnKtimeGetCoarseNs-160] _ = x[FnImaInodeHash-161] _ = x[FnSockFromFile-162] _ = x[FnCheckMtu-163] _ = x[FnForEachMapElem-164] _ = x[FnSnprintf-165] _ = x[FnSysBpf-166] _ = x[FnBtfFindByNameKind-167] _ = x[FnSysClose-168] _ = x[FnTimerInit-169] _ = x[FnTimerSetCallback-170] _ = x[FnTimerStart-171] _ = x[FnTimerCancel-172] _ = x[FnGetFuncIp-173] _ = x[FnGetAttachCookie-174] _ = x[FnTaskPtRegs-175] _ = x[FnGetBranchSnapshot-176] _ = x[FnTraceVprintk-177] _ = x[FnSkcToUnixSock-178] _ = x[FnKallsymsLookupName-179] _ = x[FnFindVma-180] _ = x[FnLoop-181] _ = x[FnStrncmp-182] _ = x[FnGetFuncArg-183] _ = x[FnGetFuncRet-184] _ = x[FnGetFuncArgCnt-185] _ = x[FnGetRetval-186] _ = x[FnSetRetval-187] _ = x[FnXdpGetBuffLen-188] _ = x[FnXdpLoadBytes-189] _ = x[FnXdpStoreBytes-190] _ = x[FnCopyFromUserTask-191] _ = x[FnSkbSetTstamp-192] _ = x[FnImaFileHash-193] _ = x[FnKptrXchg-194] _ = x[FnMapLookupPercpuElem-195] _ = x[FnSkcToMptcpSock-196] _ = x[FnDynptrFromMem-197] _ = x[FnRingbufReserveDynptr-198] _ = x[FnRingbufSubmitDynptr-199] _ = x[FnRingbufDiscardDynptr-200] _ = x[FnDynptrRead-201] _ = x[FnDynptrWrite-202] _ = x[FnDynptrData-203] _ = x[FnTcpRawGenSyncookieIpv4-204] _ = x[FnTcpRawGenSyncookieIpv6-205] _ = x[FnTcpRawCheckSyncookieIpv4-206] _ = x[FnTcpRawCheckSyncookieIpv6-207] _ = x[FnKtimeGetTaiNs-208] _ = x[FnUserRingbufDrain-209] _ = x[FnCgrpStorageGet-210] _ = x[FnCgrpStorageDelete-211] _ = x[maxBuiltinFunc-212] } const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDeletemaxBuiltinFunc" var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165, 3179} func (i BuiltinFunc) String() string { if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { return "BuiltinFunc(" + strconv.FormatInt(int64(i), 10) + ")" } return _BuiltinFunc_name[_BuiltinFunc_index[i]:_BuiltinFunc_index[i+1]] } golang-github-cilium-ebpf-0.11.0/asm/instruction.go000066400000000000000000000546071456504511400222230ustar00rootroot00000000000000package asm import ( "crypto/sha1" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "math" "sort" "strings" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // InstructionSize is the size of a BPF instruction in bytes const InstructionSize = 8 // RawInstructionOffset is an offset in units of raw BPF instructions. type RawInstructionOffset uint64 var ErrUnreferencedSymbol = errors.New("unreferenced symbol") var ErrUnsatisfiedMapReference = errors.New("unsatisfied map reference") var ErrUnsatisfiedProgramReference = errors.New("unsatisfied program reference") // Bytes returns the offset of an instruction in bytes. func (rio RawInstructionOffset) Bytes() uint64 { return uint64(rio) * InstructionSize } // Instruction is a single eBPF instruction. type Instruction struct { OpCode OpCode Dst Register Src Register Offset int16 Constant int64 // Metadata contains optional metadata about this instruction. Metadata Metadata } // Unmarshal decodes a BPF instruction. func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) { data := make([]byte, InstructionSize) if _, err := io.ReadFull(r, data); err != nil { return 0, err } ins.OpCode = OpCode(data[0]) regs := data[1] switch bo { case binary.LittleEndian: ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4) case binary.BigEndian: ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf) } ins.Offset = int16(bo.Uint16(data[2:4])) // Convert to int32 before widening to int64 // to ensure the signed bit is carried over. ins.Constant = int64(int32(bo.Uint32(data[4:8]))) if !ins.OpCode.IsDWordLoad() { return InstructionSize, nil } // Pull another instruction from the stream to retrieve the second // half of the 64-bit immediate value. if _, err := io.ReadFull(r, data); err != nil { // No Wrap, to avoid io.EOF clash return 0, errors.New("64bit immediate is missing second half") } // Require that all fields other than the value are zero. if bo.Uint32(data[0:4]) != 0 { return 0, errors.New("64bit immediate has non-zero fields") } cons1 := uint32(ins.Constant) cons2 := int32(bo.Uint32(data[4:8])) ins.Constant = int64(cons2)<<32 | int64(cons1) return 2 * InstructionSize, nil } // Marshal encodes a BPF instruction. func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { if ins.OpCode == InvalidOpCode { return 0, errors.New("invalid opcode") } isDWordLoad := ins.OpCode.IsDWordLoad() cons := int32(ins.Constant) if isDWordLoad { // Encode least significant 32bit first for 64bit operations. cons = int32(uint32(ins.Constant)) } regs, err := newBPFRegisters(ins.Dst, ins.Src, bo) if err != nil { return 0, fmt.Errorf("can't marshal registers: %s", err) } data := make([]byte, InstructionSize) data[0] = byte(ins.OpCode) data[1] = byte(regs) bo.PutUint16(data[2:4], uint16(ins.Offset)) bo.PutUint32(data[4:8], uint32(cons)) if _, err := w.Write(data); err != nil { return 0, err } if !isDWordLoad { return InstructionSize, nil } // The first half of the second part of a double-wide instruction // must be zero. The second half carries the value. bo.PutUint32(data[0:4], 0) bo.PutUint32(data[4:8], uint32(ins.Constant>>32)) if _, err := w.Write(data); err != nil { return 0, err } return 2 * InstructionSize, nil } // AssociateMap associates a Map with this Instruction. // // Implicitly clears the Instruction's Reference field. // // Returns an error if the Instruction is not a map load. func (ins *Instruction) AssociateMap(m FDer) error { if !ins.IsLoadFromMap() { return errors.New("not a load from a map") } ins.Metadata.Set(referenceMeta{}, nil) ins.Metadata.Set(mapMeta{}, m) return nil } // RewriteMapPtr changes an instruction to use a new map fd. // // Returns an error if the instruction doesn't load a map. // // Deprecated: use AssociateMap instead. If you cannot provide a Map, // wrap an fd in a type implementing FDer. func (ins *Instruction) RewriteMapPtr(fd int) error { if !ins.IsLoadFromMap() { return errors.New("not a load from a map") } ins.encodeMapFD(fd) return nil } func (ins *Instruction) encodeMapFD(fd int) { // Preserve the offset value for direct map loads. offset := uint64(ins.Constant) & (math.MaxUint32 << 32) rawFd := uint64(uint32(fd)) ins.Constant = int64(offset | rawFd) } // MapPtr returns the map fd for this instruction. // // The result is undefined if the instruction is not a load from a map, // see IsLoadFromMap. // // Deprecated: use Map() instead. func (ins *Instruction) MapPtr() int { // If there is a map associated with the instruction, return its FD. if fd := ins.Metadata.Get(mapMeta{}); fd != nil { return fd.(FDer).FD() } // Fall back to the fd stored in the Constant field return ins.mapFd() } // mapFd returns the map file descriptor stored in the 32 least significant // bits of ins' Constant field. func (ins *Instruction) mapFd() int { return int(int32(ins.Constant)) } // RewriteMapOffset changes the offset of a direct load from a map. // // Returns an error if the instruction is not a direct load. func (ins *Instruction) RewriteMapOffset(offset uint32) error { if !ins.OpCode.IsDWordLoad() { return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) } if ins.Src != PseudoMapValue { return errors.New("not a direct load from a map") } fd := uint64(ins.Constant) & math.MaxUint32 ins.Constant = int64(uint64(offset)<<32 | fd) return nil } func (ins *Instruction) mapOffset() uint32 { return uint32(uint64(ins.Constant) >> 32) } // IsLoadFromMap returns true if the instruction loads from a map. // // This covers both loading the map pointer and direct map value loads. func (ins *Instruction) IsLoadFromMap() bool { return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue) } // IsFunctionCall returns true if the instruction calls another BPF function. // // This is not the same thing as a BPF helper call. func (ins *Instruction) IsFunctionCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall } // IsKfuncCall returns true if the instruction calls a kfunc. // // This is not the same thing as a BPF helper call. func (ins *Instruction) IsKfuncCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == PseudoKfuncCall } // IsLoadOfFunctionPointer returns true if the instruction loads a function pointer. func (ins *Instruction) IsLoadOfFunctionPointer() bool { return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc } // IsFunctionReference returns true if the instruction references another BPF // function, either by invoking a Call jump operation or by loading a function // pointer. func (ins *Instruction) IsFunctionReference() bool { return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer() } // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call. func (ins *Instruction) IsBuiltinCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0 } // IsConstantLoad returns true if the instruction loads a constant of the // given size. func (ins *Instruction) IsConstantLoad(size Size) bool { return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0 } // Format implements fmt.Formatter. func (ins Instruction) Format(f fmt.State, c rune) { if c != 'v' { fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c) return } op := ins.OpCode if op == InvalidOpCode { fmt.Fprint(f, "INVALID") return } // Omit trailing space for Exit if op.JumpOp() == Exit { fmt.Fprint(f, op) return } if ins.IsLoadFromMap() { fd := ins.mapFd() m := ins.Map() switch ins.Src { case PseudoMapFD: if m != nil { fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m) } else { fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) } case PseudoMapValue: if m != nil { fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset()) } else { fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset()) } } goto ref } fmt.Fprintf(f, "%v ", op) switch cls := op.Class(); { case cls.isLoadOrStore(): switch op.Mode() { case ImmMode: fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant) case AbsMode: fmt.Fprintf(f, "imm: %d", ins.Constant) case IndMode: fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant) case MemMode: fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant) case XAddMode: fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src) } case cls.IsALU(): fmt.Fprintf(f, "dst: %s ", ins.Dst) if op.ALUOp() == Swap || op.Source() == ImmSource { fmt.Fprintf(f, "imm: %d", ins.Constant) } else { fmt.Fprintf(f, "src: %s", ins.Src) } case cls.IsJump(): switch jop := op.JumpOp(); jop { case Call: switch ins.Src { case PseudoCall: // bpf-to-bpf call fmt.Fprint(f, ins.Constant) case PseudoKfuncCall: // kfunc call fmt.Fprintf(f, "Kfunc(%d)", ins.Constant) default: fmt.Fprint(f, BuiltinFunc(ins.Constant)) } default: fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) if op.Source() == ImmSource { fmt.Fprintf(f, "imm: %d", ins.Constant) } else { fmt.Fprintf(f, "src: %s", ins.Src) } } } ref: if ins.Reference() != "" { fmt.Fprintf(f, " <%s>", ins.Reference()) } } func (ins Instruction) equal(other Instruction) bool { return ins.OpCode == other.OpCode && ins.Dst == other.Dst && ins.Src == other.Src && ins.Offset == other.Offset && ins.Constant == other.Constant } // Size returns the amount of bytes ins would occupy in binary form. func (ins Instruction) Size() uint64 { return uint64(InstructionSize * ins.OpCode.rawInstructions()) } // WithMetadata sets the given Metadata on the Instruction. e.g. to copy // Metadata from another Instruction when replacing it. func (ins Instruction) WithMetadata(meta Metadata) Instruction { ins.Metadata = meta return ins } type symbolMeta struct{} // WithSymbol marks the Instruction as a Symbol, which other Instructions // can point to using corresponding calls to WithReference. func (ins Instruction) WithSymbol(name string) Instruction { ins.Metadata.Set(symbolMeta{}, name) return ins } // Sym creates a symbol. // // Deprecated: use WithSymbol instead. func (ins Instruction) Sym(name string) Instruction { return ins.WithSymbol(name) } // Symbol returns the value ins has been marked with using WithSymbol, // otherwise returns an empty string. A symbol is often an Instruction // at the start of a function body. func (ins Instruction) Symbol() string { sym, _ := ins.Metadata.Get(symbolMeta{}).(string) return sym } type referenceMeta struct{} // WithReference makes ins reference another Symbol or map by name. func (ins Instruction) WithReference(ref string) Instruction { ins.Metadata.Set(referenceMeta{}, ref) return ins } // Reference returns the Symbol or map name referenced by ins, if any. func (ins Instruction) Reference() string { ref, _ := ins.Metadata.Get(referenceMeta{}).(string) return ref } type mapMeta struct{} // Map returns the Map referenced by ins, if any. // An Instruction will contain a Map if e.g. it references an existing, // pinned map that was opened during ELF loading. func (ins Instruction) Map() FDer { fd, _ := ins.Metadata.Get(mapMeta{}).(FDer) return fd } type sourceMeta struct{} // WithSource adds source information about the Instruction. func (ins Instruction) WithSource(src fmt.Stringer) Instruction { ins.Metadata.Set(sourceMeta{}, src) return ins } // Source returns source information about the Instruction. The field is // present when the compiler emits BTF line info about the Instruction and // usually contains the line of source code responsible for it. func (ins Instruction) Source() fmt.Stringer { str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer) return str } // A Comment can be passed to Instruction.WithSource to add a comment // to an instruction. type Comment string func (s Comment) String() string { return string(s) } // FDer represents a resource tied to an underlying file descriptor. // Used as a stand-in for e.g. ebpf.Map since that type cannot be // imported here and FD() is the only method we rely on. type FDer interface { FD() int } // Instructions is an eBPF program. type Instructions []Instruction // Unmarshal unmarshals an Instructions from a binary instruction stream. // All instructions in insns are replaced by instructions decoded from r. func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error { if len(*insns) > 0 { *insns = nil } var offset uint64 for { var ins Instruction n, err := ins.Unmarshal(r, bo) if errors.Is(err, io.EOF) { break } if err != nil { return fmt.Errorf("offset %d: %w", offset, err) } *insns = append(*insns, ins) offset += n } return nil } // Name returns the name of the function insns belongs to, if any. func (insns Instructions) Name() string { if len(insns) == 0 { return "" } return insns[0].Symbol() } func (insns Instructions) String() string { return fmt.Sprint(insns) } // Size returns the amount of bytes insns would occupy in binary form. func (insns Instructions) Size() uint64 { var sum uint64 for _, ins := range insns { sum += ins.Size() } return sum } // AssociateMap updates all Instructions that Reference the given symbol // to point to an existing Map m instead. // // Returns ErrUnreferencedSymbol error if no references to symbol are found // in insns. If symbol is anything else than the symbol name of map (e.g. // a bpf2bpf subprogram), an error is returned. func (insns Instructions) AssociateMap(symbol string, m FDer) error { if symbol == "" { return errors.New("empty symbol") } var found bool for i := range insns { ins := &insns[i] if ins.Reference() != symbol { continue } if err := ins.AssociateMap(m); err != nil { return err } found = true } if !found { return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol) } return nil } // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd. // // Returns ErrUnreferencedSymbol if the symbol isn't used. // // Deprecated: use AssociateMap instead. func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { if symbol == "" { return errors.New("empty symbol") } var found bool for i := range insns { ins := &insns[i] if ins.Reference() != symbol { continue } if !ins.IsLoadFromMap() { return errors.New("not a load from a map") } ins.encodeMapFD(fd) found = true } if !found { return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol) } return nil } // SymbolOffsets returns the set of symbols and their offset in // the instructions. func (insns Instructions) SymbolOffsets() (map[string]int, error) { offsets := make(map[string]int) for i, ins := range insns { if ins.Symbol() == "" { continue } if _, ok := offsets[ins.Symbol()]; ok { return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol()) } offsets[ins.Symbol()] = i } return offsets, nil } // FunctionReferences returns a set of symbol names these Instructions make // bpf-to-bpf calls to. func (insns Instructions) FunctionReferences() []string { calls := make(map[string]struct{}) for _, ins := range insns { if ins.Constant != -1 { // BPF-to-BPF calls have -1 constants. continue } if ins.Reference() == "" { continue } if !ins.IsFunctionReference() { continue } calls[ins.Reference()] = struct{}{} } result := make([]string, 0, len(calls)) for call := range calls { result = append(result, call) } sort.Strings(result) return result } // ReferenceOffsets returns the set of references and their offset in // the instructions. func (insns Instructions) ReferenceOffsets() map[string][]int { offsets := make(map[string][]int) for i, ins := range insns { if ins.Reference() == "" { continue } offsets[ins.Reference()] = append(offsets[ins.Reference()], i) } return offsets } // Format implements fmt.Formatter. // // You can control indentation of symbols by // specifying a width. Setting a precision controls the indentation of // instructions. // The default character is a tab, which can be overridden by specifying // the ' ' space flag. func (insns Instructions) Format(f fmt.State, c rune) { if c != 's' && c != 'v' { fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c) return } // Precision is better in this case, because it allows // specifying 0 padding easily. padding, ok := f.Precision() if !ok { padding = 1 } indent := strings.Repeat("\t", padding) if f.Flag(' ') { indent = strings.Repeat(" ", padding) } symPadding, ok := f.Width() if !ok { symPadding = padding - 1 } if symPadding < 0 { symPadding = 0 } symIndent := strings.Repeat("\t", symPadding) if f.Flag(' ') { symIndent = strings.Repeat(" ", symPadding) } // Guess how many digits we need at most, by assuming that all instructions // are double wide. highestOffset := len(insns) * 2 offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset)))) iter := insns.Iterate() for iter.Next() { if iter.Ins.Symbol() != "" { fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol()) } if src := iter.Ins.Source(); src != nil { line := strings.TrimSpace(src.String()) if line != "" { fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line) } } fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) } } // Marshal encodes a BPF program into the kernel format. // // insns may be modified if there are unresolved jumps or bpf2bpf calls. // // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction // without a matching Symbol Instruction within insns. func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { if err := insns.encodeFunctionReferences(); err != nil { return err } if err := insns.encodeMapPointers(); err != nil { return err } for i, ins := range insns { if _, err := ins.Marshal(w, bo); err != nil { return fmt.Errorf("instruction %d: %w", i, err) } } return nil } // Tag calculates the kernel tag for a series of instructions. // // It mirrors bpf_prog_calc_tag in the kernel and so can be compared // to ProgramInfo.Tag to figure out whether a loaded program matches // certain instructions. func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) { h := sha1.New() for i, ins := range insns { if ins.IsLoadFromMap() { ins.Constant = 0 } _, err := ins.Marshal(h, bo) if err != nil { return "", fmt.Errorf("instruction %d: %w", i, err) } } return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil } // encodeFunctionReferences populates the Offset (or Constant, depending on // the instruction type) field of instructions with a Reference field to point // to the offset of the corresponding instruction with a matching Symbol field. // // Only Reference Instructions that are either jumps or BPF function references // (calls or function pointer loads) are populated. // // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction // without at least one corresponding Symbol Instruction within insns. func (insns Instructions) encodeFunctionReferences() error { // Index the offsets of instructions tagged as a symbol. symbolOffsets := make(map[string]RawInstructionOffset) iter := insns.Iterate() for iter.Next() { ins := iter.Ins if ins.Symbol() == "" { continue } if _, ok := symbolOffsets[ins.Symbol()]; ok { return fmt.Errorf("duplicate symbol %s", ins.Symbol()) } symbolOffsets[ins.Symbol()] = iter.Offset } // Find all instructions tagged as references to other symbols. // Depending on the instruction type, populate their constant or offset // fields to point to the symbol they refer to within the insn stream. iter = insns.Iterate() for iter.Next() { i := iter.Index offset := iter.Offset ins := iter.Ins if ins.Reference() == "" { continue } switch { case ins.IsFunctionReference() && ins.Constant == -1: symOffset, ok := symbolOffsets[ins.Reference()] if !ok { return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Constant = int64(symOffset - offset - 1) case ins.OpCode.Class().IsJump() && ins.Offset == -1: symOffset, ok := symbolOffsets[ins.Reference()] if !ok { return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Offset = int16(symOffset - offset - 1) } } return nil } // encodeMapPointers finds all Map Instructions and encodes their FDs // into their Constant fields. func (insns Instructions) encodeMapPointers() error { iter := insns.Iterate() for iter.Next() { ins := iter.Ins if !ins.IsLoadFromMap() { continue } m := ins.Map() if m == nil { continue } fd := m.FD() if fd < 0 { return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd) } ins.encodeMapFD(m.FD()) } return nil } // Iterate allows iterating a BPF program while keeping track of // various offsets. // // Modifying the instruction slice will lead to undefined behaviour. func (insns Instructions) Iterate() *InstructionIterator { return &InstructionIterator{insns: insns} } // InstructionIterator iterates over a BPF program. type InstructionIterator struct { insns Instructions // The instruction in question. Ins *Instruction // The index of the instruction in the original instruction slice. Index int // The offset of the instruction in raw BPF instructions. This accounts // for double-wide instructions. Offset RawInstructionOffset } // Next returns true as long as there are any instructions remaining. func (iter *InstructionIterator) Next() bool { if len(iter.insns) == 0 { return false } if iter.Ins != nil { iter.Index++ iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions()) } iter.Ins = &iter.insns[0] iter.insns = iter.insns[1:] return true } type bpfRegisters uint8 func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) { switch bo { case binary.LittleEndian: return bpfRegisters((src << 4) | (dst & 0xF)), nil case binary.BigEndian: return bpfRegisters((dst << 4) | (src & 0xF)), nil default: return 0, fmt.Errorf("unrecognized ByteOrder %T", bo) } } // IsUnreferencedSymbol returns true if err was caused by // an unreferenced symbol. // // Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol). func IsUnreferencedSymbol(err error) bool { return errors.Is(err, ErrUnreferencedSymbol) } golang-github-cilium-ebpf-0.11.0/asm/instruction_test.go000066400000000000000000000202531456504511400232500ustar00rootroot00000000000000package asm import ( "bytes" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "math" "testing" qt "github.com/frankban/quicktest" ) var test64bitImmProg = []byte{ // r0 = math.MinInt32 - 1 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, } func TestRead64bitImmediate(t *testing.T) { var ins Instruction n, err := ins.Unmarshal(bytes.NewReader(test64bitImmProg), binary.LittleEndian) if err != nil { t.Fatal(err) } if want := uint64(InstructionSize * 2); n != want { t.Errorf("Expected %d bytes to be read, got %d", want, n) } if c := ins.Constant; c != math.MinInt32-1 { t.Errorf("Expected immediate to be %v, got %v", int64(math.MinInt32)-1, c) } } func BenchmarkRead64bitImmediate(b *testing.B) { r := &bytes.Reader{} for i := 0; i < b.N; i++ { r.Reset(test64bitImmProg) var ins Instruction if _, err := ins.Unmarshal(r, binary.LittleEndian); err != nil { b.Fatal(err) } } } func TestWrite64bitImmediate(t *testing.T) { insns := Instructions{ LoadImm(R0, math.MinInt32-1, DWord), } var buf bytes.Buffer if err := insns.Marshal(&buf, binary.LittleEndian); err != nil { t.Fatal(err) } if prog := buf.Bytes(); !bytes.Equal(prog, test64bitImmProg) { t.Errorf("Marshalled program does not match:\n%s", hex.Dump(prog)) } } func BenchmarkWrite64BitImmediate(b *testing.B) { ins := LoadImm(R0, math.MinInt32-1, DWord) var buf bytes.Buffer for i := 0; i < b.N; i++ { buf.Reset() if _, err := ins.Marshal(&buf, binary.LittleEndian); err != nil { b.Fatal(err) } } } func TestUnmarshalInstructions(t *testing.T) { r := bytes.NewReader(test64bitImmProg) var insns Instructions if err := insns.Unmarshal(r, binary.LittleEndian); err != nil { t.Fatal(err) } // Unmarshaling into the same Instructions multiple times replaces // the instruction stream. r.Reset(test64bitImmProg) if err := insns.Unmarshal(r, binary.LittleEndian); err != nil { t.Fatal(err) } if len(insns) != 1 { t.Fatalf("Expected one instruction, got %d", len(insns)) } } func TestSignedJump(t *testing.T) { insns := Instructions{ JSGT.Imm(R0, -1, "foo"), } insns[0].Offset = 1 err := insns.Marshal(io.Discard, binary.LittleEndian) if err != nil { t.Error("Can't marshal signed jump:", err) } } func TestInstructionRewriteMapConstant(t *testing.T) { ins := LoadMapValue(R0, 123, 321) qt.Assert(t, ins.MapPtr(), qt.Equals, 123) qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(321)) qt.Assert(t, ins.RewriteMapPtr(-1), qt.IsNil) qt.Assert(t, ins.MapPtr(), qt.Equals, -1) qt.Assert(t, ins.RewriteMapPtr(1), qt.IsNil) qt.Assert(t, ins.MapPtr(), qt.Equals, 1) // mapOffset should be unchanged after rewriting the pointer. qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(321)) qt.Assert(t, ins.RewriteMapOffset(123), qt.IsNil) qt.Assert(t, ins.mapOffset(), qt.Equals, uint32(123)) // MapPtr should be unchanged. qt.Assert(t, ins.MapPtr(), qt.Equals, 1) ins = Mov.Imm(R1, 32) if err := ins.RewriteMapPtr(1); err == nil { t.Error("RewriteMapPtr rewriting bogus instruction") } if err := ins.RewriteMapOffset(1); err == nil { t.Error("RewriteMapOffset rewriting bogus instruction") } } func TestInstructionLoadMapValue(t *testing.T) { ins := LoadMapValue(R0, 1, 123) if !ins.IsLoadFromMap() { t.Error("isLoadFromMap returns false") } if fd := ins.mapFd(); fd != 1 { t.Error("Expected map fd to be 1, got", fd) } if off := ins.mapOffset(); off != 123 { t.Fatal("Expected map offset to be 123 after changin the pointer, got", off) } } func TestInstructionsRewriteMapPtr(t *testing.T) { insns := Instructions{ LoadMapPtr(R1, 0).WithReference("good"), Return(), } if err := insns.RewriteMapPtr("good", 1); err != nil { t.Fatal(err) } if insns[0].Constant != 1 { t.Error("Constant should be 1, have", insns[0].Constant) } if err := insns.RewriteMapPtr("good", 2); err != nil { t.Fatal(err) } if insns[0].Constant != 2 { t.Error("Constant should be 2, have", insns[0].Constant) } if err := insns.RewriteMapPtr("bad", 1); !errors.Is(err, ErrUnreferencedSymbol) { t.Error("Rewriting unreferenced map doesn't return appropriate error") } } func TestInstructionWithMetadata(t *testing.T) { ins := LoadImm(R0, 123, DWord).WithSymbol("abc") ins2 := LoadImm(R0, 567, DWord).WithMetadata(ins.Metadata) if want, got := "abc", ins2.Symbol(); want != got { t.Fatalf("unexpected Symbol value on ins2: want: %s, got: %s", want, got) } if want, got := ins.Metadata, ins2.Metadata; want != got { t.Fatal("expected ins and isn2 Metadata to match") } } // You can use format flags to change the way an eBPF // program is stringified. func ExampleInstructions_Format() { insns := Instructions{ FnMapLookupElem.Call().WithSymbol("my_func").WithSource(Comment("bpf_map_lookup_elem()")), LoadImm(R0, 42, DWord).WithSource(Comment("abc = 42")), Return(), } fmt.Println("Default format:") fmt.Printf("%v\n", insns) fmt.Println("Don't indent instructions:") fmt.Printf("%.0v\n", insns) fmt.Println("Indent using spaces:") fmt.Printf("% v\n", insns) fmt.Println("Control symbol indentation:") fmt.Printf("%2v\n", insns) // Output: Default format: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Don't indent instructions: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Indent using spaces: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Control symbol indentation: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit } func TestReadSrcDst(t *testing.T) { testSrcDstProg := []byte{ // on little-endian: r0 = r1 // on big-endian: be: r1 = r0 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } testcases := []struct { bo binary.ByteOrder dst, src Register }{ {binary.BigEndian, R1, R0}, {binary.LittleEndian, R0, R1}, } for _, tc := range testcases { t.Run(tc.bo.String(), func(t *testing.T) { var ins Instruction _, err := ins.Unmarshal(bytes.NewReader(testSrcDstProg), tc.bo) if err != nil { t.Fatal(err) } if ins.Dst != tc.dst { t.Errorf("Expected destination to be %v, got %v", tc.dst, ins.Dst) } if ins.Src != tc.src { t.Errorf("Expected source to be %v, got %v", tc.src, ins.Src) } }) } } func TestInstructionIterator(t *testing.T) { insns := Instructions{ LoadImm(R0, 0, Word), LoadImm(R0, 0, DWord), Return(), } offsets := []RawInstructionOffset{0, 1, 3} iter := insns.Iterate() for i := 0; i < len(insns); i++ { if !iter.Next() { t.Fatalf("Expected %dth call to Next to return true", i) } if iter.Ins == nil { t.Errorf("Expected iter.Ins to be non-nil") } if iter.Index != i { t.Errorf("Expected iter.Index to be %d, got %d", i, iter.Index) } if iter.Offset != offsets[i] { t.Errorf("Expected iter.Offset to be %d, got %d", offsets[i], iter.Offset) } } } func TestMetadataCopyOnWrite(t *testing.T) { c := qt.New(t) // Setting metadata should copy Instruction and modify the metadata pointer // of the new object without touching the old Instruction. // Reference ins := Ja.Label("my_func") ins2 := ins.WithReference("my_func2") c.Assert(ins.Reference(), qt.Equals, "my_func", qt.Commentf("WithReference updated ins")) c.Assert(ins2.Reference(), qt.Equals, "my_func2", qt.Commentf("WithReference didn't update ins2")) // Symbol ins = Ja.Label("").WithSymbol("my_sym") ins2 = ins.WithSymbol("my_sym2") c.Assert(ins.Symbol(), qt.Equals, "my_sym", qt.Commentf("WithSymbol updated ins")) c.Assert(ins2.Symbol(), qt.Equals, "my_sym2", qt.Commentf("WithSymbol didn't update ins2")) // Map ins = LoadMapPtr(R1, 0) ins2 = ins testMap := testFDer(1) c.Assert(ins2.AssociateMap(testMap), qt.IsNil, qt.Commentf("failed to associate map with ins2")) c.Assert(ins.Map(), qt.IsNil, qt.Commentf("AssociateMap updated ins")) c.Assert(ins2.Map(), qt.Equals, testMap, qt.Commentf("AssociateMap didn't update ins2")) } type testFDer int func (t testFDer) FD() int { return int(t) } golang-github-cilium-ebpf-0.11.0/asm/jump.go000066400000000000000000000065241456504511400206100ustar00rootroot00000000000000package asm //go:generate stringer -output jump_string.go -type=JumpOp // JumpOp affect control flow. // // msb lsb // +----+-+---+ // |OP |s|cls| // +----+-+---+ type JumpOp uint8 const jumpMask OpCode = aluMask const ( // InvalidJumpOp is returned by getters when invoked // on non branch OpCodes InvalidJumpOp JumpOp = 0xff // Ja jumps by offset unconditionally Ja JumpOp = 0x00 // JEq jumps by offset if r == imm JEq JumpOp = 0x10 // JGT jumps by offset if r > imm JGT JumpOp = 0x20 // JGE jumps by offset if r >= imm JGE JumpOp = 0x30 // JSet jumps by offset if r & imm JSet JumpOp = 0x40 // JNE jumps by offset if r != imm JNE JumpOp = 0x50 // JSGT jumps by offset if signed r > signed imm JSGT JumpOp = 0x60 // JSGE jumps by offset if signed r >= signed imm JSGE JumpOp = 0x70 // Call builtin or user defined function from imm Call JumpOp = 0x80 // Exit ends execution, with value in r0 Exit JumpOp = 0x90 // JLT jumps by offset if r < imm JLT JumpOp = 0xa0 // JLE jumps by offset if r <= imm JLE JumpOp = 0xb0 // JSLT jumps by offset if signed r < signed imm JSLT JumpOp = 0xc0 // JSLE jumps by offset if signed r <= signed imm JSLE JumpOp = 0xd0 ) // Return emits an exit instruction. // // Requires a return value in R0. func Return() Instruction { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Exit), } } // Op returns the OpCode for a given jump source. func (op JumpOp) Op(source Source) OpCode { return OpCode(JumpClass).SetJumpOp(op).SetSource(source) } // Imm compares 64 bit dst to 64 bit value (sign extended), and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { return Instruction{ OpCode: op.opCode(JumpClass, ImmSource), Dst: dst, Offset: -1, Constant: int64(value), }.WithReference(label) } // Imm32 compares 32 bit dst to 32 bit value, and adjusts PC by offset if the condition is fulfilled. // Requires kernel 5.1. func (op JumpOp) Imm32(dst Register, value int32, label string) Instruction { return Instruction{ OpCode: op.opCode(Jump32Class, ImmSource), Dst: dst, Offset: -1, Constant: int64(value), }.WithReference(label) } // Reg compares 64 bit dst to 64 bit src, and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Reg(dst, src Register, label string) Instruction { return Instruction{ OpCode: op.opCode(JumpClass, RegSource), Dst: dst, Src: src, Offset: -1, }.WithReference(label) } // Reg32 compares 32 bit dst to 32 bit src, and adjusts PC by offset if the condition is fulfilled. // Requires kernel 5.1. func (op JumpOp) Reg32(dst, src Register, label string) Instruction { return Instruction{ OpCode: op.opCode(Jump32Class, RegSource), Dst: dst, Src: src, Offset: -1, }.WithReference(label) } func (op JumpOp) opCode(class Class, source Source) OpCode { if op == Exit || op == Call || op == Ja { return InvalidOpCode } return OpCode(class).SetJumpOp(op).SetSource(source) } // Label adjusts PC to the address of the label. func (op JumpOp) Label(label string) Instruction { if op == Call { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Call), Src: PseudoCall, Constant: -1, }.WithReference(label) } return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(op), Offset: -1, }.WithReference(label) } golang-github-cilium-ebpf-0.11.0/asm/jump_string.go000066400000000000000000000022761456504511400221760ustar00rootroot00000000000000// Code generated by "stringer -output jump_string.go -type=JumpOp"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidJumpOp-255] _ = x[Ja-0] _ = x[JEq-16] _ = x[JGT-32] _ = x[JGE-48] _ = x[JSet-64] _ = x[JNE-80] _ = x[JSGT-96] _ = x[JSGE-112] _ = x[Call-128] _ = x[Exit-144] _ = x[JLT-160] _ = x[JLE-176] _ = x[JSLT-192] _ = x[JSLE-208] } const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp" var _JumpOp_map = map[JumpOp]string{ 0: _JumpOp_name[0:2], 16: _JumpOp_name[2:5], 32: _JumpOp_name[5:8], 48: _JumpOp_name[8:11], 64: _JumpOp_name[11:15], 80: _JumpOp_name[15:18], 96: _JumpOp_name[18:22], 112: _JumpOp_name[22:26], 128: _JumpOp_name[26:30], 144: _JumpOp_name[30:34], 160: _JumpOp_name[34:37], 176: _JumpOp_name[37:40], 192: _JumpOp_name[40:44], 208: _JumpOp_name[44:48], 255: _JumpOp_name[48:61], } func (i JumpOp) String() string { if str, ok := _JumpOp_map[i]; ok { return str } return "JumpOp(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.11.0/asm/load_store.go000066400000000000000000000114311456504511400217610ustar00rootroot00000000000000package asm //go:generate stringer -output load_store_string.go -type=Mode,Size // Mode for load and store operations // // msb lsb // +---+--+---+ // |MDE|sz|cls| // +---+--+---+ type Mode uint8 const modeMask OpCode = 0xe0 const ( // InvalidMode is returned by getters when invoked // on non load / store OpCodes InvalidMode Mode = 0xff // ImmMode - immediate value ImmMode Mode = 0x00 // AbsMode - immediate value + offset AbsMode Mode = 0x20 // IndMode - indirect (imm+src) IndMode Mode = 0x40 // MemMode - load from memory MemMode Mode = 0x60 // XAddMode - add atomically across processors. XAddMode Mode = 0xc0 ) // Size of load and store operations // // msb lsb // +---+--+---+ // |mde|SZ|cls| // +---+--+---+ type Size uint8 const sizeMask OpCode = 0x18 const ( // InvalidSize is returned by getters when invoked // on non load / store OpCodes InvalidSize Size = 0xff // DWord - double word; 64 bits DWord Size = 0x18 // Word - word; 32 bits Word Size = 0x00 // Half - half-word; 16 bits Half Size = 0x08 // Byte - byte; 8 bits Byte Size = 0x10 ) // Sizeof returns the size in bytes. func (s Size) Sizeof() int { switch s { case DWord: return 8 case Word: return 4 case Half: return 2 case Byte: return 1 default: return -1 } } // LoadMemOp returns the OpCode to load a value of given size from memory. func LoadMemOp(size Size) OpCode { return OpCode(LdXClass).SetMode(MemMode).SetSize(size) } // LoadMem emits `dst = *(size *)(src + offset)`. func LoadMem(dst, src Register, offset int16, size Size) Instruction { return Instruction{ OpCode: LoadMemOp(size), Dst: dst, Src: src, Offset: offset, } } // LoadImmOp returns the OpCode to load an immediate of given size. // // As of kernel 4.20, only DWord size is accepted. func LoadImmOp(size Size) OpCode { return OpCode(LdClass).SetMode(ImmMode).SetSize(size) } // LoadImm emits `dst = (size)value`. // // As of kernel 4.20, only DWord size is accepted. func LoadImm(dst Register, value int64, size Size) Instruction { return Instruction{ OpCode: LoadImmOp(size), Dst: dst, Constant: value, } } // LoadMapPtr stores a pointer to a map in dst. func LoadMapPtr(dst Register, fd int) Instruction { if fd < 0 { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: LoadImmOp(DWord), Dst: dst, Src: PseudoMapFD, Constant: int64(uint32(fd)), } } // LoadMapValue stores a pointer to the value at a certain offset of a map. func LoadMapValue(dst Register, fd int, offset uint32) Instruction { if fd < 0 { return Instruction{OpCode: InvalidOpCode} } fdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd)) return Instruction{ OpCode: LoadImmOp(DWord), Dst: dst, Src: PseudoMapValue, Constant: int64(fdAndOffset), } } // LoadIndOp returns the OpCode for loading a value of given size from an sk_buff. func LoadIndOp(size Size) OpCode { return OpCode(LdClass).SetMode(IndMode).SetSize(size) } // LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`. func LoadInd(dst, src Register, offset int32, size Size) Instruction { return Instruction{ OpCode: LoadIndOp(size), Dst: dst, Src: src, Constant: int64(offset), } } // LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff. func LoadAbsOp(size Size) OpCode { return OpCode(LdClass).SetMode(AbsMode).SetSize(size) } // LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`. func LoadAbs(offset int32, size Size) Instruction { return Instruction{ OpCode: LoadAbsOp(size), Dst: R0, Constant: int64(offset), } } // StoreMemOp returns the OpCode for storing a register of given size in memory. func StoreMemOp(size Size) OpCode { return OpCode(StXClass).SetMode(MemMode).SetSize(size) } // StoreMem emits `*(size *)(dst + offset) = src` func StoreMem(dst Register, offset int16, src Register, size Size) Instruction { return Instruction{ OpCode: StoreMemOp(size), Dst: dst, Src: src, Offset: offset, } } // StoreImmOp returns the OpCode for storing an immediate of given size in memory. func StoreImmOp(size Size) OpCode { return OpCode(StClass).SetMode(MemMode).SetSize(size) } // StoreImm emits `*(size *)(dst + offset) = value`. func StoreImm(dst Register, offset int16, value int64, size Size) Instruction { return Instruction{ OpCode: StoreImmOp(size), Dst: dst, Offset: offset, Constant: value, } } // StoreXAddOp returns the OpCode to atomically add a register to a value in memory. func StoreXAddOp(size Size) OpCode { return OpCode(StXClass).SetMode(XAddMode).SetSize(size) } // StoreXAdd atomically adds src to *dst. func StoreXAdd(dst, src Register, size Size) Instruction { return Instruction{ OpCode: StoreXAddOp(size), Dst: dst, Src: src, } } golang-github-cilium-ebpf-0.11.0/asm/load_store_string.go000066400000000000000000000031431456504511400233500ustar00rootroot00000000000000// Code generated by "stringer -output load_store_string.go -type=Mode,Size"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidMode-255] _ = x[ImmMode-0] _ = x[AbsMode-32] _ = x[IndMode-64] _ = x[MemMode-96] _ = x[XAddMode-192] } const ( _Mode_name_0 = "ImmMode" _Mode_name_1 = "AbsMode" _Mode_name_2 = "IndMode" _Mode_name_3 = "MemMode" _Mode_name_4 = "XAddMode" _Mode_name_5 = "InvalidMode" ) func (i Mode) String() string { switch { case i == 0: return _Mode_name_0 case i == 32: return _Mode_name_1 case i == 64: return _Mode_name_2 case i == 96: return _Mode_name_3 case i == 192: return _Mode_name_4 case i == 255: return _Mode_name_5 default: return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidSize-255] _ = x[DWord-24] _ = x[Word-0] _ = x[Half-8] _ = x[Byte-16] } const ( _Size_name_0 = "Word" _Size_name_1 = "Half" _Size_name_2 = "Byte" _Size_name_3 = "DWord" _Size_name_4 = "InvalidSize" ) func (i Size) String() string { switch { case i == 0: return _Size_name_0 case i == 8: return _Size_name_1 case i == 16: return _Size_name_2 case i == 24: return _Size_name_3 case i == 255: return _Size_name_4 default: return "Size(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-cilium-ebpf-0.11.0/asm/metadata.go000066400000000000000000000034551456504511400214150ustar00rootroot00000000000000package asm // Metadata contains metadata about an instruction. type Metadata struct { head *metaElement } type metaElement struct { next *metaElement key, value interface{} } // Find the element containing key. // // Returns nil if there is no such element. func (m *Metadata) find(key interface{}) *metaElement { for e := m.head; e != nil; e = e.next { if e.key == key { return e } } return nil } // Remove an element from the linked list. // // Copies as many elements of the list as necessary to remove r, but doesn't // perform a full copy. func (m *Metadata) remove(r *metaElement) { current := &m.head for e := m.head; e != nil; e = e.next { if e == r { // We've found the element we want to remove. *current = e.next // No need to copy the tail. return } // There is another element in front of the one we want to remove. // We have to copy it to be able to change metaElement.next. cpy := &metaElement{key: e.key, value: e.value} *current = cpy current = &cpy.next } } // Set a key to a value. // // If value is nil, the key is removed. Avoids modifying old metadata by // copying if necessary. func (m *Metadata) Set(key, value interface{}) { if e := m.find(key); e != nil { if e.value == value { // Key is present and the value is the same. Nothing to do. return } // Key is present with a different value. Create a copy of the list // which doesn't have the element in it. m.remove(e) } // m.head is now a linked list that doesn't contain key. if value == nil { return } m.head = &metaElement{key: key, value: value, next: m.head} } // Get the value of a key. // // Returns nil if no value with the given key is present. func (m *Metadata) Get(key interface{}) interface{} { if e := m.find(key); e != nil { return e.value } return nil } golang-github-cilium-ebpf-0.11.0/asm/metadata_test.go000066400000000000000000000043421456504511400224500ustar00rootroot00000000000000package asm import ( "testing" "unsafe" qt "github.com/frankban/quicktest" ) func TestMetadata(t *testing.T) { var m Metadata // Metadata should be the size of a pointer. qt.Assert(t, unsafe.Sizeof(m), qt.Equals, unsafe.Sizeof(uintptr(0))) // A lookup in a nil meta should return nil. qt.Assert(t, m.Get(bool(false)), qt.IsNil) // We can look up anything we inserted. m.Set(bool(false), int(0)) m.Set(int(1), int(1)) qt.Assert(t, m.Get(bool(false)), qt.Equals, int(0)) qt.Assert(t, m.Get(int(1)), qt.Equals, int(1)) // We have copy on write semantics old := m m.Set(bool(false), int(1)) qt.Assert(t, m.Get(bool(false)), qt.Equals, int(1)) qt.Assert(t, m.Get(int(1)), qt.Equals, int(1)) qt.Assert(t, old.Get(bool(false)), qt.Equals, int(0)) qt.Assert(t, old.Get(int(1)), qt.Equals, int(1)) // Newtypes are handled distinctly. type b bool m.Set(b(false), int(42)) qt.Assert(t, m.Get(bool(false)), qt.Equals, int(1)) qt.Assert(t, m.Get(int(1)), qt.Equals, int(1)) qt.Assert(t, m.Get(b(false)), qt.Equals, int(42)) // Setting nil removes a key. m.Set(bool(false), nil) qt.Assert(t, m.Get(bool(false)), qt.IsNil) qt.Assert(t, m.Get(int(1)), qt.Equals, int(1)) qt.Assert(t, m.Get(b(false)), qt.Equals, int(42)) } func BenchmarkMetadata(b *testing.B) { // Assume that three bits of metadata on a single instruction is // our worst case. const worstCaseItems = 3 type t struct{} b.Run("add first", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { var v Metadata v.Set(t{}, 0) } }) b.Run("add last", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems-1; i++ { m.Set(i, i) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { v := m v.Set(t{}, 0) } }) b.Run("add existing", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems-1; i++ { m.Set(i, i) } m.Set(t{}, 0) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { v := m v.Set(t{}, 0) } }) b.Run("get miss", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems; i++ { m.Set(i, i) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { if m.Get(t{}) != nil { b.Fatal("got result from miss") } } }) } golang-github-cilium-ebpf-0.11.0/asm/opcode.go000066400000000000000000000142621456504511400211040ustar00rootroot00000000000000package asm import ( "fmt" "strings" ) //go:generate stringer -output opcode_string.go -type=Class // Class of operations // // msb lsb // +---+--+---+ // | ?? |CLS| // +---+--+---+ type Class uint8 const classMask OpCode = 0x07 const ( // LdClass loads immediate values into registers. // Also used for non-standard load operations from cBPF. LdClass Class = 0x00 // LdXClass loads memory into registers. LdXClass Class = 0x01 // StClass stores immediate values to memory. StClass Class = 0x02 // StXClass stores registers to memory. StXClass Class = 0x03 // ALUClass describes arithmetic operators. ALUClass Class = 0x04 // JumpClass describes jump operators. JumpClass Class = 0x05 // Jump32Class describes jump operators with 32-bit comparisons. // Requires kernel 5.1. Jump32Class Class = 0x06 // ALU64Class describes arithmetic operators in 64-bit mode. ALU64Class Class = 0x07 ) // IsLoad checks if this is either LdClass or LdXClass. func (cls Class) IsLoad() bool { return cls == LdClass || cls == LdXClass } // IsStore checks if this is either StClass or StXClass. func (cls Class) IsStore() bool { return cls == StClass || cls == StXClass } func (cls Class) isLoadOrStore() bool { return cls.IsLoad() || cls.IsStore() } // IsALU checks if this is either ALUClass or ALU64Class. func (cls Class) IsALU() bool { return cls == ALUClass || cls == ALU64Class } // IsJump checks if this is either JumpClass or Jump32Class. func (cls Class) IsJump() bool { return cls == JumpClass || cls == Jump32Class } func (cls Class) isJumpOrALU() bool { return cls.IsJump() || cls.IsALU() } // OpCode is a packed eBPF opcode. // // Its encoding is defined by a Class value: // // msb lsb // +----+-+---+ // | ???? |CLS| // +----+-+---+ type OpCode uint8 // InvalidOpCode is returned by setters on OpCode const InvalidOpCode OpCode = 0xff // rawInstructions returns the number of BPF instructions required // to encode this opcode. func (op OpCode) rawInstructions() int { if op.IsDWordLoad() { return 2 } return 1 } func (op OpCode) IsDWordLoad() bool { return op == LoadImmOp(DWord) } // Class returns the class of operation. func (op OpCode) Class() Class { return Class(op & classMask) } // Mode returns the mode for load and store operations. func (op OpCode) Mode() Mode { if !op.Class().isLoadOrStore() { return InvalidMode } return Mode(op & modeMask) } // Size returns the size for load and store operations. func (op OpCode) Size() Size { if !op.Class().isLoadOrStore() { return InvalidSize } return Size(op & sizeMask) } // Source returns the source for branch and ALU operations. func (op OpCode) Source() Source { if !op.Class().isJumpOrALU() || op.ALUOp() == Swap { return InvalidSource } return Source(op & sourceMask) } // ALUOp returns the ALUOp. func (op OpCode) ALUOp() ALUOp { if !op.Class().IsALU() { return InvalidALUOp } return ALUOp(op & aluMask) } // Endianness returns the Endianness for a byte swap instruction. func (op OpCode) Endianness() Endianness { if op.ALUOp() != Swap { return InvalidEndian } return Endianness(op & endianMask) } // JumpOp returns the JumpOp. // Returns InvalidJumpOp if it doesn't encode a jump. func (op OpCode) JumpOp() JumpOp { if !op.Class().IsJump() { return InvalidJumpOp } jumpOp := JumpOp(op & jumpMask) // Some JumpOps are only supported by JumpClass, not Jump32Class. if op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call || jumpOp == Ja) { return InvalidJumpOp } return jumpOp } // SetMode sets the mode on load and store operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetMode(mode Mode) OpCode { if !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) { return InvalidOpCode } return (op & ^modeMask) | OpCode(mode) } // SetSize sets the size on load and store operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetSize(size Size) OpCode { if !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) { return InvalidOpCode } return (op & ^sizeMask) | OpCode(size) } // SetSource sets the source on jump and ALU operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetSource(source Source) OpCode { if !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) { return InvalidOpCode } return (op & ^sourceMask) | OpCode(source) } // SetALUOp sets the ALUOp on ALU operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetALUOp(alu ALUOp) OpCode { if !op.Class().IsALU() || !valid(OpCode(alu), aluMask) { return InvalidOpCode } return (op & ^aluMask) | OpCode(alu) } // SetJumpOp sets the JumpOp on jump operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetJumpOp(jump JumpOp) OpCode { if !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) { return InvalidOpCode } newOp := (op & ^jumpMask) | OpCode(jump) // Check newOp is legal. if newOp.JumpOp() == InvalidJumpOp { return InvalidOpCode } return newOp } func (op OpCode) String() string { var f strings.Builder switch class := op.Class(); { case class.isLoadOrStore(): f.WriteString(strings.TrimSuffix(class.String(), "Class")) mode := op.Mode() f.WriteString(strings.TrimSuffix(mode.String(), "Mode")) switch op.Size() { case DWord: f.WriteString("DW") case Word: f.WriteString("W") case Half: f.WriteString("H") case Byte: f.WriteString("B") } case class.IsALU(): f.WriteString(op.ALUOp().String()) if op.ALUOp() == Swap { // Width for Endian is controlled by Constant f.WriteString(op.Endianness().String()) } else { if class == ALUClass { f.WriteString("32") } f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) } case class.IsJump(): f.WriteString(op.JumpOp().String()) if class == Jump32Class { f.WriteString("32") } if jop := op.JumpOp(); jop != Exit && jop != Call { f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) } default: fmt.Fprintf(&f, "OpCode(%#x)", uint8(op)) } return f.String() } // valid returns true if all bits in value are covered by mask. func valid(value, mask OpCode) bool { return value & ^mask == 0 } golang-github-cilium-ebpf-0.11.0/asm/opcode_string.go000066400000000000000000000014371456504511400224720ustar00rootroot00000000000000// Code generated by "stringer -output opcode_string.go -type=Class"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[LdClass-0] _ = x[LdXClass-1] _ = x[StClass-2] _ = x[StXClass-3] _ = x[ALUClass-4] _ = x[JumpClass-5] _ = x[Jump32Class-6] _ = x[ALU64Class-7] } const _Class_name = "LdClassLdXClassStClassStXClassALUClassJumpClassJump32ClassALU64Class" var _Class_index = [...]uint8{0, 7, 15, 22, 30, 38, 47, 58, 68} func (i Class) String() string { if i >= Class(len(_Class_index)-1) { return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } return _Class_name[_Class_index[i]:_Class_index[i+1]] } golang-github-cilium-ebpf-0.11.0/asm/opcode_test.go000066400000000000000000000017341456504511400221430ustar00rootroot00000000000000package asm import ( "fmt" "testing" qt "github.com/frankban/quicktest" ) func TestGetSetJumpOp(t *testing.T) { test := func(class Class, op JumpOp, valid bool) { t.Run(fmt.Sprintf("%s-%s", class, op), func(t *testing.T) { opcode := OpCode(class).SetJumpOp(op) if valid { qt.Assert(t, opcode, qt.Not(qt.Equals), InvalidOpCode) qt.Assert(t, opcode.JumpOp(), qt.Equals, op) } else { qt.Assert(t, opcode, qt.Equals, InvalidOpCode) qt.Assert(t, opcode.JumpOp(), qt.Equals, InvalidJumpOp) } }) } // Exit, call and JA aren't allowed with Jump32 test(Jump32Class, Exit, false) test(Jump32Class, Call, false) test(Jump32Class, Ja, false) // But are with Jump test(JumpClass, Exit, true) test(JumpClass, Call, true) test(JumpClass, Ja, true) // All other ops work for _, op := range []JumpOp{ JEq, JGT, JGE, JSet, JNE, JSGT, JSGE, JLT, JLE, JSLT, JSLE, } { test(Jump32Class, op, true) test(JumpClass, op, true) } } golang-github-cilium-ebpf-0.11.0/asm/register.go000066400000000000000000000015331456504511400214540ustar00rootroot00000000000000package asm import ( "fmt" ) // Register is the source or destination of most operations. type Register uint8 // R0 contains return values. const R0 Register = 0 // Registers for function arguments. const ( R1 Register = R0 + 1 + iota R2 R3 R4 R5 ) // Callee saved registers preserved by function calls. const ( R6 Register = R5 + 1 + iota R7 R8 R9 ) // Read-only frame pointer to access stack. const ( R10 Register = R9 + 1 RFP = R10 ) // Pseudo registers used by 64bit loads and jumps const ( PseudoMapFD = R1 // BPF_PSEUDO_MAP_FD PseudoMapValue = R2 // BPF_PSEUDO_MAP_VALUE PseudoCall = R1 // BPF_PSEUDO_CALL PseudoFunc = R4 // BPF_PSEUDO_FUNC PseudoKfuncCall = R2 // BPF_PSEUDO_KFUNC_CALL ) func (r Register) String() string { v := uint8(r) if v == 10 { return "rfp" } return fmt.Sprintf("r%d", v) } golang-github-cilium-ebpf-0.11.0/attachtype_string.go000066400000000000000000000054401456504511400226050ustar00rootroot00000000000000// Code generated by "stringer -type AttachType -trimprefix Attach"; DO NOT EDIT. package ebpf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[AttachNone-0] _ = x[AttachCGroupInetIngress-0] _ = x[AttachCGroupInetEgress-1] _ = x[AttachCGroupInetSockCreate-2] _ = x[AttachCGroupSockOps-3] _ = x[AttachSkSKBStreamParser-4] _ = x[AttachSkSKBStreamVerdict-5] _ = x[AttachCGroupDevice-6] _ = x[AttachSkMsgVerdict-7] _ = x[AttachCGroupInet4Bind-8] _ = x[AttachCGroupInet6Bind-9] _ = x[AttachCGroupInet4Connect-10] _ = x[AttachCGroupInet6Connect-11] _ = x[AttachCGroupInet4PostBind-12] _ = x[AttachCGroupInet6PostBind-13] _ = x[AttachCGroupUDP4Sendmsg-14] _ = x[AttachCGroupUDP6Sendmsg-15] _ = x[AttachLircMode2-16] _ = x[AttachFlowDissector-17] _ = x[AttachCGroupSysctl-18] _ = x[AttachCGroupUDP4Recvmsg-19] _ = x[AttachCGroupUDP6Recvmsg-20] _ = x[AttachCGroupGetsockopt-21] _ = x[AttachCGroupSetsockopt-22] _ = x[AttachTraceRawTp-23] _ = x[AttachTraceFEntry-24] _ = x[AttachTraceFExit-25] _ = x[AttachModifyReturn-26] _ = x[AttachLSMMac-27] _ = x[AttachTraceIter-28] _ = x[AttachCgroupInet4GetPeername-29] _ = x[AttachCgroupInet6GetPeername-30] _ = x[AttachCgroupInet4GetSockname-31] _ = x[AttachCgroupInet6GetSockname-32] _ = x[AttachXDPDevMap-33] _ = x[AttachCgroupInetSockRelease-34] _ = x[AttachXDPCPUMap-35] _ = x[AttachSkLookup-36] _ = x[AttachXDP-37] _ = x[AttachSkSKBVerdict-38] _ = x[AttachSkReuseportSelect-39] _ = x[AttachSkReuseportSelectOrMigrate-40] _ = x[AttachPerfEvent-41] _ = x[AttachTraceKprobeMulti-42] } const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMulti" var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626} func (i AttachType) String() string { if i >= AttachType(len(_AttachType_index)-1) { return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" } return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]] } golang-github-cilium-ebpf-0.11.0/btf/000077500000000000000000000000001456504511400172725ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/btf.go000066400000000000000000000523121456504511400203770ustar00rootroot00000000000000package btf import ( "bufio" "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "reflect" "sync" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) const btfMagic = 0xeB9F // Errors returned by BTF functions. var ( ErrNotSupported = internal.ErrNotSupported ErrNotFound = errors.New("not found") ErrNoExtendedInfo = errors.New("no extended info") ErrMultipleMatches = errors.New("multiple matching types") ) // ID represents the unique ID of a BTF object. type ID = sys.BTFID // Spec allows querying a set of Types and loading the set into the // kernel. type Spec struct { // All types contained by the spec, not including types from the base in // case the spec was parsed from split BTF. types []Type // Type IDs indexed by type. typeIDs map[Type]TypeID // The ID of the first type in types. firstTypeID TypeID // Types indexed by essential name. // Includes all struct flavors and types with the same name. namedTypes map[essentialName][]Type // String table from ELF, may be nil. strings *stringTable // Byte order of the ELF we decoded the spec from, may be nil. byteOrder binary.ByteOrder } var btfHeaderLen = binary.Size(&btfHeader{}) type btfHeader struct { Magic uint16 Version uint8 Flags uint8 HdrLen uint32 TypeOff uint32 TypeLen uint32 StringOff uint32 StringLen uint32 } // typeStart returns the offset from the beginning of the .BTF section // to the start of its type entries. func (h *btfHeader) typeStart() int64 { return int64(h.HdrLen + h.TypeOff) } // stringStart returns the offset from the beginning of the .BTF section // to the start of its string table. func (h *btfHeader) stringStart() int64 { return int64(h.HdrLen + h.StringOff) } // newSpec creates a Spec containing only Void. func newSpec() *Spec { return &Spec{ []Type{(*Void)(nil)}, map[Type]TypeID{(*Void)(nil): 0}, 0, make(map[essentialName][]Type), nil, nil, } } // LoadSpec opens file and calls LoadSpecFromReader on it. func LoadSpec(file string) (*Spec, error) { fh, err := os.Open(file) if err != nil { return nil, err } defer fh.Close() return LoadSpecFromReader(fh) } // LoadSpecFromReader reads from an ELF or a raw BTF blob. // // Returns ErrNotFound if reading from an ELF which contains no BTF. ExtInfos // may be nil. func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { file, err := internal.NewSafeELFFile(rd) if err != nil { if bo := guessRawBTFByteOrder(rd); bo != nil { return loadRawSpec(io.NewSectionReader(rd, 0, math.MaxInt64), bo, nil) } return nil, err } return loadSpecFromELF(file) } // LoadSpecAndExtInfosFromReader reads from an ELF. // // ExtInfos may be nil if the ELF doesn't contain section metadata. // Returns ErrNotFound if the ELF contains no BTF. func LoadSpecAndExtInfosFromReader(rd io.ReaderAt) (*Spec, *ExtInfos, error) { file, err := internal.NewSafeELFFile(rd) if err != nil { return nil, nil, err } spec, err := loadSpecFromELF(file) if err != nil { return nil, nil, err } extInfos, err := loadExtInfosFromELF(file, spec) if err != nil && !errors.Is(err, ErrNotFound) { return nil, nil, err } return spec, extInfos, nil } // symbolOffsets extracts all symbols offsets from an ELF and indexes them by // section and variable name. // // References to variables in BTF data sections carry unsigned 32-bit offsets. // Some ELF symbols (e.g. in vmlinux) may point to virtual memory that is well // beyond this range. Since these symbols cannot be described by BTF info, // ignore them here. func symbolOffsets(file *internal.SafeELFFile) (map[symbol]uint32, error) { symbols, err := file.Symbols() if err != nil { return nil, fmt.Errorf("can't read symbols: %v", err) } offsets := make(map[symbol]uint32) for _, sym := range symbols { if idx := sym.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { // Ignore things like SHN_ABS continue } if sym.Value > math.MaxUint32 { // VarSecinfo offset is u32, cannot reference symbols in higher regions. continue } if int(sym.Section) >= len(file.Sections) { return nil, fmt.Errorf("symbol %s: invalid section %d", sym.Name, sym.Section) } secName := file.Sections[sym.Section].Name offsets[symbol{secName, sym.Name}] = uint32(sym.Value) } return offsets, nil } func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) { var ( btfSection *elf.Section sectionSizes = make(map[string]uint32) ) for _, sec := range file.Sections { switch sec.Name { case ".BTF": btfSection = sec default: if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS { break } if sec.Size > math.MaxUint32 { return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) } sectionSizes[sec.Name] = uint32(sec.Size) } } if btfSection == nil { return nil, fmt.Errorf("btf: %w", ErrNotFound) } offsets, err := symbolOffsets(file) if err != nil { return nil, err } if btfSection.ReaderAt == nil { return nil, fmt.Errorf("compressed BTF is not supported") } spec, err := loadRawSpec(btfSection.ReaderAt, file.ByteOrder, nil) if err != nil { return nil, err } err = fixupDatasec(spec.types, sectionSizes, offsets) if err != nil { return nil, err } return spec, nil } func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error) { var ( baseStrings *stringTable firstTypeID TypeID err error ) if base != nil { if base.firstTypeID != 0 { return nil, fmt.Errorf("can't use split BTF as base") } if base.strings == nil { return nil, fmt.Errorf("parse split BTF: base must be loaded from an ELF") } baseStrings = base.strings firstTypeID, err = base.nextTypeID() if err != nil { return nil, err } } rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings) if err != nil { return nil, err } types, err := inflateRawTypes(rawTypes, rawStrings, base) if err != nil { return nil, err } typeIDs, typesByName := indexTypes(types, firstTypeID) return &Spec{ namedTypes: typesByName, typeIDs: typeIDs, types: types, firstTypeID: firstTypeID, strings: rawStrings, byteOrder: bo, }, nil } func indexTypes(types []Type, firstTypeID TypeID) (map[Type]TypeID, map[essentialName][]Type) { namedTypes := 0 for _, typ := range types { if typ.TypeName() != "" { // Do a pre-pass to figure out how big types by name has to be. // Most types have unique names, so it's OK to ignore essentialName // here. namedTypes++ } } typeIDs := make(map[Type]TypeID, len(types)) typesByName := make(map[essentialName][]Type, namedTypes) for i, typ := range types { if name := newEssentialName(typ.TypeName()); name != "" { typesByName[name] = append(typesByName[name], typ) } typeIDs[typ] = firstTypeID + TypeID(i) } return typeIDs, typesByName } // LoadKernelSpec returns the current kernel's BTF information. // // Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system // for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled. func LoadKernelSpec() (*Spec, error) { spec, _, err := kernelSpec() if err != nil { return nil, err } return spec.Copy(), nil } var kernelBTF struct { sync.RWMutex spec *Spec // True if the spec was read from an ELF instead of raw BTF in /sys. fallback bool } // FlushKernelSpec removes any cached kernel type information. func FlushKernelSpec() { kernelBTF.Lock() defer kernelBTF.Unlock() kernelBTF.spec, kernelBTF.fallback = nil, false } func kernelSpec() (*Spec, bool, error) { kernelBTF.RLock() spec, fallback := kernelBTF.spec, kernelBTF.fallback kernelBTF.RUnlock() if spec == nil { kernelBTF.Lock() defer kernelBTF.Unlock() spec, fallback = kernelBTF.spec, kernelBTF.fallback } if spec != nil { return spec, fallback, nil } spec, fallback, err := loadKernelSpec() if err != nil { return nil, false, err } kernelBTF.spec, kernelBTF.fallback = spec, fallback return spec, fallback, nil } func loadKernelSpec() (_ *Spec, fallback bool, _ error) { fh, err := os.Open("/sys/kernel/btf/vmlinux") if err == nil { defer fh.Close() spec, err := loadRawSpec(fh, internal.NativeEndian, nil) return spec, false, err } file, err := findVMLinux() if err != nil { return nil, false, err } defer file.Close() spec, err := loadSpecFromELF(file) return spec, true, err } // findVMLinux scans multiple well-known paths for vmlinux kernel images. func findVMLinux() (*internal.SafeELFFile, error) { release, err := internal.KernelRelease() if err != nil { return nil, err } // use same list of locations as libbpf // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 locations := []string{ "/boot/vmlinux-%s", "/lib/modules/%s/vmlinux-%[1]s", "/lib/modules/%s/build/vmlinux", "/usr/lib/modules/%s/kernel/vmlinux", "/usr/lib/debug/boot/vmlinux-%s", "/usr/lib/debug/boot/vmlinux-%s.debug", "/usr/lib/debug/lib/modules/%s/vmlinux", } for _, loc := range locations { file, err := internal.OpenSafeELFFile(fmt.Sprintf(loc, release)) if errors.Is(err, os.ErrNotExist) { continue } return file, err } return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported) } // parseBTFHeader parses the header of the .BTF section. func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) { var header btfHeader if err := binary.Read(r, bo, &header); err != nil { return nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { return nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { return nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { return nil, errors.New("header length shorter than btfHeader size") } if _, err := io.CopyN(internal.DiscardZeroes{}, r, remainder); err != nil { return nil, fmt.Errorf("header padding: %v", err) } return &header, nil } func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder { buf := new(bufio.Reader) for _, bo := range []binary.ByteOrder{ binary.LittleEndian, binary.BigEndian, } { buf.Reset(io.NewSectionReader(r, 0, math.MaxInt64)) if _, err := parseBTFHeader(buf, bo); err == nil { return bo } } return nil } // parseBTF reads a .BTF section into memory and parses it into a list of // raw types and a string table. func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) { buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64) header, err := parseBTFHeader(buf, bo) if err != nil { return nil, nil, fmt.Errorf("parsing .BTF header: %v", err) } rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)), baseStrings) if err != nil { return nil, nil, fmt.Errorf("can't read type names: %w", err) } buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen))) rawTypes, err := readTypes(buf, bo, header.TypeLen) if err != nil { return nil, nil, fmt.Errorf("can't read types: %w", err) } return rawTypes, rawStrings, nil } type symbol struct { section string name string } // fixupDatasec attempts to patch up missing info in Datasecs and its members by // supplementing them with information from the ELF headers and symbol table. func fixupDatasec(types []Type, sectionSizes map[string]uint32, offsets map[symbol]uint32) error { for _, typ := range types { ds, ok := typ.(*Datasec) if !ok { continue } name := ds.Name // Some Datasecs are virtual and don't have corresponding ELF sections. switch name { case ".ksyms": // .ksyms describes forward declarations of kfunc signatures. // Nothing to fix up, all sizes and offsets are 0. for _, vsi := range ds.Vars { _, ok := vsi.Type.(*Func) if !ok { // Only Funcs are supported in the .ksyms Datasec. return fmt.Errorf("data section %s: expected *btf.Func, not %T: %w", name, vsi.Type, ErrNotSupported) } } continue case ".kconfig": // .kconfig has a size of 0 and has all members' offsets set to 0. // Fix up all offsets and set the Datasec's size. if err := fixupDatasecLayout(ds); err != nil { return err } // Fix up extern to global linkage to avoid a BTF verifier error. for _, vsi := range ds.Vars { vsi.Type.(*Var).Linkage = GlobalVar } continue } if ds.Size != 0 { continue } ds.Size, ok = sectionSizes[name] if !ok { return fmt.Errorf("data section %s: missing size", name) } for i := range ds.Vars { symName := ds.Vars[i].Type.TypeName() ds.Vars[i].Offset, ok = offsets[symbol{name, symName}] if !ok { return fmt.Errorf("data section %s: missing offset for symbol %s", name, symName) } } } return nil } // fixupDatasecLayout populates ds.Vars[].Offset according to var sizes and // alignment. Calculate and set ds.Size. func fixupDatasecLayout(ds *Datasec) error { var off uint32 for i, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { return fmt.Errorf("member %d: unsupported type %T", i, vsi.Type) } size, err := Sizeof(v.Type) if err != nil { return fmt.Errorf("variable %s: getting size: %w", v.Name, err) } align, err := alignof(v.Type) if err != nil { return fmt.Errorf("variable %s: getting alignment: %w", v.Name, err) } // Align the current member based on the offset of the end of the previous // member and the alignment of the current member. off = internal.Align(off, uint32(align)) ds.Vars[i].Offset = off off += uint32(size) } ds.Size = off return nil } // Copy creates a copy of Spec. func (s *Spec) Copy() *Spec { types := copyTypes(s.types, nil) typeIDs, typesByName := indexTypes(types, s.firstTypeID) // NB: Other parts of spec are not copied since they are immutable. return &Spec{ types, typeIDs, s.firstTypeID, typesByName, s.strings, s.byteOrder, } } type sliceWriter []byte func (sw sliceWriter) Write(p []byte) (int, error) { if len(p) != len(sw) { return 0, errors.New("size doesn't match") } return copy(sw, p), nil } // nextTypeID returns the next unallocated type ID or an error if there are no // more type IDs. func (s *Spec) nextTypeID() (TypeID, error) { id := s.firstTypeID + TypeID(len(s.types)) if id < s.firstTypeID { return 0, fmt.Errorf("no more type IDs") } return id, nil } // TypeByID returns the BTF Type with the given type ID. // // Returns an error wrapping ErrNotFound if a Type with the given ID // does not exist in the Spec. func (s *Spec) TypeByID(id TypeID) (Type, error) { if id < s.firstTypeID { return nil, fmt.Errorf("look up type with ID %d (first ID is %d): %w", id, s.firstTypeID, ErrNotFound) } index := int(id - s.firstTypeID) if index >= len(s.types) { return nil, fmt.Errorf("look up type with ID %d: %w", id, ErrNotFound) } return s.types[index], nil } // TypeID returns the ID for a given Type. // // Returns an error wrapping ErrNoFound if the type isn't part of the Spec. func (s *Spec) TypeID(typ Type) (TypeID, error) { if _, ok := typ.(*Void); ok { // Equality is weird for void, since it is a zero sized type. return 0, nil } id, ok := s.typeIDs[typ] if !ok { return 0, fmt.Errorf("no ID for type %s: %w", typ, ErrNotFound) } return id, nil } // AnyTypesByName returns a list of BTF Types with the given name. // // If the BTF blob describes multiple compilation units like vmlinux, multiple // Types with the same name and kind can exist, but might not describe the same // data structure. // // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. func (s *Spec) AnyTypesByName(name string) ([]Type, error) { types := s.namedTypes[newEssentialName(name)] if len(types) == 0 { return nil, fmt.Errorf("type name %s: %w", name, ErrNotFound) } // Return a copy to prevent changes to namedTypes. result := make([]Type, 0, len(types)) for _, t := range types { // Match against the full name, not just the essential one // in case the type being looked up is a struct flavor. if t.TypeName() == name { result = append(result, t) } } return result, nil } // AnyTypeByName returns a Type with the given name. // // Returns an error if multiple types of that name exist. func (s *Spec) AnyTypeByName(name string) (Type, error) { types, err := s.AnyTypesByName(name) if err != nil { return nil, err } if len(types) > 1 { return nil, fmt.Errorf("found multiple types: %v", types) } return types[0], nil } // TypeByName searches for a Type with a specific name. Since multiple Types // with the same name can exist, the parameter typ is taken to narrow down the // search in case of a clash. // // typ must be a non-nil pointer to an implementation of a Type. On success, the // address of the found Type will be copied to typ. // // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. // Returns an error wrapping ErrMultipleTypes if multiple candidates are found. func (s *Spec) TypeByName(name string, typ interface{}) error { typeInterface := reflect.TypeOf((*Type)(nil)).Elem() // typ may be **T or *Type typValue := reflect.ValueOf(typ) if typValue.Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer", typ) } typPtr := typValue.Elem() if !typPtr.CanSet() { return fmt.Errorf("%T cannot be set", typ) } wanted := typPtr.Type() if wanted == typeInterface { // This is *Type. Unwrap the value's type. wanted = typPtr.Elem().Type() } if !wanted.AssignableTo(typeInterface) { return fmt.Errorf("%T does not satisfy Type interface", typ) } types, err := s.AnyTypesByName(name) if err != nil { return err } var candidate Type for _, typ := range types { if reflect.TypeOf(typ) != wanted { continue } if candidate != nil { return fmt.Errorf("type %s(%T): %w", name, typ, ErrMultipleMatches) } candidate = typ } if candidate == nil { return fmt.Errorf("%s %s: %w", wanted, name, ErrNotFound) } typPtr.Set(reflect.ValueOf(candidate)) return nil } // LoadSplitSpecFromReader loads split BTF from a reader. // // Types from base are used to resolve references in the split BTF. // The returned Spec only contains types from the split BTF, not from the base. func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) { return loadRawSpec(r, internal.NativeEndian, base) } // TypesIterator iterates over types of a given spec. type TypesIterator struct { types []Type index int // The last visited type in the spec. Type Type } // Iterate returns the types iterator. func (s *Spec) Iterate() *TypesIterator { // We share the backing array of types with the Spec. This is safe since // we don't allow deletion or shuffling of types. return &TypesIterator{types: s.types, index: 0} } // Next returns true as long as there are any remaining types. func (iter *TypesIterator) Next() bool { if len(iter.types) <= iter.index { return false } iter.Type = iter.types[iter.index] iter.index++ return true } // haveBTF attempts to load a BTF blob containing an Int. It should pass on any // kernel that supports BPF_BTF_LOAD. var haveBTF = internal.NewFeatureTest("BTF", "4.18", func() error { // 0-length anonymous integer err := probeBTF(&Int{}) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { return internal.ErrNotSupported } return err }) // haveMapBTF attempts to load a minimal BTF blob containing a Var. It is // used as a proxy for .bss, .data and .rodata map support, which generally // come with a Var and Datasec. These were introduced in Linux 5.2. var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func() error { if err := haveBTF(); err != nil { return err } v := &Var{ Name: "a", Type: &Pointer{(*Void)(nil)}, } err := probeBTF(v) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { // Treat both EINVAL and EPERM as not supported: creating the map may still // succeed without Btf* attrs. return internal.ErrNotSupported } return err }) // haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It // is used as a proxy for ext_info (func_info) support, which depends on // Func(Proto) by definition. var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0", func() error { if err := haveBTF(); err != nil { return err } fn := &Func{ Name: "a", Type: &FuncProto{Return: (*Void)(nil)}, } err := probeBTF(fn) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { return internal.ErrNotSupported } return err }) var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func() error { if err := haveProgBTF(); err != nil { return err } fn := &Func{ Name: "a", Type: &FuncProto{Return: (*Void)(nil)}, Linkage: GlobalFunc, } err := probeBTF(fn) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } return err }) func probeBTF(typ Type) error { b, err := NewBuilder([]Type{typ}) if err != nil { return err } buf, err := b.Marshal(nil, nil) if err != nil { return err } fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ Btf: sys.NewSlicePointer(buf), BtfSize: uint32(len(buf)), }) if err == nil { fd.Close() } return err } golang-github-cilium-ebpf-0.11.0/btf/btf_test.go000066400000000000000000000300001456504511400214240ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "errors" "fmt" "io" "os" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" qt "github.com/frankban/quicktest" ) func vmlinuxSpec(tb testing.TB) *Spec { tb.Helper() // /sys/kernel/btf was introduced in 341dfcf8d78e ("btf: expose BTF info // through sysfs"), which shipped in Linux 5.4. testutils.SkipOnOldKernel(tb, "5.4", "vmlinux BTF in sysfs") spec, fallback, err := kernelSpec() if err != nil { tb.Fatal(err) } if fallback { tb.Fatal("/sys/kernel/btf/vmlinux is not available") } return spec.Copy() } type specAndRawBTF struct { raw []byte spec *Spec } var vmlinuxTestdata = internal.Memoize(func() (specAndRawBTF, error) { b, err := internal.ReadAllCompressed("testdata/vmlinux.btf.gz") if err != nil { return specAndRawBTF{}, err } spec, err := loadRawSpec(bytes.NewReader(b), binary.LittleEndian, nil) if err != nil { return specAndRawBTF{}, err } return specAndRawBTF{b, spec}, nil }) func vmlinuxTestdataReader(tb testing.TB) *bytes.Reader { tb.Helper() td, err := vmlinuxTestdata() if err != nil { tb.Fatal(err) } return bytes.NewReader(td.raw) } func vmlinuxTestdataSpec(tb testing.TB) *Spec { tb.Helper() td, err := vmlinuxTestdata() if err != nil { tb.Fatal(err) } return td.spec.Copy() } func parseELFBTF(tb testing.TB, file string) *Spec { tb.Helper() spec, err := LoadSpec(file) if err != nil { tb.Fatal("Can't load BTF:", err) } return spec } func TestAnyTypesByName(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) types, err := spec.AnyTypesByName("ambiguous") if err != nil { t.Fatal(err) } if len(types) != 1 { t.Fatalf("expected to receive exactly 1 types from querying ambiguous type, got: %v", types) } types, err = spec.AnyTypesByName("ambiguous___flavour") if err != nil { t.Fatal(err) } if len(types) != 1 { t.Fatalf("expected to receive exactly 1 type from querying ambiguous flavour, got: %v", types) } }) } func TestTypeByNameAmbiguous(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) var typ *Struct if err := spec.TypeByName("ambiguous", &typ); err != nil { t.Fatal(err) } if name := typ.TypeName(); name != "ambiguous" { t.Fatal("expected type name 'ambiguous', got:", name) } if err := spec.TypeByName("ambiguous___flavour", &typ); err != nil { t.Fatal(err) } if name := typ.TypeName(); name != "ambiguous___flavour" { t.Fatal("expected type name 'ambiguous___flavour', got:", name) } }) } func TestTypeByName(t *testing.T) { spec := vmlinuxTestdataSpec(t) for _, typ := range []interface{}{ nil, Struct{}, &Struct{}, []Struct{}, &[]Struct{}, map[int]Struct{}, &map[int]Struct{}, int(0), new(int), } { t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) { // spec.TypeByName MUST fail if typ is a nil btf.Type. if err := spec.TypeByName("iphdr", typ); err == nil { t.Fatalf("TypeByName does not fail with type %T", typ) } }) } // spec.TypeByName MUST return the same address for multiple calls with the same type name. var iphdr1, iphdr2 *Struct if err := spec.TypeByName("iphdr", &iphdr1); err != nil { t.Fatal(err) } if err := spec.TypeByName("iphdr", &iphdr2); err != nil { t.Fatal(err) } if iphdr1 != iphdr2 { t.Fatal("multiple TypeByName calls for `iphdr` name do not return the same addresses") } // It's valid to pass a *Type to TypeByName. typ := Type(iphdr2) if err := spec.TypeByName("iphdr", &typ); err != nil { t.Fatal("Can't look up using *Type:", err) } // Excerpt from linux/ip.h, https://elixir.bootlin.com/linux/latest/A/ident/iphdr // // struct iphdr { // #if defined(__LITTLE_ENDIAN_BITFIELD) // __u8 ihl:4, version:4; // #elif defined (__BIG_ENDIAN_BITFIELD) // __u8 version:4, ihl:4; // #else // ... // } // // The BTF we test against is for little endian. m := iphdr1.Members[1] if m.Name != "version" { t.Fatal("Expected version as the second member, got", m.Name) } td, ok := m.Type.(*Typedef) if !ok { t.Fatalf("version member of iphdr should be a __u8 typedef: actual: %T", m.Type) } u8, ok := td.Type.(*Int) if !ok { t.Fatalf("__u8 typedef should point to an Int type: actual: %T", td.Type) } if m.BitfieldSize != 4 { t.Fatalf("incorrect bitfield size: expected: 4 actual: %d", m.BitfieldSize) } if u8.Encoding != 0 { t.Fatalf("incorrect encoding of an __u8 int: expected: 0 actual: %x", u8.Encoding) } if m.Offset != 4 { t.Fatalf("incorrect bitfield offset: expected: 4 actual: %d", m.Offset) } } func BenchmarkParseVmlinux(b *testing.B) { rd := vmlinuxTestdataReader(b) b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { if _, err := rd.Seek(0, io.SeekStart); err != nil { b.Fatal(err) } if _, err := loadRawSpec(rd, binary.LittleEndian, nil); err != nil { b.Fatal("Can't load BTF:", err) } } } func TestParseCurrentKernelBTF(t *testing.T) { spec := vmlinuxSpec(t) if len(spec.namedTypes) == 0 { t.Fatal("Empty kernel BTF") } totalBytes := 0 distinct := 0 seen := make(map[string]bool) for _, str := range spec.strings.strings { totalBytes += len(str) if !seen[str] { distinct++ seen[str] = true } } t.Logf("%d strings total, %d distinct", len(spec.strings.strings), distinct) t.Logf("Average string size: %.0f", float64(totalBytes)/float64(len(spec.strings.strings))) } func TestFindVMLinux(t *testing.T) { file, err := findVMLinux() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't find vmlinux:", err) } defer file.Close() spec, err := loadSpecFromELF(file) if err != nil { t.Fatal("Can't load BTF:", err) } if len(spec.namedTypes) == 0 { t.Fatal("Empty kernel BTF") } } func TestLoadSpecFromElf(t *testing.T) { testutils.Files(t, testutils.Glob(t, "../testdata/loader-e*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) vt, err := spec.TypeByID(0) if err != nil { t.Error("Can't retrieve void type by ID:", err) } if _, ok := vt.(*Void); !ok { t.Errorf("Expected Void for type id 0, but got: %T", vt) } var bpfMapDef *Struct if err := spec.TypeByName("bpf_map_def", &bpfMapDef); err != nil { t.Error("Can't find bpf_map_def:", err) } var tmp *Void if err := spec.TypeByName("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) { t.Error("TypeByName doesn't return ErrNotFound:", err) } var fn *Func if err := spec.TypeByName("global_fn", &fn); err != nil { t.Error("Can't find global_fn():", err) } else { if fn.Linkage != GlobalFunc { t.Error("Expected global linkage:", fn) } } var v *Var if err := spec.TypeByName("key3", &v); err != nil { t.Error("Cant find key3:", err) } else { if v.Linkage != GlobalVar { t.Error("Expected global linkage:", v) } } }) } func TestVerifierError(t *testing.T) { _, err := NewHandle(&Builder{}) testutils.SkipIfNotSupported(t, err) var ve *internal.VerifierError if !errors.As(err, &ve) { t.Fatalf("expected a VerifierError, got: %v", err) } if ve.Truncated { t.Fatalf("expected non-truncated verifier log: %v", err) } } func TestLoadKernelSpec(t *testing.T) { if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) { t.Skip("/sys/kernel/btf/vmlinux not present") } _, err := LoadKernelSpec() if err != nil { t.Fatal("Can't load kernel spec:", err) } } func TestGuessBTFByteOrder(t *testing.T) { bo := guessRawBTFByteOrder(vmlinuxTestdataReader(t)) if bo != binary.LittleEndian { t.Fatalf("Guessed %s instead of %s", bo, binary.LittleEndian) } } func TestSpecCopy(t *testing.T) { spec := parseELFBTF(t, "../testdata/loader-el.elf") if len(spec.types) < 1 { t.Fatal("Not enough types") } cpy := spec.Copy() for i := range cpy.types { if _, ok := cpy.types[i].(*Void); ok { // Since Void is an empty struct, a Type interface value containing // &Void{} stores (*Void, nil). Since interface equality first compares // the type and then the concrete value, Void is always equal. continue } if cpy.types[i] == spec.types[i] { t.Fatalf("Type at index %d is not a copy: %T == %T", i, cpy.types[i], spec.types[i]) } } } func TestSpecTypeByID(t *testing.T) { _, err := newSpec().TypeByID(0) qt.Assert(t, err, qt.IsNil) _, err = newSpec().TypeByID(1) qt.Assert(t, err, qt.ErrorIs, ErrNotFound) } func TestHaveBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveBTF) } func TestHaveMapBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveMapBTF) } func TestHaveProgBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveProgBTF) } func TestHaveFuncLinkage(t *testing.T) { testutils.CheckFeatureTest(t, haveFuncLinkage) } func ExampleSpec_TypeByName() { // Acquire a Spec via one of its constructors. spec := new(Spec) // Declare a variable of the desired type var foo *Struct if err := spec.TypeByName("foo", &foo); err != nil { // There is no struct with name foo, or there // are multiple possibilities. } // We've found struct foo fmt.Println(foo.Name) } func TestTypesIterator(t *testing.T) { types := []Type{(*Void)(nil), &Int{Size: 4}, &Int{Size: 2}} b, err := NewBuilder(types[1:]) if err != nil { t.Fatal(err) } raw, err := b.Marshal(nil, nil) if err != nil { t.Fatal(err) } spec, err := LoadSpecFromReader(bytes.NewReader(raw)) if err != nil { t.Fatal(err) } iter := spec.Iterate() for i, typ := range types { if !iter.Next() { t.Fatal("Iterator ended early at item", i) } qt.Assert(t, iter.Type, qt.DeepEquals, typ) } if iter.Next() { t.Fatalf("Iterator yielded too many items: %p (%[1]T)", iter.Type) } } func TestLoadSplitSpecFromReader(t *testing.T) { spec := vmlinuxTestdataSpec(t) f, err := os.Open("testdata/btf_testmod.btf") if err != nil { t.Fatal(err) } defer f.Close() splitSpec, err := LoadSplitSpecFromReader(f, spec) if err != nil { t.Fatal(err) } typ, err := splitSpec.AnyTypeByName("bpf_testmod_init") if err != nil { t.Fatal(err) } typeID, err := splitSpec.TypeID(typ) if err != nil { t.Fatal(err) } typeByID, err := splitSpec.TypeByID(typeID) qt.Assert(t, err, qt.IsNil) qt.Assert(t, typeByID, qt.Equals, typ) fnType := typ.(*Func) fnProto := fnType.Type.(*FuncProto) // 'int' is defined in the base BTF... intType, err := spec.AnyTypeByName("int") if err != nil { t.Fatal(err) } // ... but not in the split BTF _, err = splitSpec.AnyTypeByName("int") if err == nil { t.Fatal("'int' is not supposed to be found in the split BTF") } if fnProto.Return != intType { t.Fatalf("Return type of 'bpf_testmod_init()' (%s) does not match 'int' type (%s)", fnProto.Return, intType) } // Check that copied split-BTF's spec has correct type indexing splitSpecCopy := splitSpec.Copy() copyType, err := splitSpecCopy.AnyTypeByName("bpf_testmod_init") if err != nil { t.Fatal(err) } copyTypeID, err := splitSpecCopy.TypeID(copyType) if err != nil { t.Fatal(err) } if copyTypeID != typeID { t.Fatalf("'bpf_testmod_init` type ID (%d) does not match copied spec's (%d)", typeID, copyTypeID) } } func TestFixupDatasecLayout(t *testing.T) { ds := &Datasec{ Size: 0, // Populated by fixup. Vars: []VarSecinfo{ {Type: &Var{Type: &Int{Size: 4}}}, {Type: &Var{Type: &Int{Size: 1}}}, {Type: &Var{Type: &Int{Size: 1}}}, {Type: &Var{Type: &Int{Size: 2}}}, {Type: &Var{Type: &Int{Size: 16}}}, {Type: &Var{Type: &Int{Size: 8}}}, }, } qt.Assert(t, fixupDatasecLayout(ds), qt.IsNil) qt.Assert(t, ds.Size, qt.Equals, uint32(40)) qt.Assert(t, ds.Vars[0].Offset, qt.Equals, uint32(0)) qt.Assert(t, ds.Vars[1].Offset, qt.Equals, uint32(4)) qt.Assert(t, ds.Vars[2].Offset, qt.Equals, uint32(5)) qt.Assert(t, ds.Vars[3].Offset, qt.Equals, uint32(6)) qt.Assert(t, ds.Vars[4].Offset, qt.Equals, uint32(16)) qt.Assert(t, ds.Vars[5].Offset, qt.Equals, uint32(32)) } func BenchmarkSpecCopy(b *testing.B) { spec := vmlinuxTestdataSpec(b) b.ResetTimer() for i := 0; i < b.N; i++ { spec.Copy() } } golang-github-cilium-ebpf-0.11.0/btf/btf_types.go000066400000000000000000000205051456504511400216220ustar00rootroot00000000000000package btf import ( "encoding/binary" "fmt" "io" "unsafe" ) //go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind // btfKind describes a Type. type btfKind uint8 // Equivalents of the BTF_KIND_* constants. const ( kindUnknown btfKind = iota // Unknown kindInt // Int kindPointer // Pointer kindArray // Array kindStruct // Struct kindUnion // Union kindEnum // Enum kindForward // Forward kindTypedef // Typedef kindVolatile // Volatile kindConst // Const kindRestrict // Restrict // Added ~4.20 kindFunc // Func kindFuncProto // FuncProto // Added ~5.1 kindVar // Var kindDatasec // Datasec // Added ~5.13 kindFloat // Float // Added 5.16 kindDeclTag // DeclTag kindTypeTag // TypeTag // Added 6.0 kindEnum64 // Enum64 ) // FuncLinkage describes BTF function linkage metadata. type FuncLinkage int // Equivalent of enum btf_func_linkage. const ( StaticFunc FuncLinkage = iota // static GlobalFunc // global ExternFunc // extern ) // VarLinkage describes BTF variable linkage metadata. type VarLinkage int const ( StaticVar VarLinkage = iota // static GlobalVar // global ExternVar // extern ) const ( btfTypeKindShift = 24 btfTypeKindLen = 5 btfTypeVlenShift = 0 btfTypeVlenMask = 16 btfTypeKindFlagShift = 31 btfTypeKindFlagMask = 1 ) var btfTypeLen = binary.Size(btfType{}) // btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. type btfType struct { NameOff uint32 /* "info" bits arrangement * bits 0-15: vlen (e.g. # of struct's members), linkage * bits 16-23: unused * bits 24-28: kind (e.g. int, ptr, array...etc) * bits 29-30: unused * bit 31: kind_flag, currently used by * struct, union and fwd */ Info uint32 /* "size" is used by INT, ENUM, STRUCT and UNION. * "size" tells the size of the type it is describing. * * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, * FUNC and FUNC_PROTO. * "type" is a type_id referring to another type. */ SizeType uint32 } func mask(len uint32) uint32 { return (1 << len) - 1 } func readBits(value, len, shift uint32) uint32 { return (value >> shift) & mask(len) } func writeBits(value, len, shift, new uint32) uint32 { value &^= mask(len) << shift value |= (new & mask(len)) << shift return value } func (bt *btfType) info(len, shift uint32) uint32 { return readBits(bt.Info, len, shift) } func (bt *btfType) setInfo(value, len, shift uint32) { bt.Info = writeBits(bt.Info, len, shift, value) } func (bt *btfType) Kind() btfKind { return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift)) } func (bt *btfType) SetKind(kind btfKind) { bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift) } func (bt *btfType) Vlen() int { return int(bt.info(btfTypeVlenMask, btfTypeVlenShift)) } func (bt *btfType) SetVlen(vlen int) { bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) } func (bt *btfType) kindFlagBool() bool { return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1 } func (bt *btfType) setKindFlagBool(set bool) { var value uint32 if set { value = 1 } bt.setInfo(value, btfTypeKindFlagMask, btfTypeKindFlagShift) } // Bitfield returns true if the struct or union contain a bitfield. func (bt *btfType) Bitfield() bool { return bt.kindFlagBool() } func (bt *btfType) SetBitfield(isBitfield bool) { bt.setKindFlagBool(isBitfield) } func (bt *btfType) FwdKind() FwdKind { return FwdKind(bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift)) } func (bt *btfType) SetFwdKind(kind FwdKind) { bt.setInfo(uint32(kind), btfTypeKindFlagMask, btfTypeKindFlagShift) } func (bt *btfType) Signed() bool { return bt.kindFlagBool() } func (bt *btfType) SetSigned(signed bool) { bt.setKindFlagBool(signed) } func (bt *btfType) Linkage() FuncLinkage { return FuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift)) } func (bt *btfType) SetLinkage(linkage FuncLinkage) { bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift) } func (bt *btfType) Type() TypeID { // TODO: Panic here if wrong kind? return TypeID(bt.SizeType) } func (bt *btfType) SetType(id TypeID) { bt.SizeType = uint32(id) } func (bt *btfType) Size() uint32 { // TODO: Panic here if wrong kind? return bt.SizeType } func (bt *btfType) SetSize(size uint32) { bt.SizeType = size } func (bt *btfType) Marshal(w io.Writer, bo binary.ByteOrder) error { buf := make([]byte, unsafe.Sizeof(*bt)) bo.PutUint32(buf[0:], bt.NameOff) bo.PutUint32(buf[4:], bt.Info) bo.PutUint32(buf[8:], bt.SizeType) _, err := w.Write(buf) return err } type rawType struct { btfType data interface{} } func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error { if err := rt.btfType.Marshal(w, bo); err != nil { return err } if rt.data == nil { return nil } return binary.Write(w, bo, rt.data) } // btfInt encodes additional data for integers. // // ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b // ? = undefined // e = encoding // o = offset (bitfields?) // b = bits (bitfields) type btfInt struct { Raw uint32 } const ( btfIntEncodingLen = 4 btfIntEncodingShift = 24 btfIntOffsetLen = 8 btfIntOffsetShift = 16 btfIntBitsLen = 8 btfIntBitsShift = 0 ) func (bi btfInt) Encoding() IntEncoding { return IntEncoding(readBits(bi.Raw, btfIntEncodingLen, btfIntEncodingShift)) } func (bi *btfInt) SetEncoding(e IntEncoding) { bi.Raw = writeBits(uint32(bi.Raw), btfIntEncodingLen, btfIntEncodingShift, uint32(e)) } func (bi btfInt) Offset() Bits { return Bits(readBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift)) } func (bi *btfInt) SetOffset(offset uint32) { bi.Raw = writeBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift, offset) } func (bi btfInt) Bits() Bits { return Bits(readBits(bi.Raw, btfIntBitsLen, btfIntBitsShift)) } func (bi *btfInt) SetBits(bits byte) { bi.Raw = writeBits(bi.Raw, btfIntBitsLen, btfIntBitsShift, uint32(bits)) } type btfArray struct { Type TypeID IndexType TypeID Nelems uint32 } type btfMember struct { NameOff uint32 Type TypeID Offset uint32 } type btfVarSecinfo struct { Type TypeID Offset uint32 Size uint32 } type btfVariable struct { Linkage uint32 } type btfEnum struct { NameOff uint32 Val uint32 } type btfEnum64 struct { NameOff uint32 ValLo32 uint32 ValHi32 uint32 } type btfParam struct { NameOff uint32 Type TypeID } type btfDeclTag struct { ComponentIdx uint32 } func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, error) { var header btfType // because of the interleaving between types and struct members it is difficult to // precompute the numbers of raw types this will parse // this "guess" is a good first estimation sizeOfbtfType := uintptr(btfTypeLen) tyMaxCount := uintptr(typeLen) / sizeOfbtfType / 2 types := make([]rawType, 0, tyMaxCount) for id := TypeID(1); ; id++ { if err := binary.Read(r, bo, &header); err == io.EOF { return types, nil } else if err != nil { return nil, fmt.Errorf("can't read type info for id %v: %v", id, err) } var data interface{} switch header.Kind() { case kindInt: data = new(btfInt) case kindPointer: case kindArray: data = new(btfArray) case kindStruct: fallthrough case kindUnion: data = make([]btfMember, header.Vlen()) case kindEnum: data = make([]btfEnum, header.Vlen()) case kindForward: case kindTypedef: case kindVolatile: case kindConst: case kindRestrict: case kindFunc: case kindFuncProto: data = make([]btfParam, header.Vlen()) case kindVar: data = new(btfVariable) case kindDatasec: data = make([]btfVarSecinfo, header.Vlen()) case kindFloat: case kindDeclTag: data = new(btfDeclTag) case kindTypeTag: case kindEnum64: data = make([]btfEnum64, header.Vlen()) default: return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind()) } if data == nil { types = append(types, rawType{header, nil}) continue } if err := binary.Read(r, bo, data); err != nil { return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) } types = append(types, rawType{header, data}) } } golang-github-cilium-ebpf-0.11.0/btf/btf_types_string.go000066400000000000000000000045301456504511400232100ustar00rootroot00000000000000// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind"; DO NOT EDIT. package btf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[StaticFunc-0] _ = x[GlobalFunc-1] _ = x[ExternFunc-2] } const _FuncLinkage_name = "staticglobalextern" var _FuncLinkage_index = [...]uint8{0, 6, 12, 18} func (i FuncLinkage) String() string { if i < 0 || i >= FuncLinkage(len(_FuncLinkage_index)-1) { return "FuncLinkage(" + strconv.FormatInt(int64(i), 10) + ")" } return _FuncLinkage_name[_FuncLinkage_index[i]:_FuncLinkage_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[StaticVar-0] _ = x[GlobalVar-1] _ = x[ExternVar-2] } const _VarLinkage_name = "staticglobalextern" var _VarLinkage_index = [...]uint8{0, 6, 12, 18} func (i VarLinkage) String() string { if i < 0 || i >= VarLinkage(len(_VarLinkage_index)-1) { return "VarLinkage(" + strconv.FormatInt(int64(i), 10) + ")" } return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[kindUnknown-0] _ = x[kindInt-1] _ = x[kindPointer-2] _ = x[kindArray-3] _ = x[kindStruct-4] _ = x[kindUnion-5] _ = x[kindEnum-6] _ = x[kindForward-7] _ = x[kindTypedef-8] _ = x[kindVolatile-9] _ = x[kindConst-10] _ = x[kindRestrict-11] _ = x[kindFunc-12] _ = x[kindFuncProto-13] _ = x[kindVar-14] _ = x[kindDatasec-15] _ = x[kindFloat-16] _ = x[kindDeclTag-17] _ = x[kindTypeTag-18] _ = x[kindEnum64-19] } const _btfKind_name = "UnknownIntPointerArrayStructUnionEnumForwardTypedefVolatileConstRestrictFuncFuncProtoVarDatasecFloatDeclTagTypeTagEnum64" var _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114, 120} func (i btfKind) String() string { if i >= btfKind(len(_btfKind_index)-1) { return "btfKind(" + strconv.FormatInt(int64(i), 10) + ")" } return _btfKind_name[_btfKind_index[i]:_btfKind_index[i+1]] } golang-github-cilium-ebpf-0.11.0/btf/core.go000066400000000000000000000712731456504511400205630ustar00rootroot00000000000000package btf import ( "encoding/binary" "errors" "fmt" "math" "reflect" "strconv" "strings" "github.com/cilium/ebpf/asm" ) // Code in this file is derived from libbpf, which is available under a BSD // 2-Clause license. // COREFixup is the result of computing a CO-RE relocation for a target. type COREFixup struct { kind coreKind local uint32 target uint32 // True if there is no valid fixup. The instruction is replaced with an // invalid dummy. poison bool // True if the validation of the local value should be skipped. Used by // some kinds of bitfield relocations. skipLocalValidation bool } func (f *COREFixup) equal(other COREFixup) bool { return f.local == other.local && f.target == other.target } func (f *COREFixup) String() string { if f.poison { return fmt.Sprintf("%s=poison", f.kind) } return fmt.Sprintf("%s=%d->%d", f.kind, f.local, f.target) } func (f *COREFixup) Apply(ins *asm.Instruction) error { if f.poison { const badRelo = 0xbad2310 *ins = asm.BuiltinFunc(badRelo).Call() return nil } switch class := ins.OpCode.Class(); class { case asm.LdXClass, asm.StClass, asm.StXClass: if want := int16(f.local); !f.skipLocalValidation && want != ins.Offset { return fmt.Errorf("invalid offset %d, expected %d", ins.Offset, f.local) } if f.target > math.MaxInt16 { return fmt.Errorf("offset %d exceeds MaxInt16", f.target) } ins.Offset = int16(f.target) case asm.LdClass: if !ins.IsConstantLoad(asm.DWord) { return fmt.Errorf("not a dword-sized immediate load") } if want := int64(f.local); !f.skipLocalValidation && want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d (fixup: %v)", ins.Constant, want, f) } ins.Constant = int64(f.target) case asm.ALUClass: if ins.OpCode.ALUOp() == asm.Swap { return fmt.Errorf("relocation against swap") } fallthrough case asm.ALU64Class: if src := ins.OpCode.Source(); src != asm.ImmSource { return fmt.Errorf("invalid source %s", src) } if want := int64(f.local); !f.skipLocalValidation && want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d (fixup: %v, kind: %v, ins: %v)", ins.Constant, want, f, f.kind, ins) } if f.target > math.MaxInt32 { return fmt.Errorf("immediate %d exceeds MaxInt32", f.target) } ins.Constant = int64(f.target) default: return fmt.Errorf("invalid class %s", class) } return nil } func (f COREFixup) isNonExistant() bool { return f.kind.checksForExistence() && f.target == 0 } // coreKind is the type of CO-RE relocation as specified in BPF source code. type coreKind uint32 const ( reloFieldByteOffset coreKind = iota /* field byte offset */ reloFieldByteSize /* field size in bytes */ reloFieldExists /* field existence in target kernel */ reloFieldSigned /* field signedness (0 - unsigned, 1 - signed) */ reloFieldLShiftU64 /* bitfield-specific left bitshift */ reloFieldRShiftU64 /* bitfield-specific right bitshift */ reloTypeIDLocal /* type ID in local BPF object */ reloTypeIDTarget /* type ID in target kernel */ reloTypeExists /* type existence in target kernel */ reloTypeSize /* type size in bytes */ reloEnumvalExists /* enum value existence in target kernel */ reloEnumvalValue /* enum value integer value */ ) func (k coreKind) checksForExistence() bool { return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists } func (k coreKind) String() string { switch k { case reloFieldByteOffset: return "byte_off" case reloFieldByteSize: return "byte_sz" case reloFieldExists: return "field_exists" case reloFieldSigned: return "signed" case reloFieldLShiftU64: return "lshift_u64" case reloFieldRShiftU64: return "rshift_u64" case reloTypeIDLocal: return "local_type_id" case reloTypeIDTarget: return "target_type_id" case reloTypeExists: return "type_exists" case reloTypeSize: return "type_size" case reloEnumvalExists: return "enumval_exists" case reloEnumvalValue: return "enumval_value" default: return "unknown" } } // CORERelocate calculates changes needed to adjust eBPF instructions for differences // in types. // // Returns a list of fixups which can be applied to instructions to make them // match the target type(s). // // Fixups are returned in the order of relos, e.g. fixup[i] is the solution // for relos[i]. func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([]COREFixup, error) { if target == nil { var err error target, _, err = kernelSpec() if err != nil { return nil, fmt.Errorf("load kernel spec: %w", err) } } if bo != target.byteOrder { return nil, fmt.Errorf("can't relocate %s against %s", bo, target.byteOrder) } type reloGroup struct { relos []*CORERelocation // Position of each relocation in relos. indices []int } // Split relocations into per Type lists. relosByType := make(map[Type]*reloGroup) result := make([]COREFixup, len(relos)) for i, relo := range relos { if relo.kind == reloTypeIDLocal { // Filtering out reloTypeIDLocal here makes our lives a lot easier // down the line, since it doesn't have a target at all. if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) } result[i] = COREFixup{ kind: relo.kind, local: uint32(relo.id), // NB: Using relo.id as the target here is incorrect, since // it doesn't match the BTF we generate on the fly. This isn't // too bad for now since there are no uses of the local type ID // in the kernel, yet. target: uint32(relo.id), } continue } group, ok := relosByType[relo.typ] if !ok { group = &reloGroup{} relosByType[relo.typ] = group } group.relos = append(group.relos, relo) group.indices = append(group.indices, i) } for localType, group := range relosByType { localTypeName := localType.TypeName() if localTypeName == "" { return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) } targets := target.namedTypes[newEssentialName(localTypeName)] fixups, err := coreCalculateFixups(group.relos, target, targets, bo) if err != nil { return nil, fmt.Errorf("relocate %s: %w", localType, err) } for j, index := range group.indices { result[index] = fixups[j] } } return result, nil } var errAmbiguousRelocation = errors.New("ambiguous relocation") var errImpossibleRelocation = errors.New("impossible relocation") var errIncompatibleTypes = errors.New("incompatible types") // coreCalculateFixups finds the target type that best matches all relocations. // // All relos must target the same type. // // The best target is determined by scoring: the less poisoning we have to do // the better the target is. func coreCalculateFixups(relos []*CORERelocation, targetSpec *Spec, targets []Type, bo binary.ByteOrder) ([]COREFixup, error) { bestScore := len(relos) var bestFixups []COREFixup for _, target := range targets { targetID, err := targetSpec.TypeID(target) if err != nil { return nil, fmt.Errorf("target type ID: %w", err) } score := 0 // lower is better fixups := make([]COREFixup, 0, len(relos)) for _, relo := range relos { fixup, err := coreCalculateFixup(relo, target, targetID, bo) if err != nil { return nil, fmt.Errorf("target %s: %s: %w", target, relo.kind, err) } if fixup.poison || fixup.isNonExistant() { score++ } fixups = append(fixups, fixup) } if score > bestScore { // We have a better target already, ignore this one. continue } if score < bestScore { // This is the best target yet, use it. bestScore = score bestFixups = fixups continue } // Some other target has the same score as the current one. Make sure // the fixups agree with each other. for i, fixup := range bestFixups { if !fixup.equal(fixups[i]) { return nil, fmt.Errorf("%s: multiple types match: %w", fixup.kind, errAmbiguousRelocation) } } } if bestFixups == nil { // Nothing at all matched, probably because there are no suitable // targets at all. // // Poison everything except checksForExistence. bestFixups = make([]COREFixup, len(relos)) for i, relo := range relos { if relo.kind.checksForExistence() { bestFixups[i] = COREFixup{kind: relo.kind, local: 1, target: 0} } else { bestFixups[i] = COREFixup{kind: relo.kind, poison: true} } } } return bestFixups, nil } var errNoSignedness = errors.New("no signedness") // coreCalculateFixup calculates the fixup for a single local type, target type // and relocation. func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo binary.ByteOrder) (COREFixup, error) { fixup := func(local, target uint32) (COREFixup, error) { return COREFixup{kind: relo.kind, local: local, target: target}, nil } fixupWithoutValidation := func(local, target uint32) (COREFixup, error) { return COREFixup{kind: relo.kind, local: local, target: target, skipLocalValidation: true}, nil } poison := func() (COREFixup, error) { if relo.kind.checksForExistence() { return fixup(1, 0) } return COREFixup{kind: relo.kind, poison: true}, nil } zero := COREFixup{} local := relo.typ switch relo.kind { case reloTypeIDTarget, reloTypeSize, reloTypeExists: if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return zero, fmt.Errorf("unexpected accessor %v", relo.accessor) } err := coreAreTypesCompatible(local, target) if errors.Is(err, errIncompatibleTypes) { return poison() } if err != nil { return zero, err } switch relo.kind { case reloTypeExists: return fixup(1, 1) case reloTypeIDTarget: return fixup(uint32(relo.id), uint32(targetID)) case reloTypeSize: localSize, err := Sizeof(local) if err != nil { return zero, err } targetSize, err := Sizeof(target) if err != nil { return zero, err } return fixup(uint32(localSize), uint32(targetSize)) } case reloEnumvalValue, reloEnumvalExists: localValue, targetValue, err := coreFindEnumValue(local, relo.accessor, target) if errors.Is(err, errImpossibleRelocation) { return poison() } if err != nil { return zero, err } switch relo.kind { case reloEnumvalExists: return fixup(1, 1) case reloEnumvalValue: return fixup(uint32(localValue.Value), uint32(targetValue.Value)) } case reloFieldByteOffset, reloFieldByteSize, reloFieldExists, reloFieldLShiftU64, reloFieldRShiftU64, reloFieldSigned: if _, ok := as[*Fwd](target); ok { // We can't relocate fields using a forward declaration, so // skip it. If a non-forward declaration is present in the BTF // we'll find it in one of the other iterations. return poison() } localField, targetField, err := coreFindField(local, relo.accessor, target) if errors.Is(err, errImpossibleRelocation) { return poison() } if err != nil { return zero, err } maybeSkipValidation := func(f COREFixup, err error) (COREFixup, error) { f.skipLocalValidation = localField.bitfieldSize > 0 return f, err } switch relo.kind { case reloFieldExists: return fixup(1, 1) case reloFieldByteOffset: return maybeSkipValidation(fixup(localField.offset, targetField.offset)) case reloFieldByteSize: localSize, err := Sizeof(localField.Type) if err != nil { return zero, err } targetSize, err := Sizeof(targetField.Type) if err != nil { return zero, err } return maybeSkipValidation(fixup(uint32(localSize), uint32(targetSize))) case reloFieldLShiftU64: var target uint32 if bo == binary.LittleEndian { targetSize, err := targetField.sizeBits() if err != nil { return zero, err } target = uint32(64 - targetField.bitfieldOffset - targetSize) } else { loadWidth, err := Sizeof(targetField.Type) if err != nil { return zero, err } target = uint32(64 - Bits(loadWidth*8) + targetField.bitfieldOffset) } return fixupWithoutValidation(0, target) case reloFieldRShiftU64: targetSize, err := targetField.sizeBits() if err != nil { return zero, err } return fixupWithoutValidation(0, uint32(64-targetSize)) case reloFieldSigned: switch local := UnderlyingType(localField.Type).(type) { case *Enum: target, ok := as[*Enum](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Enum but %T", targetField.Type) } return fixup(boolToUint32(local.Signed), boolToUint32(target.Signed)) case *Int: target, ok := as[*Int](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Int but %T", targetField.Type) } return fixup( uint32(local.Encoding&Signed), uint32(target.Encoding&Signed), ) default: return zero, fmt.Errorf("type %T: %w", local, errNoSignedness) } } } return zero, ErrNotSupported } func boolToUint32(val bool) uint32 { if val { return 1 } return 0 } /* coreAccessor contains a path through a struct. It contains at least one index. * * The interpretation depends on the kind of the relocation. The following is * taken from struct bpf_core_relo in libbpf_internal.h: * * - for field-based relocations, string encodes an accessed field using * a sequence of field and array indices, separated by colon (:). It's * conceptually very close to LLVM's getelementptr ([0]) instruction's * arguments for identifying offset to a field. * - for type-based relocations, strings is expected to be just "0"; * - for enum value-based relocations, string contains an index of enum * value within its enum type; * * Example to provide a better feel. * * struct sample { * int a; * struct { * int b[10]; * }; * }; * * struct sample s = ...; * int x = &s->a; // encoded as "0:0" (a is field #0) * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1, * // b is field #0 inside anon struct, accessing elem #5) * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array) */ type coreAccessor []int func parseCOREAccessor(accessor string) (coreAccessor, error) { if accessor == "" { return nil, fmt.Errorf("empty accessor") } parts := strings.Split(accessor, ":") result := make(coreAccessor, 0, len(parts)) for _, part := range parts { // 31 bits to avoid overflowing int on 32 bit platforms. index, err := strconv.ParseUint(part, 10, 31) if err != nil { return nil, fmt.Errorf("accessor index %q: %s", part, err) } result = append(result, int(index)) } return result, nil } func (ca coreAccessor) String() string { strs := make([]string, 0, len(ca)) for _, i := range ca { strs = append(strs, strconv.Itoa(i)) } return strings.Join(strs, ":") } func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) { e, ok := as[*Enum](t) if !ok { return nil, fmt.Errorf("not an enum: %s", t) } if len(ca) > 1 { return nil, fmt.Errorf("invalid accessor %s for enum", ca) } i := ca[0] if i >= len(e.Values) { return nil, fmt.Errorf("invalid index %d for %s", i, e) } return &e.Values[i], nil } // coreField represents the position of a "child" of a composite type from the // start of that type. // // /- start of composite // | offset * 8 | bitfieldOffset | bitfieldSize | ... | // \- start of field end of field -/ type coreField struct { Type Type // The position of the field from the start of the composite type in bytes. offset uint32 // The offset of the bitfield in bits from the start of the field. bitfieldOffset Bits // The size of the bitfield in bits. // // Zero if the field is not a bitfield. bitfieldSize Bits } func (cf *coreField) adjustOffsetToNthElement(n int) error { if n == 0 { return nil } size, err := Sizeof(cf.Type) if err != nil { return err } cf.offset += uint32(n) * uint32(size) return nil } func (cf *coreField) adjustOffsetBits(offset Bits) error { align, err := alignof(cf.Type) if err != nil { return err } // We can compute the load offset by: // 1) converting the bit offset to bytes with a flooring division. // 2) dividing and multiplying that offset by the alignment, yielding the // load size aligned offset. offsetBytes := uint32(offset/8) / uint32(align) * uint32(align) // The number of bits remaining is the bit offset less the number of bits // we can "skip" with the aligned offset. cf.bitfieldOffset = offset - Bits(offsetBytes*8) // We know that cf.offset is aligned at to at least align since we get it // from the compiler via BTF. Adding an aligned offsetBytes preserves the // alignment. cf.offset += offsetBytes return nil } func (cf *coreField) sizeBits() (Bits, error) { if cf.bitfieldSize > 0 { return cf.bitfieldSize, nil } // Someone is trying to access a non-bitfield via a bit shift relocation. // This happens when a field changes from a bitfield to a regular field // between kernel versions. Synthesise the size to make the shifts work. size, err := Sizeof(cf.Type) if err != nil { return 0, err } return Bits(size * 8), nil } // coreFindField descends into the local type using the accessor and tries to // find an equivalent field in target at each step. // // Returns the field and the offset of the field from the start of // target in bits. func coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, coreField, error) { local := coreField{Type: localT} target := coreField{Type: targetT} if err := coreAreMembersCompatible(local.Type, target.Type); err != nil { return coreField{}, coreField{}, fmt.Errorf("fields: %w", err) } // The first index is used to offset a pointer of the base type like // when accessing an array. if err := local.adjustOffsetToNthElement(localAcc[0]); err != nil { return coreField{}, coreField{}, err } if err := target.adjustOffsetToNthElement(localAcc[0]); err != nil { return coreField{}, coreField{}, err } var localMaybeFlex, targetMaybeFlex bool for i, acc := range localAcc[1:] { switch localType := UnderlyingType(local.Type).(type) { case composite: // For composite types acc is used to find the field in the local type, // and then we try to find a field in target with the same name. localMembers := localType.members() if acc >= len(localMembers) { return coreField{}, coreField{}, fmt.Errorf("invalid accessor %d for %s", acc, localType) } localMember := localMembers[acc] if localMember.Name == "" { localMemberType, ok := as[composite](localMember.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("unnamed field with type %s: %s", localMember.Type, ErrNotSupported) } // This is an anonymous struct or union, ignore it. local = coreField{ Type: localMemberType, offset: local.offset + localMember.Offset.Bytes(), } localMaybeFlex = false continue } targetType, ok := as[composite](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not composite: %w", errImpossibleRelocation) } targetMember, last, err := coreFindMember(targetType, localMember.Name) if err != nil { return coreField{}, coreField{}, err } local = coreField{ Type: localMember.Type, offset: local.offset, bitfieldSize: localMember.BitfieldSize, } localMaybeFlex = acc == len(localMembers)-1 target = coreField{ Type: targetMember.Type, offset: target.offset, bitfieldSize: targetMember.BitfieldSize, } targetMaybeFlex = last if local.bitfieldSize == 0 && target.bitfieldSize == 0 { local.offset += localMember.Offset.Bytes() target.offset += targetMember.Offset.Bytes() break } // Either of the members is a bitfield. Make sure we're at the // end of the accessor. if next := i + 1; next < len(localAcc[1:]) { return coreField{}, coreField{}, fmt.Errorf("can't descend into bitfield") } if err := local.adjustOffsetBits(localMember.Offset); err != nil { return coreField{}, coreField{}, err } if err := target.adjustOffsetBits(targetMember.Offset); err != nil { return coreField{}, coreField{}, err } case *Array: // For arrays, acc is the index in the target. targetType, ok := as[*Array](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not array: %w", errImpossibleRelocation) } if localType.Nelems == 0 && !localMaybeFlex { return coreField{}, coreField{}, fmt.Errorf("local type has invalid flexible array") } if targetType.Nelems == 0 && !targetMaybeFlex { return coreField{}, coreField{}, fmt.Errorf("target type has invalid flexible array") } if localType.Nelems > 0 && acc >= int(localType.Nelems) { return coreField{}, coreField{}, fmt.Errorf("invalid access of %s at index %d", localType, acc) } if targetType.Nelems > 0 && acc >= int(targetType.Nelems) { return coreField{}, coreField{}, fmt.Errorf("out of bounds access of target: %w", errImpossibleRelocation) } local = coreField{ Type: localType.Type, offset: local.offset, } localMaybeFlex = false if err := local.adjustOffsetToNthElement(acc); err != nil { return coreField{}, coreField{}, err } target = coreField{ Type: targetType.Type, offset: target.offset, } targetMaybeFlex = false if err := target.adjustOffsetToNthElement(acc); err != nil { return coreField{}, coreField{}, err } default: return coreField{}, coreField{}, fmt.Errorf("relocate field of %T: %w", localType, ErrNotSupported) } if err := coreAreMembersCompatible(local.Type, target.Type); err != nil { return coreField{}, coreField{}, err } } return local, target, nil } // coreFindMember finds a member in a composite type while handling anonymous // structs and unions. func coreFindMember(typ composite, name string) (Member, bool, error) { if name == "" { return Member{}, false, errors.New("can't search for anonymous member") } type offsetTarget struct { composite offset Bits } targets := []offsetTarget{{typ, 0}} visited := make(map[composite]bool) for i := 0; i < len(targets); i++ { target := targets[i] // Only visit targets once to prevent infinite recursion. if visited[target] { continue } if len(visited) >= maxTypeDepth { // This check is different than libbpf, which restricts the entire // path to BPF_CORE_SPEC_MAX_LEN items. return Member{}, false, fmt.Errorf("type is nested too deep") } visited[target] = true members := target.members() for j, member := range members { if member.Name == name { // NB: This is safe because member is a copy. member.Offset += target.offset return member, j == len(members)-1, nil } // The names don't match, but this member could be an anonymous struct // or union. if member.Name != "" { continue } comp, ok := as[composite](member.Type) if !ok { return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) } targets = append(targets, offsetTarget{comp, target.offset + member.Offset}) } } return Member{}, false, fmt.Errorf("no matching member: %w", errImpossibleRelocation) } // coreFindEnumValue follows localAcc to find the equivalent enum value in target. func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localValue, targetValue *EnumValue, _ error) { localValue, err := localAcc.enumValue(local) if err != nil { return nil, nil, err } targetEnum, ok := as[*Enum](target) if !ok { return nil, nil, errImpossibleRelocation } localName := newEssentialName(localValue.Name) for i, targetValue := range targetEnum.Values { if newEssentialName(targetValue.Name) != localName { continue } return localValue, &targetEnum.Values[i], nil } return nil, nil, errImpossibleRelocation } // CheckTypeCompatibility checks local and target types for Compatibility according to CO-RE rules. // // Only layout compatibility is checked, ignoring names of the root type. func CheckTypeCompatibility(localType Type, targetType Type) error { return coreAreTypesCompatible(localType, targetType) } /* The comment below is from bpf_core_types_are_compat in libbpf.c: * * Check local and target types for compatibility. This check is used for * type-based CO-RE relocations and follow slightly different rules than * field-based relocations. This function assumes that root types were already * checked for name match. Beyond that initial root-level name check, names * are completely ignored. Compatibility rules are as follows: * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but * kind should match for local and target types (i.e., STRUCT is not * compatible with UNION); * - for ENUMs, the size is ignored; * - for INT, size and signedness are ignored; * - for ARRAY, dimensionality is ignored, element types are checked for * compatibility recursively; * - CONST/VOLATILE/RESTRICT modifiers are ignored; * - TYPEDEFs/PTRs are compatible if types they pointing to are compatible; * - FUNC_PROTOs are compatible if they have compatible signature: same * number of input args and compatible return and argument types. * These rules are not set in stone and probably will be adjusted as we get * more experience with using BPF CO-RE relocations. * * Returns errIncompatibleTypes if types are not compatible. */ func coreAreTypesCompatible(localType Type, targetType Type) error { var ( localTs, targetTs typeDeque l, t = &localType, &targetType depth = 0 ) for ; l != nil && t != nil; l, t = localTs.Shift(), targetTs.Shift() { if depth >= maxTypeDepth { return errors.New("types are nested too deep") } localType = UnderlyingType(*l) targetType = UnderlyingType(*t) if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch: %w", errIncompatibleTypes) } switch lv := (localType).(type) { case *Void, *Struct, *Union, *Enum, *Fwd, *Int: // Nothing to do here case *Pointer, *Array: depth++ walkType(localType, localTs.Push) walkType(targetType, targetTs.Push) case *FuncProto: tv := targetType.(*FuncProto) if len(lv.Params) != len(tv.Params) { return fmt.Errorf("function param mismatch: %w", errIncompatibleTypes) } depth++ walkType(localType, localTs.Push) walkType(targetType, targetTs.Push) default: return fmt.Errorf("unsupported type %T", localType) } } if l != nil { return fmt.Errorf("dangling local type %T", *l) } if t != nil { return fmt.Errorf("dangling target type %T", *t) } return nil } /* coreAreMembersCompatible checks two types for field-based relocation compatibility. * * The comment below is from bpf_core_fields_are_compat in libbpf.c: * * Check two types for compatibility for the purpose of field access * relocation. const/volatile/restrict and typedefs are skipped to ensure we * are relocating semantically compatible entities: * - any two STRUCTs/UNIONs are compatible and can be mixed; * - any two FWDs are compatible, if their names match (modulo flavor suffix); * - any two PTRs are always compatible; * - for ENUMs, names should be the same (ignoring flavor suffix) or at * least one of enums should be anonymous; * - for ENUMs, check sizes, names are ignored; * - for INT, size and signedness are ignored; * - any two FLOATs are always compatible; * - for ARRAY, dimensionality is ignored, element types are checked for * compatibility recursively; * [ NB: coreAreMembersCompatible doesn't recurse, this check is done * by coreFindField. ] * - everything else shouldn't be ever a target of relocation. * These rules are not set in stone and probably will be adjusted as we get * more experience with using BPF CO-RE relocations. * * Returns errImpossibleRelocation if the members are not compatible. */ func coreAreMembersCompatible(localType Type, targetType Type) error { localType = UnderlyingType(localType) targetType = UnderlyingType(targetType) doNamesMatch := func(a, b string) error { if a == "" || b == "" { // allow anonymous and named type to match return nil } if newEssentialName(a) == newEssentialName(b) { return nil } return fmt.Errorf("names don't match: %w", errImpossibleRelocation) } _, lok := localType.(composite) _, tok := targetType.(composite) if lok && tok { return nil } if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch: %w", errImpossibleRelocation) } switch lv := localType.(type) { case *Array, *Pointer, *Float, *Int: return nil case *Enum: tv := targetType.(*Enum) return doNamesMatch(lv.Name, tv.Name) case *Fwd: tv := targetType.(*Fwd) return doNamesMatch(lv.Name, tv.Name) default: return fmt.Errorf("type %s: %w", localType, ErrNotSupported) } } golang-github-cilium-ebpf-0.11.0/btf/core_reloc_test.go000066400000000000000000000046041456504511400230000ustar00rootroot00000000000000package btf_test import ( "fmt" "io" "os" "strings" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestCORERelocationLoad(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) { fh, err := os.Open(file) if err != nil { t.Fatal(err) } defer fh.Close() spec, err := ebpf.LoadCollectionSpecFromReader(fh) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } for _, progSpec := range spec.Programs { t.Run(progSpec.Name, func(t *testing.T) { if _, err := fh.Seek(0, io.SeekStart); err != nil { t.Fatal(err) } prog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{ KernelTypes: spec.Types, }) if strings.HasPrefix(progSpec.Name, "err_") { if err == nil { prog.Close() t.Fatal("Expected an error") } t.Log("Got expected error:", err) return } if err != nil { t.Fatal("Load program:", err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error when running:", err) } if ret != 0 { t.Error("Assertion failed on line", ret) } }) } }) } func TestCORERelocationRead(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs_read-*.elf"), func(t *testing.T, file string) { spec, err := ebpf.LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } targetFile := fmt.Sprintf("testdata/relocs_read_tgt-%s.elf", internal.ClangEndian) targetSpec, err := btf.LoadSpec(targetFile) if err != nil { t.Fatal(err) } for _, progSpec := range spec.Programs { t.Run(progSpec.Name, func(t *testing.T) { prog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{ KernelTypes: targetSpec, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Load program:", err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error when running:", err) } if ret != 0 { t.Error("Assertion failed on line", ret) } }) } }) } golang-github-cilium-ebpf-0.11.0/btf/core_test.go000066400000000000000000000465141456504511400216220ustar00rootroot00000000000000package btf import ( "errors" "fmt" "os" "strings" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/google/go-cmp/cmp" qt "github.com/frankban/quicktest" ) func TestCheckTypeCompatibility(t *testing.T) { tests := []struct { a, b Type compatible bool }{ {&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Int{}}, true}, {&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Void{}}, false}, } for _, test := range tests { err := CheckTypeCompatibility(test.a, test.b) if test.compatible { if err != nil { t.Errorf("Expected types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } err = CheckTypeCompatibility(test.b, test.a) if test.compatible { if err != nil { t.Errorf("Expected reversed types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } } func TestCOREAreTypesCompatible(t *testing.T) { tests := []struct { a, b Type compatible bool }{ {&Void{}, &Void{}, true}, {&Struct{Name: "a"}, &Struct{Name: "b"}, true}, {&Union{Name: "a"}, &Union{Name: "b"}, true}, {&Union{Name: "a"}, &Struct{Name: "b"}, false}, {&Enum{Name: "a"}, &Enum{Name: "b"}, true}, {&Fwd{Name: "a"}, &Fwd{Name: "b"}, true}, {&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true}, {&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true}, {&Pointer{Target: &Void{}}, &Void{}, false}, {&Array{Index: &Void{}, Type: &Void{}}, &Array{Index: &Void{}, Type: &Void{}}, true}, {&Array{Index: &Void{}, Type: &Int{}}, &Array{Index: &Void{}, Type: &Void{}}, false}, {&FuncProto{Return: &Int{}}, &FuncProto{Return: &Void{}}, false}, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "a", Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "b", Type: &Void{}}}}, true, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Int{}}}}, false, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}, {Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}}, false, }, } for _, test := range tests { err := coreAreTypesCompatible(test.a, test.b) if test.compatible { if err != nil { t.Errorf("Expected types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } err = coreAreTypesCompatible(test.b, test.a) if test.compatible { if err != nil { t.Errorf("Expected reversed types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } for _, invalid := range []Type{&Var{}, &Datasec{}} { err := coreAreTypesCompatible(invalid, invalid) if errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected an error for %T, not errIncompatibleTypes", invalid) } else if err == nil { t.Errorf("Expected an error for %T", invalid) } } } func TestCOREAreMembersCompatible(t *testing.T) { tests := []struct { a, b Type compatible bool }{ {&Struct{Name: "a"}, &Struct{Name: "b"}, true}, {&Union{Name: "a"}, &Union{Name: "b"}, true}, {&Union{Name: "a"}, &Struct{Name: "b"}, true}, {&Enum{Name: "a"}, &Enum{Name: "b"}, false}, {&Enum{Name: "a"}, &Enum{Name: "a___foo"}, true}, {&Enum{Name: "a"}, &Enum{Name: ""}, true}, {&Fwd{Name: "a"}, &Fwd{Name: "b"}, false}, {&Fwd{Name: "a"}, &Fwd{Name: "a___foo"}, true}, {&Fwd{Name: "a"}, &Fwd{Name: ""}, true}, {&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true}, {&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true}, {&Pointer{Target: &Void{}}, &Void{}, false}, {&Array{Type: &Int{Size: 1}}, &Array{Type: &Int{Encoding: Signed}}, true}, {&Float{Size: 2}, &Float{Size: 4}, true}, } for _, test := range tests { err := coreAreMembersCompatible(test.a, test.b) if test.compatible { if err != nil { t.Errorf("Expected members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } err = coreAreMembersCompatible(test.b, test.a) if test.compatible { if err != nil { t.Errorf("Expected reversed members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected reversed members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } for _, invalid := range []Type{&Void{}, &FuncProto{}, &Var{}, &Datasec{}} { err := coreAreMembersCompatible(invalid, invalid) if errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected an error for %T, not errImpossibleRelocation", invalid) } else if err == nil { t.Errorf("Expected an error for %T", invalid) } } } func TestCOREAccessor(t *testing.T) { for _, valid := range []string{ "0", "1:0", "1:0:3:34:10:1", } { _, err := parseCOREAccessor(valid) if err != nil { t.Errorf("Parse %q: %s", valid, err) } } for _, invalid := range []string{ "", "-1", ":", "0:", ":12", "4294967296", } { _, err := parseCOREAccessor(invalid) if err == nil { t.Errorf("Accepted invalid accessor %q", invalid) } } } func TestCOREFindEnumValue(t *testing.T) { a := &Enum{Values: []EnumValue{{"foo", 23}, {"bar", 42}}} b := &Enum{Values: []EnumValue{ {"foo___flavour", 0}, {"bar", 123}, {"garbage", 3}, }} invalid := []struct { name string local Type target Type acc coreAccessor err error }{ {"o-o-b accessor", a, b, coreAccessor{len(a.Values)}, nil}, {"long accessor", a, b, coreAccessor{0, 1}, nil}, {"wrong target", a, &Void{}, coreAccessor{0, 1}, nil}, { "no matching value", b, a, coreAccessor{2}, errImpossibleRelocation, }, } for _, test := range invalid { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindEnumValue(test.local, test.acc, test.target) if test.err != nil && !errors.Is(err, test.err) { t.Fatalf("Expected %s, got %s", test.err, err) } if err == nil { t.Fatal("Accepted invalid case") } }) } valid := []struct { name string local, target Type acc coreAccessor localValue, targetValue uint64 }{ {"a to b", a, b, coreAccessor{0}, 23, 0}, {"b to a", b, a, coreAccessor{1}, 123, 42}, } for _, test := range valid { t.Run(test.name, func(t *testing.T) { local, target, err := coreFindEnumValue(test.local, test.acc, test.target) qt.Assert(t, err, qt.IsNil) qt.Check(t, local.Value, qt.Equals, test.localValue) qt.Check(t, target.Value, qt.Equals, test.targetValue) }) } } func TestCOREFindField(t *testing.T) { ptr := &Pointer{} u16 := &Int{Size: 2} u32 := &Int{Size: 4} aFields := []Member{ {Name: "foo", Type: ptr, Offset: 8}, {Name: "bar", Type: u16, Offset: 16}, {Name: "baz", Type: u32, Offset: 32, BitfieldSize: 3}, {Name: "quux", Type: u32, Offset: 35, BitfieldSize: 10}, {Name: "quuz", Type: u32, Offset: 45, BitfieldSize: 8}, } bFields := []Member{ {Name: "foo", Type: ptr, Offset: 16}, {Name: "bar", Type: u32, Offset: 8}, {Name: "other", Offset: 4}, // baz is separated out from the other bitfields {Name: "baz", Type: u32, Offset: 64, BitfieldSize: 3}, // quux's type changes u32->u16 {Name: "quux", Type: u16, Offset: 96, BitfieldSize: 10}, // quuz becomes a normal field {Name: "quuz", Type: u16, Offset: 112}, } aStruct := &Struct{Members: aFields, Size: 48} bStruct := &Struct{Members: bFields, Size: 80} aArray := &Array{Nelems: 4, Type: u16} bArray := &Array{Nelems: 3, Type: u32} invalid := []struct { name string local, target Type acc coreAccessor err error }{ { "unsupported type", &Void{}, &Void{}, coreAccessor{0, 0}, ErrNotSupported, }, { "different types", &Union{}, &Array{Type: u16}, coreAccessor{0}, errImpossibleRelocation, }, { "invalid composite accessor", aStruct, aStruct, coreAccessor{0, len(aStruct.Members)}, nil, }, { "invalid array accessor", aArray, aArray, coreAccessor{0, int(aArray.Nelems)}, nil, }, { "o-o-b array accessor", aArray, bArray, coreAccessor{0, int(bArray.Nelems)}, errImpossibleRelocation, }, { "no match", bStruct, aStruct, coreAccessor{0, 2}, errImpossibleRelocation, }, { "incompatible match", &Union{Members: []Member{{Name: "foo", Type: &Pointer{}}}}, &Union{Members: []Member{{Name: "foo", Type: &Int{}}}}, coreAccessor{0, 0}, errImpossibleRelocation, }, { "unsized type", bStruct, &Func{}, // non-zero accessor to force calculating the offset. coreAccessor{1}, errImpossibleRelocation, }, } for _, test := range invalid { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindField(test.local, test.acc, test.target) if test.err != nil && !errors.Is(err, test.err) { t.Fatalf("Expected %s, got %s", test.err, err) } if err == nil { t.Fatal("Accepted invalid case") } t.Log(err) }) } bytes := func(typ Type) uint32 { sz, err := Sizeof(typ) if err != nil { t.Fatal(err) } return uint32(sz) } anon := func(t Type, offset Bits) []Member { return []Member{{Type: t, Offset: offset}} } anonStruct := func(m ...Member) Member { return Member{Type: &Struct{Members: m}} } anonUnion := func(m ...Member) Member { return Member{Type: &Union{Members: m}} } valid := []struct { name string local Type target Type acc coreAccessor localField, targetField coreField }{ { "array[0]", aArray, bArray, coreAccessor{0, 0}, coreField{u16, 0, 0, 0}, coreField{u32, 0, 0, 0}, }, { "array[1]", aArray, bArray, coreAccessor{0, 1}, coreField{u16, bytes(aArray.Type), 0, 0}, coreField{u32, bytes(bArray.Type), 0, 0}, }, { "array[0] with base offset", aArray, bArray, coreAccessor{1, 0}, coreField{u16, bytes(aArray), 0, 0}, coreField{u32, bytes(bArray), 0, 0}, }, { "array[2] with base offset", aArray, bArray, coreAccessor{1, 2}, coreField{u16, bytes(aArray) + 2*bytes(aArray.Type), 0, 0}, coreField{u32, bytes(bArray) + 2*bytes(bArray.Type), 0, 0}, }, { "flex array", &Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u16}}}}, &Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u32}}}}, coreAccessor{0, 0, 9000}, coreField{u16, bytes(u16) * 9000, 0, 0}, coreField{u32, bytes(u32) * 9000, 0, 0}, }, { "struct.0", aStruct, bStruct, coreAccessor{0, 0}, coreField{ptr, 1, 0, 0}, coreField{ptr, 2, 0, 0}, }, { "struct.0 anon", aStruct, &Struct{Members: anon(bStruct, 24)}, coreAccessor{0, 0}, coreField{ptr, 1, 0, 0}, coreField{ptr, 3 + 2, 0, 0}, }, { "struct.0 with base offset", aStruct, bStruct, coreAccessor{3, 0}, coreField{ptr, 3*bytes(aStruct) + 1, 0, 0}, coreField{ptr, 3*bytes(bStruct) + 2, 0, 0}, }, { "struct.1", aStruct, bStruct, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 1, 0, 0}, }, { "struct.1 anon", aStruct, &Struct{Members: anon(bStruct, 24)}, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 3 + 1, 0, 0}, }, { "union.1", &Union{Members: aFields, Size: 32}, &Union{Members: bFields, Size: 32}, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 1, 0, 0}, }, { "interchangeable composites", &Struct{ Members: []Member{ anonStruct(anonUnion(Member{Name: "_1", Type: u16})), }, }, &Struct{ Members: []Member{ anonUnion(anonStruct(Member{Name: "_1", Type: u16})), }, }, coreAccessor{0, 0, 0, 0}, coreField{u16, 0, 0, 0}, coreField{u16, 0, 0, 0}, }, { "struct.2 (bitfield baz)", aStruct, bStruct, coreAccessor{0, 2}, coreField{u32, 4, 0, 3}, coreField{u32, 8, 0, 3}, }, { "struct.3 (bitfield quux)", aStruct, bStruct, coreAccessor{0, 3}, coreField{u32, 4, 3, 10}, coreField{u16, 12, 0, 10}, }, { "struct.4 (bitfield quuz)", aStruct, bStruct, coreAccessor{0, 4}, coreField{u32, 4, 13, 8}, coreField{u16, 14, 0, 0}, }, } allowCoreField := cmp.AllowUnexported(coreField{}) checkCOREField := func(t *testing.T, which string, got, want coreField) { t.Helper() if diff := cmp.Diff(want, got, allowCoreField); diff != "" { t.Errorf("%s mismatch (-want +got):\n%s", which, diff) } } for _, test := range valid { t.Run(test.name, func(t *testing.T) { localField, targetField, err := coreFindField(test.local, test.acc, test.target) qt.Assert(t, err, qt.IsNil) checkCOREField(t, "local", localField, test.localField) checkCOREField(t, "target", targetField, test.targetField) }) } } func TestCOREFindFieldCyclical(t *testing.T) { members := []Member{{Name: "foo", Type: &Pointer{}}} cyclicStruct := &Struct{} cyclicStruct.Members = []Member{{Type: cyclicStruct}} cyclicUnion := &Union{} cyclicUnion.Members = []Member{{Type: cyclicUnion}} cyclicArray := &Array{Nelems: 1} cyclicArray.Type = &Pointer{Target: cyclicArray} tests := []struct { name string local, cyclic Type }{ {"struct", &Struct{Members: members}, cyclicStruct}, {"union", &Union{Members: members}, cyclicUnion}, {"array", &Array{Nelems: 2, Type: &Int{}}, cyclicArray}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindField(test.local, coreAccessor{0, 0}, test.cyclic) if !errors.Is(err, errImpossibleRelocation) { t.Fatal("Should return errImpossibleRelocation, got", err) } }) } } func TestCORERelocation(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/*.elf"), func(t *testing.T, file string) { rd, err := os.Open(file) if err != nil { t.Fatal(err) } defer rd.Close() spec, extInfos, err := LoadSpecAndExtInfosFromReader(rd) if err != nil { t.Fatal(err) } if extInfos == nil { t.Skip("No ext_infos") } errs := map[string]error{ "err_ambiguous": errAmbiguousRelocation, "err_ambiguous_flavour": errAmbiguousRelocation, } for section := range extInfos.funcInfos { name := strings.TrimPrefix(section, "socket_filter/") t.Run(name, func(t *testing.T) { var relos []*CORERelocation for _, reloInfo := range extInfos.relocationInfos[section] { relos = append(relos, reloInfo.relo) } fixups, err := CORERelocate(relos, spec, spec.byteOrder) if want := errs[name]; want != nil { if !errors.Is(err, want) { t.Fatal("Expected", want, "got", err) } return } if err != nil { t.Fatal("Can't relocate against itself:", err) } for offset, fixup := range fixups { if want := fixup.local; !fixup.skipLocalValidation && want != fixup.target { // Since we're relocating against ourselves both values // should match. t.Errorf("offset %d: local %v doesn't match target %d (kind %s)", offset, fixup.local, fixup.target, fixup.kind) } } }) } }) } func TestCORECopyWithoutQualifiers(t *testing.T) { qualifiers := []struct { name string fn func(Type) Type }{ {"const", func(t Type) Type { return &Const{Type: t} }}, {"volatile", func(t Type) Type { return &Volatile{Type: t} }}, {"restrict", func(t Type) Type { return &Restrict{Type: t} }}, {"typedef", func(t Type) Type { return &Typedef{Type: t} }}, } for _, test := range qualifiers { t.Run(test.name+" cycle", func(t *testing.T) { root := &Volatile{} root.Type = test.fn(root) cycle, ok := Copy(root, UnderlyingType).(*cycle) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, cycle.root, qt.Equals, root) }) } for _, a := range qualifiers { for _, b := range qualifiers { t.Run(a.name+" "+b.name, func(t *testing.T) { v := a.fn(&Pointer{Target: b.fn(&Int{Name: "z"})}) want := &Pointer{Target: &Int{Name: "z"}} got := Copy(v, UnderlyingType) qt.Assert(t, got, qt.DeepEquals, want) }) } } t.Run("long chain", func(t *testing.T) { root := &Int{Name: "abc"} v := Type(root) for i := 0; i < maxTypeDepth; i++ { q := qualifiers[testutils.Rand().Intn(len(qualifiers))] v = q.fn(v) t.Log(q.name) } got := Copy(v, UnderlyingType) qt.Assert(t, got, qt.DeepEquals, root) }) } func TestCOREReloFieldSigned(t *testing.T) { for _, typ := range []Type{&Int{}, &Enum{}} { t.Run(fmt.Sprintf("%T with invalid target", typ), func(t *testing.T) { relo := &CORERelocation{ typ, coreAccessor{0}, reloFieldSigned, 0, } fixup, err := coreCalculateFixup(relo, &Void{}, 0, internal.NativeEndian) qt.Assert(t, fixup.poison, qt.IsTrue) qt.Assert(t, err, qt.IsNil) }) } t.Run("type without signedness", func(t *testing.T) { relo := &CORERelocation{ &Array{}, coreAccessor{0}, reloFieldSigned, 0, } _, err := coreCalculateFixup(relo, &Array{}, 0, internal.NativeEndian) qt.Assert(t, err, qt.ErrorIs, errNoSignedness) }) } func TestCOREReloFieldShiftU64(t *testing.T) { typ := &Struct{ Members: []Member{ {Name: "A", Type: &Fwd{}}, }, } for _, relo := range []*CORERelocation{ {typ, coreAccessor{0, 0}, reloFieldRShiftU64, 1}, {typ, coreAccessor{0, 0}, reloFieldLShiftU64, 1}, } { t.Run(relo.kind.String(), func(t *testing.T) { _, err := coreCalculateFixup(relo, typ, 1, internal.NativeEndian) qt.Assert(t, err, qt.ErrorIs, errUnsizedType) }) } } func BenchmarkCORESkBuff(b *testing.B) { spec := vmlinuxTestdataSpec(b) var skb *Struct err := spec.TypeByName("sk_buff", &skb) qt.Assert(b, err, qt.IsNil) skbID, err := spec.TypeID(skb) qt.Assert(b, err, qt.IsNil) var pktHashTypes *Enum err = spec.TypeByName("pkt_hash_types", &pktHashTypes) qt.Assert(b, err, qt.IsNil) pktHashTypesID, err := spec.TypeID(pktHashTypes) qt.Assert(b, err, qt.IsNil) for _, relo := range []*CORERelocation{ {skb, coreAccessor{0, 0}, reloFieldByteOffset, skbID}, {skb, coreAccessor{0, 0}, reloFieldByteSize, skbID}, {skb, coreAccessor{0, 0}, reloFieldExists, skbID}, {skb, coreAccessor{0, 0}, reloFieldSigned, skbID}, {skb, coreAccessor{0, 0}, reloFieldLShiftU64, skbID}, {skb, coreAccessor{0, 0}, reloFieldRShiftU64, skbID}, {skb, coreAccessor{0}, reloTypeIDLocal, skbID}, {skb, coreAccessor{0}, reloTypeIDTarget, skbID}, {skb, coreAccessor{0}, reloTypeExists, skbID}, {skb, coreAccessor{0}, reloTypeSize, skbID}, {pktHashTypes, coreAccessor{0}, reloEnumvalExists, pktHashTypesID}, {pktHashTypes, coreAccessor{0}, reloEnumvalValue, pktHashTypesID}, } { b.Run(relo.kind.String(), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, err = CORERelocate([]*CORERelocation{relo}, spec, spec.byteOrder) if err != nil { b.Fatal(err) } } }) } } golang-github-cilium-ebpf-0.11.0/btf/doc.go000066400000000000000000000003441456504511400203670ustar00rootroot00000000000000// Package btf handles data encoded according to the BPF Type Format. // // The canonical documentation lives in the Linux kernel repository and is // available at https://www.kernel.org/doc/html/latest/bpf/btf.html package btf golang-github-cilium-ebpf-0.11.0/btf/ext_info.go000066400000000000000000000516121456504511400214410ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" "sort" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" ) // ExtInfos contains ELF section metadata. type ExtInfos struct { // The slices are sorted by offset in ascending order. funcInfos map[string][]funcInfo lineInfos map[string][]lineInfo relocationInfos map[string][]coreRelocationInfo } // loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF. // // Returns an error wrapping ErrNotFound if no ext infos are present. func loadExtInfosFromELF(file *internal.SafeELFFile, spec *Spec) (*ExtInfos, error) { section := file.Section(".BTF.ext") if section == nil { return nil, fmt.Errorf("btf ext infos: %w", ErrNotFound) } if section.ReaderAt == nil { return nil, fmt.Errorf("compressed ext_info is not supported") } return loadExtInfos(section.ReaderAt, file.ByteOrder, spec, spec.strings) } // loadExtInfos parses bare ext infos. func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec, strings *stringTable) (*ExtInfos, error) { // Open unbuffered section reader. binary.Read() calls io.ReadFull on // the header structs, resulting in one syscall per header. headerRd := io.NewSectionReader(r, 0, math.MaxInt64) extHeader, err := parseBTFExtHeader(headerRd, bo) if err != nil { return nil, fmt.Errorf("parsing BTF extension header: %w", err) } coreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader) if err != nil { return nil, fmt.Errorf("parsing BTF CO-RE header: %w", err) } buf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen)) btfFuncInfos, err := parseFuncInfos(buf, bo, strings) if err != nil { return nil, fmt.Errorf("parsing BTF function info: %w", err) } funcInfos := make(map[string][]funcInfo, len(btfFuncInfos)) for section, bfis := range btfFuncInfos { funcInfos[section], err = newFuncInfos(bfis, spec) if err != nil { return nil, fmt.Errorf("section %s: func infos: %w", section, err) } } buf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen)) btfLineInfos, err := parseLineInfos(buf, bo, strings) if err != nil { return nil, fmt.Errorf("parsing BTF line info: %w", err) } lineInfos := make(map[string][]lineInfo, len(btfLineInfos)) for section, blis := range btfLineInfos { lineInfos[section], err = newLineInfos(blis, strings) if err != nil { return nil, fmt.Errorf("section %s: line infos: %w", section, err) } } if coreHeader == nil || coreHeader.COREReloLen == 0 { return &ExtInfos{funcInfos, lineInfos, nil}, nil } var btfCORERelos map[string][]bpfCORERelo buf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen)) btfCORERelos, err = parseCORERelos(buf, bo, strings) if err != nil { return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err) } coreRelos := make(map[string][]coreRelocationInfo, len(btfCORERelos)) for section, brs := range btfCORERelos { coreRelos[section], err = newRelocationInfos(brs, spec, strings) if err != nil { return nil, fmt.Errorf("section %s: CO-RE relocations: %w", section, err) } } return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil } type funcInfoMeta struct{} type coreRelocationMeta struct{} // Assign per-section metadata from BTF to a section's instructions. func (ei *ExtInfos) Assign(insns asm.Instructions, section string) { funcInfos := ei.funcInfos[section] lineInfos := ei.lineInfos[section] reloInfos := ei.relocationInfos[section] iter := insns.Iterate() for iter.Next() { if len(funcInfos) > 0 && funcInfos[0].offset == iter.Offset { *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos[0].fn) funcInfos = funcInfos[1:] } if len(lineInfos) > 0 && lineInfos[0].offset == iter.Offset { *iter.Ins = iter.Ins.WithSource(lineInfos[0].line) lineInfos = lineInfos[1:] } if len(reloInfos) > 0 && reloInfos[0].offset == iter.Offset { iter.Ins.Metadata.Set(coreRelocationMeta{}, reloInfos[0].relo) reloInfos = reloInfos[1:] } } } // MarshalExtInfos encodes function and line info embedded in insns into kernel // wire format. // // Returns ErrNotSupported if the kernel doesn't support BTF-associated programs. func MarshalExtInfos(insns asm.Instructions) (_ *Handle, funcInfos, lineInfos []byte, _ error) { // Bail out early if the kernel doesn't support Func(Proto). If this is the // case, func_info will also be unsupported. if err := haveProgBTF(); err != nil { return nil, nil, nil, err } iter := insns.Iterate() for iter.Next() { _, ok := iter.Ins.Source().(*Line) fn := FuncMetadata(iter.Ins) if ok || fn != nil { goto marshal } } return nil, nil, nil, nil marshal: var b Builder var fiBuf, liBuf bytes.Buffer for { if fn := FuncMetadata(iter.Ins); fn != nil { fi := &funcInfo{ fn: fn, offset: iter.Offset, } if err := fi.marshal(&fiBuf, &b); err != nil { return nil, nil, nil, fmt.Errorf("write func info: %w", err) } } if line, ok := iter.Ins.Source().(*Line); ok { li := &lineInfo{ line: line, offset: iter.Offset, } if err := li.marshal(&liBuf, &b); err != nil { return nil, nil, nil, fmt.Errorf("write line info: %w", err) } } if !iter.Next() { break } } handle, err := NewHandle(&b) return handle, fiBuf.Bytes(), liBuf.Bytes(), err } // btfExtHeader is found at the start of the .BTF.ext section. type btfExtHeader struct { Magic uint16 Version uint8 Flags uint8 // HdrLen is larger than the size of struct btfExtHeader when it is // immediately followed by a btfExtCOREHeader. HdrLen uint32 FuncInfoOff uint32 FuncInfoLen uint32 LineInfoOff uint32 LineInfoLen uint32 } // parseBTFExtHeader parses the header of the .BTF.ext section. func parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) { var header btfExtHeader if err := binary.Read(r, bo, &header); err != nil { return nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { return nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { return nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, fmt.Errorf("unsupported flags %v", header.Flags) } if int64(header.HdrLen) < int64(binary.Size(&header)) { return nil, fmt.Errorf("header length shorter than btfExtHeader size") } return &header, nil } // funcInfoStart returns the offset from the beginning of the .BTF.ext section // to the start of its func_info entries. func (h *btfExtHeader) funcInfoStart() int64 { return int64(h.HdrLen + h.FuncInfoOff) } // lineInfoStart returns the offset from the beginning of the .BTF.ext section // to the start of its line_info entries. func (h *btfExtHeader) lineInfoStart() int64 { return int64(h.HdrLen + h.LineInfoOff) } // coreReloStart returns the offset from the beginning of the .BTF.ext section // to the start of its CO-RE relocation entries. func (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 { return int64(h.HdrLen + ch.COREReloOff) } // btfExtCOREHeader is found right after the btfExtHeader when its HdrLen // field is larger than its size. type btfExtCOREHeader struct { COREReloOff uint32 COREReloLen uint32 } // parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional // header bytes are present, extHeader.HdrLen will be larger than the struct, // indicating the presence of a CO-RE extension header. func parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) { extHdrSize := int64(binary.Size(&extHeader)) remainder := int64(extHeader.HdrLen) - extHdrSize if remainder == 0 { return nil, nil } var coreHeader btfExtCOREHeader if err := binary.Read(r, bo, &coreHeader); err != nil { return nil, fmt.Errorf("can't read header: %v", err) } return &coreHeader, nil } type btfExtInfoSec struct { SecNameOff uint32 NumInfo uint32 } // parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext, // appearing within func_info and line_info sub-sections. // These headers appear once for each program section in the ELF and are // followed by one or more func/line_info records for the section. func parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) { var infoHeader btfExtInfoSec if err := binary.Read(r, bo, &infoHeader); err != nil { return "", nil, fmt.Errorf("read ext info header: %w", err) } secName, err := strings.Lookup(infoHeader.SecNameOff) if err != nil { return "", nil, fmt.Errorf("get section name: %w", err) } if secName == "" { return "", nil, fmt.Errorf("extinfo header refers to empty section name") } if infoHeader.NumInfo == 0 { return "", nil, fmt.Errorf("section %s has zero records", secName) } return secName, &infoHeader, nil } // parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos // or line_infos segment that describes the length of all extInfoRecords in // that segment. func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) { const maxRecordSize = 256 var recordSize uint32 if err := binary.Read(r, bo, &recordSize); err != nil { return 0, fmt.Errorf("can't read record size: %v", err) } if recordSize < 4 { // Need at least InsnOff worth of bytes per record. return 0, errors.New("record size too short") } if recordSize > maxRecordSize { return 0, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize) } return recordSize, nil } // The size of a FuncInfo in BTF wire format. var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{})) type funcInfo struct { fn *Func offset asm.RawInstructionOffset } type bpfFuncInfo struct { // Instruction offset of the function within an ELF section. InsnOff uint32 TypeID TypeID } func newFuncInfo(fi bpfFuncInfo, spec *Spec) (*funcInfo, error) { typ, err := spec.TypeByID(fi.TypeID) if err != nil { return nil, err } fn, ok := typ.(*Func) if !ok { return nil, fmt.Errorf("type ID %d is a %T, but expected a Func", fi.TypeID, typ) } // C doesn't have anonymous functions, but check just in case. if fn.Name == "" { return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID) } return &funcInfo{ fn, asm.RawInstructionOffset(fi.InsnOff), }, nil } func newFuncInfos(bfis []bpfFuncInfo, spec *Spec) ([]funcInfo, error) { fis := make([]funcInfo, 0, len(bfis)) for _, bfi := range bfis { fi, err := newFuncInfo(bfi, spec) if err != nil { return nil, fmt.Errorf("offset %d: %w", bfi.InsnOff, err) } fis = append(fis, *fi) } sort.Slice(fis, func(i, j int) bool { return fis[i].offset <= fis[j].offset }) return fis, nil } // marshal into the BTF wire format. func (fi *funcInfo) marshal(w *bytes.Buffer, b *Builder) error { id, err := b.Add(fi.fn) if err != nil { return err } bfi := bpfFuncInfo{ InsnOff: uint32(fi.offset), TypeID: id, } buf := make([]byte, FuncInfoSize) internal.NativeEndian.PutUint32(buf, bfi.InsnOff) internal.NativeEndian.PutUint32(buf[4:], uint32(bfi.TypeID)) _, err = w.Write(buf) return err } // parseFuncInfos parses a func_info sub-section within .BTF.ext ito a map of // func infos indexed by section name. func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } result := make(map[string][]bpfFuncInfo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseFuncInfoRecords parses a stream of func_infos into a funcInfos. // These records appear after a btf_ext_info_sec header in the func_info // sub-section of .BTF.ext. func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfFuncInfo, error) { var out []bpfFuncInfo var fi bpfFuncInfo if exp, got := FuncInfoSize, recordSize; exp != got { // BTF blob's record size is longer than we know how to parse. return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got) } for i := uint32(0); i < recordNum; i++ { if err := binary.Read(r, bo, &fi); err != nil { return nil, fmt.Errorf("can't read function info: %v", err) } if fi.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. fi.InsnOff /= asm.InstructionSize out = append(out, fi) } return out, nil } var LineInfoSize = uint32(binary.Size(bpfLineInfo{})) // Line represents the location and contents of a single line of source // code a BPF ELF was compiled from. type Line struct { fileName string line string lineNumber uint32 lineColumn uint32 } func (li *Line) FileName() string { return li.fileName } func (li *Line) Line() string { return li.line } func (li *Line) LineNumber() uint32 { return li.lineNumber } func (li *Line) LineColumn() uint32 { return li.lineColumn } func (li *Line) String() string { return li.line } type lineInfo struct { line *Line offset asm.RawInstructionOffset } // Constants for the format of bpfLineInfo.LineCol. const ( bpfLineShift = 10 bpfLineMax = (1 << (32 - bpfLineShift)) - 1 bpfColumnMax = (1 << bpfLineShift) - 1 ) type bpfLineInfo struct { // Instruction offset of the line within the whole instruction stream, in instructions. InsnOff uint32 FileNameOff uint32 LineOff uint32 LineCol uint32 } func newLineInfo(li bpfLineInfo, strings *stringTable) (*lineInfo, error) { line, err := strings.Lookup(li.LineOff) if err != nil { return nil, fmt.Errorf("lookup of line: %w", err) } fileName, err := strings.Lookup(li.FileNameOff) if err != nil { return nil, fmt.Errorf("lookup of filename: %w", err) } lineNumber := li.LineCol >> bpfLineShift lineColumn := li.LineCol & bpfColumnMax return &lineInfo{ &Line{ fileName, line, lineNumber, lineColumn, }, asm.RawInstructionOffset(li.InsnOff), }, nil } func newLineInfos(blis []bpfLineInfo, strings *stringTable) ([]lineInfo, error) { lis := make([]lineInfo, 0, len(blis)) for _, bli := range blis { li, err := newLineInfo(bli, strings) if err != nil { return nil, fmt.Errorf("offset %d: %w", bli.InsnOff, err) } lis = append(lis, *li) } sort.Slice(lis, func(i, j int) bool { return lis[i].offset <= lis[j].offset }) return lis, nil } // marshal writes the binary representation of the LineInfo to w. func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error { line := li.line if line.lineNumber > bpfLineMax { return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax) } if line.lineColumn > bpfColumnMax { return fmt.Errorf("column %d exceeds %d", line.lineColumn, bpfColumnMax) } fileNameOff, err := b.addString(line.fileName) if err != nil { return fmt.Errorf("file name %q: %w", line.fileName, err) } lineOff, err := b.addString(line.line) if err != nil { return fmt.Errorf("line %q: %w", line.line, err) } bli := bpfLineInfo{ uint32(li.offset), fileNameOff, lineOff, (line.lineNumber << bpfLineShift) | line.lineColumn, } buf := make([]byte, LineInfoSize) internal.NativeEndian.PutUint32(buf, bli.InsnOff) internal.NativeEndian.PutUint32(buf[4:], bli.FileNameOff) internal.NativeEndian.PutUint32(buf[8:], bli.LineOff) internal.NativeEndian.PutUint32(buf[12:], bli.LineCol) _, err = w.Write(buf) return err } // parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of // line infos indexed by section name. func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } result := make(map[string][]bpfLineInfo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseLineInfoRecords parses a stream of line_infos into a lineInfos. // These records appear after a btf_ext_info_sec header in the line_info // sub-section of .BTF.ext. func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfLineInfo, error) { var out []bpfLineInfo var li bpfLineInfo if exp, got := uint32(binary.Size(li)), recordSize; exp != got { // BTF blob's record size is longer than we know how to parse. return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got) } for i := uint32(0); i < recordNum; i++ { if err := binary.Read(r, bo, &li); err != nil { return nil, fmt.Errorf("can't read line info: %v", err) } if li.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. li.InsnOff /= asm.InstructionSize out = append(out, li) } return out, nil } // bpfCORERelo matches the kernel's struct bpf_core_relo. type bpfCORERelo struct { InsnOff uint32 TypeID TypeID AccessStrOff uint32 Kind coreKind } type CORERelocation struct { // The local type of the relocation, stripped of typedefs and qualifiers. typ Type accessor coreAccessor kind coreKind // The ID of the local type in the source BTF. id TypeID } func (cr *CORERelocation) String() string { return fmt.Sprintf("CORERelocation(%s, %s[%s], local_id=%d)", cr.kind, cr.typ, cr.accessor, cr.id) } func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation { relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation) return relo } type coreRelocationInfo struct { relo *CORERelocation offset asm.RawInstructionOffset } func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*coreRelocationInfo, error) { typ, err := spec.TypeByID(relo.TypeID) if err != nil { return nil, err } accessorStr, err := strings.Lookup(relo.AccessStrOff) if err != nil { return nil, err } accessor, err := parseCOREAccessor(accessorStr) if err != nil { return nil, fmt.Errorf("accessor %q: %s", accessorStr, err) } return &coreRelocationInfo{ &CORERelocation{ typ, accessor, relo.Kind, relo.TypeID, }, asm.RawInstructionOffset(relo.InsnOff), }, nil } func newRelocationInfos(brs []bpfCORERelo, spec *Spec, strings *stringTable) ([]coreRelocationInfo, error) { rs := make([]coreRelocationInfo, 0, len(brs)) for _, br := range brs { relo, err := newRelocationInfo(br, spec, strings) if err != nil { return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err) } rs = append(rs, *relo) } sort.Slice(rs, func(i, j int) bool { return rs[i].offset < rs[j].offset }) return rs, nil } var extInfoReloSize = binary.Size(bpfCORERelo{}) // parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of // CO-RE relocations indexed by section name. func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } if recordSize != uint32(extInfoReloSize) { return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize) } result := make(map[string][]bpfCORERelo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseCOREReloRecords parses a stream of CO-RE relocation entries into a // coreRelos. These records appear after a btf_ext_info_sec header in the // core_relos sub-section of .BTF.ext. func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) { var out []bpfCORERelo var relo bpfCORERelo for i := uint32(0); i < recordNum; i++ { if err := binary.Read(r, bo, &relo); err != nil { return nil, fmt.Errorf("can't read CO-RE relocation: %v", err) } if relo.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", relo.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. relo.InsnOff /= asm.InstructionSize out = append(out, relo) } return out, nil } golang-github-cilium-ebpf-0.11.0/btf/ext_info_test.go000066400000000000000000000011771456504511400225010ustar00rootroot00000000000000package btf import ( "bytes" "strings" "testing" "github.com/cilium/ebpf/internal" ) func TestParseExtInfoBigRecordSize(t *testing.T) { rd := strings.NewReader("\xff\xff\xff\xff\x00\x00\x00\x000709171295166016") table, err := readStringTable(bytes.NewReader([]byte{0}), nil) if err != nil { t.Fatal(err) } if _, err := parseFuncInfos(rd, internal.NativeEndian, table); err == nil { t.Error("Parsing func info with large record size doesn't return an error") } if _, err := parseLineInfos(rd, internal.NativeEndian, table); err == nil { t.Error("Parsing line info with large record size doesn't return an error") } } golang-github-cilium-ebpf-0.11.0/btf/format.go000066400000000000000000000166061456504511400211220ustar00rootroot00000000000000package btf import ( "errors" "fmt" "strings" ) var errNestedTooDeep = errors.New("nested too deep") // GoFormatter converts a Type to Go syntax. // // A zero GoFormatter is valid to use. type GoFormatter struct { w strings.Builder // Types present in this map are referred to using the given name if they // are encountered when outputting another type. Names map[Type]string // Identifier is called for each field of struct-like types. By default the // field name is used as is. Identifier func(string) string // EnumIdentifier is called for each element of an enum. By default the // name of the enum type is concatenated with Identifier(element). EnumIdentifier func(name, element string) string } // TypeDeclaration generates a Go type declaration for a BTF type. func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) { gf.w.Reset() if err := gf.writeTypeDecl(name, typ); err != nil { return "", err } return gf.w.String(), nil } func (gf *GoFormatter) identifier(s string) string { if gf.Identifier != nil { return gf.Identifier(s) } return s } func (gf *GoFormatter) enumIdentifier(name, element string) string { if gf.EnumIdentifier != nil { return gf.EnumIdentifier(name, element) } return name + gf.identifier(element) } // writeTypeDecl outputs a declaration of the given type. // // It encodes https://golang.org/ref/spec#Type_declarations: // // type foo struct { bar uint32; } // type bar int32 func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error { if name == "" { return fmt.Errorf("need a name for type %s", typ) } typ = skipQualifiers(typ) fmt.Fprintf(&gf.w, "type %s ", name) if err := gf.writeTypeLit(typ, 0); err != nil { return err } e, ok := typ.(*Enum) if !ok || len(e.Values) == 0 { return nil } gf.w.WriteString("; const ( ") for _, ev := range e.Values { id := gf.enumIdentifier(name, ev.Name) fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value) } gf.w.WriteString(")") return nil } // writeType outputs the name of a named type or a literal describing the type. // // It encodes https://golang.org/ref/spec#Types. // // foo (if foo is a named type) // uint32 func (gf *GoFormatter) writeType(typ Type, depth int) error { typ = skipQualifiers(typ) name := gf.Names[typ] if name != "" { gf.w.WriteString(name) return nil } return gf.writeTypeLit(typ, depth) } // writeTypeLit outputs a literal describing the type. // // The function ignores named types. // // It encodes https://golang.org/ref/spec#TypeLit. // // struct { bar uint32; } // uint32 func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error { depth++ if depth > maxTypeDepth { return errNestedTooDeep } var err error switch v := skipQualifiers(typ).(type) { case *Int: err = gf.writeIntLit(v) case *Enum: if !v.Signed { gf.w.WriteRune('u') } switch v.Size { case 1: gf.w.WriteString("int8") case 2: gf.w.WriteString("int16") case 4: gf.w.WriteString("int32") case 8: gf.w.WriteString("int64") default: err = fmt.Errorf("invalid enum size %d", v.Size) } case *Typedef: err = gf.writeType(v.Type, depth) case *Array: fmt.Fprintf(&gf.w, "[%d]", v.Nelems) err = gf.writeType(v.Type, depth) case *Struct: err = gf.writeStructLit(v.Size, v.Members, depth) case *Union: // Always choose the first member to represent the union in Go. err = gf.writeStructLit(v.Size, v.Members[:1], depth) case *Datasec: err = gf.writeDatasecLit(v, depth) default: return fmt.Errorf("type %T: %w", v, ErrNotSupported) } if err != nil { return fmt.Errorf("%s: %w", typ, err) } return nil } func (gf *GoFormatter) writeIntLit(i *Int) error { bits := i.Size * 8 switch i.Encoding { case Bool: if i.Size != 1 { return fmt.Errorf("bool with size %d", i.Size) } gf.w.WriteString("bool") case Char: if i.Size != 1 { return fmt.Errorf("char with size %d", i.Size) } // BTF doesn't have a way to specify the signedness of a char. Assume // we are dealing with unsigned, since this works nicely with []byte // in Go code. fallthrough case Unsigned, Signed: stem := "uint" if i.Encoding == Signed { stem = "int" } if i.Size > 8 { fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8) } else { fmt.Fprintf(&gf.w, "%s%d", stem, bits) } default: return fmt.Errorf("can't encode %s", i.Encoding) } return nil } func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error { gf.w.WriteString("struct { ") prevOffset := uint32(0) skippedBitfield := false for i, m := range members { if m.BitfieldSize > 0 { skippedBitfield = true continue } offset := m.Offset.Bytes() if n := offset - prevOffset; skippedBitfield && n > 0 { fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n) } else { gf.writePadding(n) } fieldSize, err := Sizeof(m.Type) if err != nil { return fmt.Errorf("field %d: %w", i, err) } prevOffset = offset + uint32(fieldSize) if prevOffset > size { return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size) } if err := gf.writeStructField(m, depth); err != nil { return fmt.Errorf("field %d: %w", i, err) } } gf.writePadding(size - prevOffset) gf.w.WriteString("}") return nil } func (gf *GoFormatter) writeStructField(m Member, depth int) error { if m.BitfieldSize > 0 { return fmt.Errorf("bitfields are not supported") } if m.Offset%8 != 0 { return fmt.Errorf("unsupported offset %d", m.Offset) } if m.Name == "" { // Special case a nested anonymous union like // struct foo { union { int bar; int baz }; } // by replacing the whole union with its first member. union, ok := m.Type.(*Union) if !ok { return fmt.Errorf("anonymous fields are not supported") } if len(union.Members) == 0 { return errors.New("empty anonymous union") } depth++ if depth > maxTypeDepth { return errNestedTooDeep } m := union.Members[0] size, err := Sizeof(m.Type) if err != nil { return err } if err := gf.writeStructField(m, depth); err != nil { return err } gf.writePadding(union.Size - uint32(size)) return nil } fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name)) if err := gf.writeType(m.Type, depth); err != nil { return err } gf.w.WriteString("; ") return nil } func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error { gf.w.WriteString("struct { ") prevOffset := uint32(0) for i, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { return fmt.Errorf("can't format %s as part of data section", vsi.Type) } if v.Linkage != GlobalVar { // Ignore static, extern, etc. for now. continue } if v.Name == "" { return fmt.Errorf("variable %d: empty name", i) } gf.writePadding(vsi.Offset - prevOffset) prevOffset = vsi.Offset + vsi.Size fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name)) if err := gf.writeType(v.Type, depth); err != nil { return fmt.Errorf("variable %d: %w", i, err) } gf.w.WriteString("; ") } gf.writePadding(ds.Size - prevOffset) gf.w.WriteString("}") return nil } func (gf *GoFormatter) writePadding(bytes uint32) { if bytes > 0 { fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes) } } func skipQualifiers(typ Type) Type { result := typ for depth := 0; depth <= maxTypeDepth; depth++ { switch v := (result).(type) { case qualifier: result = v.qualify() default: return result } } return &cycle{typ} } golang-github-cilium-ebpf-0.11.0/btf/format_test.go000066400000000000000000000141231456504511400221510ustar00rootroot00000000000000package btf import ( "errors" "fmt" "go/format" "strings" "testing" ) func TestGoTypeDeclaration(t *testing.T) { tests := []struct { typ Type output string }{ {&Int{Size: 1}, "type t uint8"}, {&Int{Size: 1, Encoding: Bool}, "type t bool"}, {&Int{Size: 1, Encoding: Char}, "type t uint8"}, {&Int{Size: 2, Encoding: Signed}, "type t int16"}, {&Int{Size: 4, Encoding: Signed}, "type t int32"}, {&Int{Size: 8}, "type t uint64"}, {&Typedef{Name: "frob", Type: &Int{Size: 8}}, "type t uint64"}, {&Int{Size: 16}, "type t [16]byte /* uint128 */"}, {&Enum{Values: []EnumValue{{"FOO", 32}}, Size: 4}, "type t uint32; const ( tFOO t = 32; )"}, {&Enum{Values: []EnumValue{{"BAR", 1}}, Size: 1, Signed: true}, "type t int8; const ( tBAR t = 1; )"}, { &Struct{ Name: "enum literals", Size: 2, Members: []Member{ {Name: "enum", Type: &Enum{Values: []EnumValue{{"BAR", 1}}, Size: 2}, Offset: 0}, }, }, "type t struct { enum uint16; }", }, {&Array{Nelems: 2, Type: &Int{Size: 1}}, "type t [2]uint8"}, { &Union{ Size: 8, Members: []Member{ {Name: "a", Type: &Int{Size: 4}}, {Name: "b", Type: &Int{Size: 8}}, }, }, "type t struct { a uint32; _ [4]byte; }", }, { &Struct{ Name: "field padding", Size: 16, Members: []Member{ {Name: "frob", Type: &Int{Size: 4}, Offset: 0}, {Name: "foo", Type: &Int{Size: 8}, Offset: 8 * 8}, }, }, "type t struct { frob uint32; _ [4]byte; foo uint64; }", }, { &Struct{ Name: "end padding", Size: 16, Members: []Member{ {Name: "foo", Type: &Int{Size: 8}, Offset: 0}, {Name: "frob", Type: &Int{Size: 4}, Offset: 8 * 8}, }, }, "type t struct { foo uint64; frob uint32; _ [4]byte; }", }, { &Struct{ Name: "bitfield", Size: 8, Members: []Member{ {Name: "foo", Type: &Int{Size: 4}, Offset: 0, BitfieldSize: 1}, {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, }, }, "type t struct { _ [4]byte /* unsupported bitfield */; frob uint32; }", }, { &Struct{ Name: "nested", Size: 8, Members: []Member{ { Name: "foo", Type: &Struct{ Size: 4, Members: []Member{ {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, }, }, }, {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, }, }, "type t struct { foo struct { bar uint32; }; frob uint32; }", }, { &Struct{ Name: "nested anon union", Size: 8, Members: []Member{ { Name: "", Type: &Union{ Size: 4, Members: []Member{ {Name: "foo", Type: &Int{Size: 4}, Offset: 0}, {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, }, }, }, }, }, "type t struct { foo uint32; _ [4]byte; }", }, { &Datasec{ Size: 16, Vars: []VarSecinfo{ {&Var{Name: "s", Type: &Int{Size: 2}, Linkage: StaticVar}, 0, 2}, {&Var{Name: "g", Type: &Int{Size: 4}, Linkage: GlobalVar}, 4, 4}, {&Var{Name: "e", Type: &Int{Size: 8}, Linkage: ExternVar}, 8, 8}, }, }, "type t struct { _ [4]byte; g uint32; _ [8]byte; }", }, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { have := mustGoTypeDeclaration(t, test.typ, nil, nil) if have != test.output { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) } }) } } func TestGoTypeDeclarationNamed(t *testing.T) { e1 := &Enum{Name: "e1", Size: 4} s1 := &Struct{ Name: "s1", Size: 4, Members: []Member{ {Name: "frob", Type: e1}, }, } s2 := &Struct{ Name: "s2", Size: 4, Members: []Member{ {Name: "frood", Type: s1}, }, } td := &Typedef{Name: "td", Type: e1} arr := &Array{Nelems: 1, Type: td} tests := []struct { typ Type named []Type output string }{ {e1, []Type{e1}, "type t uint32"}, {s1, []Type{e1, s1}, "type t struct { frob E1; }"}, {s2, []Type{e1}, "type t struct { frood struct { frob E1; }; }"}, {s2, []Type{e1, s1}, "type t struct { frood S1; }"}, {td, nil, "type t uint32"}, {td, []Type{td}, "type t uint32"}, {arr, []Type{td}, "type t [1]TD"}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { names := make(map[Type]string) for _, t := range test.named { names[t] = strings.ToUpper(t.TypeName()) } have := mustGoTypeDeclaration(t, test.typ, names, nil) if have != test.output { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) } }) } } func TestGoTypeDeclarationQualifiers(t *testing.T) { i := &Int{Size: 4} want := mustGoTypeDeclaration(t, i, nil, nil) tests := []struct { typ Type }{ {&Volatile{Type: i}}, {&Const{Type: i}}, {&Restrict{Type: i}}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { have := mustGoTypeDeclaration(t, test.typ, nil, nil) if have != want { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", want, have) } }) } } func TestGoTypeDeclarationCycle(t *testing.T) { s := &Struct{Name: "cycle"} s.Members = []Member{{Name: "f", Type: s}} var gf GoFormatter _, err := gf.TypeDeclaration("t", s) if !errors.Is(err, errNestedTooDeep) { t.Fatal("Expected errNestedTooDeep, got", err) } } func TestRejectBogusTypes(t *testing.T) { tests := []struct { typ Type }{ {&Struct{ Size: 1, Members: []Member{ {Name: "foo", Type: &Int{Size: 2}, Offset: 0}, }, }}, {&Int{Size: 2, Encoding: Bool}}, {&Int{Size: 1, Encoding: Char | Signed}}, {&Int{Size: 2, Encoding: Char}}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { var gf GoFormatter _, err := gf.TypeDeclaration("t", test.typ) if err == nil { t.Fatal("TypeDeclaration does not reject bogus type") } }) } } func mustGoTypeDeclaration(tb testing.TB, typ Type, names map[Type]string, id func(string) string) string { tb.Helper() gf := GoFormatter{ Names: names, Identifier: id, } have, err := gf.TypeDeclaration("t", typ) if err != nil { tb.Fatal(err) } _, err = format.Source([]byte(have)) if err != nil { tb.Fatalf("Output can't be formatted: %s\n%s", err, have) } return have } golang-github-cilium-ebpf-0.11.0/btf/fuzz_test.go000066400000000000000000000030731456504511400216610ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "fmt" "io" "testing" "github.com/cilium/ebpf/internal" ) func FuzzSpec(f *testing.F) { var buf bytes.Buffer err := binary.Write(&buf, internal.NativeEndian, &btfHeader{ Magic: btfMagic, Version: 1, HdrLen: uint32(binary.Size(btfHeader{})), }) if err != nil { f.Fatal(err) } f.Add(buf.Bytes()) f.Fuzz(func(t *testing.T, data []byte) { if len(data) < binary.Size(btfHeader{}) { t.Skip("data is too short") } spec, err := loadRawSpec(bytes.NewReader(data), internal.NativeEndian, nil) if err != nil { if spec != nil { t.Fatal("spec is not nil") } return } if spec == nil { t.Fatal("spec is nil") } for _, typ := range spec.types { fmt.Fprintf(io.Discard, "%+10v", typ) } }) } func FuzzExtInfo(f *testing.F) { var buf bytes.Buffer err := binary.Write(&buf, internal.NativeEndian, &btfExtHeader{ Magic: btfMagic, Version: 1, HdrLen: uint32(binary.Size(btfExtHeader{})), }) if err != nil { f.Fatal(err) } f.Add(buf.Bytes(), []byte("\x00foo\x00barfoo\x00")) emptySpec := newSpec() f.Fuzz(func(t *testing.T, data, strings []byte) { if len(data) < binary.Size(btfExtHeader{}) { t.Skip("data is too short") } table, err := readStringTable(bytes.NewReader(strings), nil) if err != nil { t.Skip("invalid string table") } info, err := loadExtInfos(bytes.NewReader(data), internal.NativeEndian, emptySpec, table) if err != nil { if info != nil { t.Fatal("info is not nil") } } else if info == nil { t.Fatal("info is nil") } }) } golang-github-cilium-ebpf-0.11.0/btf/handle.go000066400000000000000000000161231456504511400210570ustar00rootroot00000000000000package btf import ( "bytes" "errors" "fmt" "math" "os" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // Handle is a reference to BTF loaded into the kernel. type Handle struct { fd *sys.FD // Size of the raw BTF in bytes. size uint32 needsKernelBase bool } // NewHandle loads the contents of a [Builder] into the kernel. // // Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF. func NewHandle(b *Builder) (*Handle, error) { small := getByteSlice() defer putByteSlice(small) buf, err := b.Marshal(*small, KernelMarshalOptions()) if err != nil { return nil, fmt.Errorf("marshal BTF: %w", err) } return NewHandleFromRawBTF(buf) } // NewHandleFromRawBTF loads raw BTF into the kernel. // // Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF. func NewHandleFromRawBTF(btf []byte) (*Handle, error) { if uint64(len(btf)) > math.MaxUint32 { return nil, errors.New("BTF exceeds the maximum size") } attr := &sys.BtfLoadAttr{ Btf: sys.NewSlicePointer(btf), BtfSize: uint32(len(btf)), } fd, err := sys.BtfLoad(attr) if err == nil { return &Handle{fd, attr.BtfSize, false}, nil } if err := haveBTF(); err != nil { return nil, err } logBuf := make([]byte, 64*1024) attr.BtfLogBuf = sys.NewSlicePointer(logBuf) attr.BtfLogSize = uint32(len(logBuf)) attr.BtfLogLevel = 1 // Up until at least kernel 6.0, the BTF verifier does not return ENOSPC // if there are other verification errors. ENOSPC is only returned when // the BTF blob is correct, a log was requested, and the provided buffer // is too small. _, ve := sys.BtfLoad(attr) return nil, internal.ErrorWithLog("load btf", err, logBuf, errors.Is(ve, unix.ENOSPC)) } // NewHandleFromID returns the BTF handle for a given id. // // Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible. // // Returns ErrNotExist, if there is no BTF with the given id. // // Requires CAP_SYS_ADMIN. func NewHandleFromID(id ID) (*Handle, error) { fd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, fmt.Errorf("get FD for ID %d: %w", id, err) } info, err := newHandleInfoFromFD(fd) if err != nil { _ = fd.Close() return nil, err } return &Handle{fd, info.size, info.IsModule()}, nil } // Spec parses the kernel BTF into Go types. // // base must contain type information for vmlinux if the handle is for // a kernel module. It may be nil otherwise. func (h *Handle) Spec(base *Spec) (*Spec, error) { var btfInfo sys.BtfInfo btfBuffer := make([]byte, h.size) btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer) if err := sys.ObjInfo(h.fd, &btfInfo); err != nil { return nil, err } if h.needsKernelBase && base == nil { return nil, fmt.Errorf("missing base types") } return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, base) } // Close destroys the handle. // // Subsequent calls to FD will return an invalid value. func (h *Handle) Close() error { if h == nil { return nil } return h.fd.Close() } // FD returns the file descriptor for the handle. func (h *Handle) FD() int { return h.fd.Int() } // Info returns metadata about the handle. func (h *Handle) Info() (*HandleInfo, error) { return newHandleInfoFromFD(h.fd) } // HandleInfo describes a Handle. type HandleInfo struct { // ID of this handle in the kernel. The ID is only valid as long as the // associated handle is kept alive. ID ID // Name is an identifying name for the BTF, currently only used by the // kernel. Name string // IsKernel is true if the BTF originated with the kernel and not // userspace. IsKernel bool // Size of the raw BTF in bytes. size uint32 } func newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) { // We invoke the syscall once with a empty BTF and name buffers to get size // information to allocate buffers. Then we invoke it a second time with // buffers to receive the data. var btfInfo sys.BtfInfo if err := sys.ObjInfo(fd, &btfInfo); err != nil { return nil, fmt.Errorf("get BTF info for fd %s: %w", fd, err) } if btfInfo.NameLen > 0 { // NameLen doesn't account for the terminating NUL. btfInfo.NameLen++ } // Don't pull raw BTF by default, since it may be quite large. btfSize := btfInfo.BtfSize btfInfo.BtfSize = 0 nameBuffer := make([]byte, btfInfo.NameLen) btfInfo.Name, btfInfo.NameLen = sys.NewSlicePointerLen(nameBuffer) if err := sys.ObjInfo(fd, &btfInfo); err != nil { return nil, err } return &HandleInfo{ ID: ID(btfInfo.Id), Name: unix.ByteSliceToString(nameBuffer), IsKernel: btfInfo.KernelBtf != 0, size: btfSize, }, nil } // IsVmlinux returns true if the BTF is for the kernel itself. func (i *HandleInfo) IsVmlinux() bool { return i.IsKernel && i.Name == "vmlinux" } // IsModule returns true if the BTF is for a kernel module. func (i *HandleInfo) IsModule() bool { return i.IsKernel && i.Name != "vmlinux" } // HandleIterator allows enumerating BTF blobs loaded into the kernel. type HandleIterator struct { // The ID of the current handle. Only valid after a call to Next. ID ID // The current Handle. Only valid until a call to Next. // See Take if you want to retain the handle. Handle *Handle err error } // Next retrieves a handle for the next BTF object. // // Returns true if another BTF object was found. Call [HandleIterator.Err] after // the function returns false. func (it *HandleIterator) Next() bool { id := it.ID for { attr := &sys.BtfGetNextIdAttr{Id: id} err := sys.BtfGetNextId(attr) if errors.Is(err, os.ErrNotExist) { // There are no more BTF objects. break } else if err != nil { it.err = fmt.Errorf("get next BTF ID: %w", err) break } id = attr.NextId handle, err := NewHandleFromID(id) if errors.Is(err, os.ErrNotExist) { // Try again with the next ID. continue } else if err != nil { it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err) break } it.Handle.Close() it.ID, it.Handle = id, handle return true } // No more handles or we encountered an error. it.Handle.Close() it.Handle = nil return false } // Take the ownership of the current handle. // // It's the callers responsibility to close the handle. func (it *HandleIterator) Take() *Handle { handle := it.Handle it.Handle = nil return handle } // Err returns an error if iteration failed for some reason. func (it *HandleIterator) Err() error { return it.err } // FindHandle returns the first handle for which predicate returns true. // // Requires CAP_SYS_ADMIN. // // Returns an error wrapping ErrNotFound if predicate never returns true or if // there is no BTF loaded into the kernel. func FindHandle(predicate func(info *HandleInfo) bool) (*Handle, error) { it := new(HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { return nil, fmt.Errorf("info for ID %d: %w", it.ID, err) } if predicate(info) { return it.Take(), nil } } if err := it.Err(); err != nil { return nil, fmt.Errorf("iterate handles: %w", err) } return nil, fmt.Errorf("find handle: %w", ErrNotFound) } golang-github-cilium-ebpf-0.11.0/btf/handle_test.go000066400000000000000000000037551456504511400221250ustar00rootroot00000000000000package btf_test import ( "fmt" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/testutils" ) func TestHandleIterator(t *testing.T) { // There is no guarantee that there is a BTF ID allocated, but loading a module // triggers loading vmlinux. // See https://github.com/torvalds/linux/commit/5329722057d41aebc31e391907a501feaa42f7d9 testutils.SkipOnOldKernel(t, "5.11", "vmlinux BTF ID") it := new(btf.HandleIterator) defer it.Handle.Close() if !it.Next() { t.Fatalf("No BTF loaded") } if it.Handle == nil { t.Fatal("Next doesn't assign handle") } prev := it.ID for it.Next() { // Iterate all loaded BTF. if it.Handle == nil { t.Fatal("Next doesn't assign handle") } if it.ID == prev { t.Fatal("Iterator doesn't advance ID") } prev = it.ID } if err := it.Err(); err != nil { t.Fatal("Iteration returned an error:", err) } if it.Handle != nil { t.Fatal("Next doesn't clean up handle on last iteration") } if prev != it.ID { t.Fatal("Next changes ID on last iteration") } } func TestParseModuleSplitSpec(t *testing.T) { // See TestNewHandleFromID for reasoning. testutils.SkipOnOldKernel(t, "5.11", "vmlinux BTF ID") module, err := btf.FindHandle(func(info *btf.HandleInfo) bool { if info.IsModule() { t.Log("Using module", info.Name) return true } return false }) if err != nil { t.Fatal(err) } defer module.Close() vmlinux, err := btf.FindHandle(func(info *btf.HandleInfo) bool { return info.IsVmlinux() }) if err != nil { t.Fatal(err) } defer vmlinux.Close() base, err := vmlinux.Spec(nil) if err != nil { t.Fatal(err) } _, err = module.Spec(base) if err != nil { t.Fatal("Parse module BTF:", err) } } func ExampleHandleIterator() { it := new(btf.HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { panic(err) } fmt.Printf("Found handle with ID %d and name %s\n", it.ID, info.Name) } if err := it.Err(); err != nil { panic(err) } } golang-github-cilium-ebpf-0.11.0/btf/marshal.go000066400000000000000000000270041456504511400212530ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "sync" "github.com/cilium/ebpf/internal" "golang.org/x/exp/slices" ) type MarshalOptions struct { // Target byte order. Defaults to the system's native endianness. Order binary.ByteOrder // Remove function linkage information for compatibility with <5.6 kernels. StripFuncLinkage bool } // KernelMarshalOptions will generate BTF suitable for the current kernel. func KernelMarshalOptions() *MarshalOptions { return &MarshalOptions{ Order: internal.NativeEndian, StripFuncLinkage: haveFuncLinkage() != nil, } } // encoder turns Types into raw BTF. type encoder struct { MarshalOptions pending internal.Deque[Type] buf *bytes.Buffer strings *stringTableBuilder ids map[Type]TypeID lastID TypeID } var bufferPool = sync.Pool{ New: func() any { buf := make([]byte, btfHeaderLen+128) return &buf }, } func getByteSlice() *[]byte { return bufferPool.Get().(*[]byte) } func putByteSlice(buf *[]byte) { *buf = (*buf)[:0] bufferPool.Put(buf) } // Builder turns Types into raw BTF. // // The default value may be used and represents an empty BTF blob. Void is // added implicitly if necessary. type Builder struct { // Explicitly added types. types []Type // IDs for all added types which the user knows about. stableIDs map[Type]TypeID // Explicitly added strings. strings *stringTableBuilder } // NewBuilder creates a Builder from a list of types. // // It is more efficient than calling [Add] individually. // // Returns an error if adding any of the types fails. func NewBuilder(types []Type) (*Builder, error) { b := &Builder{ make([]Type, 0, len(types)), make(map[Type]TypeID, len(types)), nil, } for _, typ := range types { _, err := b.Add(typ) if err != nil { return nil, fmt.Errorf("add %s: %w", typ, err) } } return b, nil } // Add a Type and allocate a stable ID for it. // // Adding the identical Type multiple times is valid and will return the same ID. // // See [Type] for details on identity. func (b *Builder) Add(typ Type) (TypeID, error) { if b.stableIDs == nil { b.stableIDs = make(map[Type]TypeID) } if _, ok := typ.(*Void); ok { // Equality is weird for void, since it is a zero sized type. return 0, nil } if ds, ok := typ.(*Datasec); ok { if err := datasecResolveWorkaround(b, ds); err != nil { return 0, err } } id, ok := b.stableIDs[typ] if ok { return id, nil } b.types = append(b.types, typ) id = TypeID(len(b.types)) if int(id) != len(b.types) { return 0, fmt.Errorf("no more type IDs") } b.stableIDs[typ] = id return id, nil } // Marshal encodes all types in the Marshaler into BTF wire format. // // opts may be nil. func (b *Builder) Marshal(buf []byte, opts *MarshalOptions) ([]byte, error) { stb := b.strings if stb == nil { // Assume that most types are named. This makes encoding large BTF like // vmlinux a lot cheaper. stb = newStringTableBuilder(len(b.types)) } else { // Avoid modifying the Builder's string table. stb = b.strings.Copy() } if opts == nil { opts = &MarshalOptions{Order: internal.NativeEndian} } // Reserve space for the BTF header. buf = slices.Grow(buf, btfHeaderLen)[:btfHeaderLen] w := internal.NewBuffer(buf) defer internal.PutBuffer(w) e := encoder{ MarshalOptions: *opts, buf: w, strings: stb, lastID: TypeID(len(b.types)), ids: make(map[Type]TypeID, len(b.types)), } // Ensure that types are marshaled in the exact order they were Add()ed. // Otherwise the ID returned from Add() won't match. e.pending.Grow(len(b.types)) for _, typ := range b.types { e.pending.Push(typ) e.ids[typ] = b.stableIDs[typ] } if err := e.deflatePending(); err != nil { return nil, err } length := e.buf.Len() typeLen := uint32(length - btfHeaderLen) stringLen := e.strings.Length() buf = e.strings.AppendEncoded(e.buf.Bytes()) // Fill out the header, and write it out. header := &btfHeader{ Magic: btfMagic, Version: 1, Flags: 0, HdrLen: uint32(btfHeaderLen), TypeOff: 0, TypeLen: typeLen, StringOff: typeLen, StringLen: uint32(stringLen), } err := binary.Write(sliceWriter(buf[:btfHeaderLen]), e.Order, header) if err != nil { return nil, fmt.Errorf("write header: %v", err) } return buf, nil } // addString adds a string to the resulting BTF. // // Adding the same string multiple times will return the same result. // // Returns an identifier into the string table or an error if the string // contains invalid characters. func (b *Builder) addString(str string) (uint32, error) { if b.strings == nil { b.strings = newStringTableBuilder(0) } return b.strings.Add(str) } func (e *encoder) allocateID(typ Type) error { id := e.lastID + 1 if id < e.lastID { return errors.New("type ID overflow") } e.pending.Push(typ) e.ids[typ] = id e.lastID = id return nil } // id returns the ID for the given type or panics with an error. func (e *encoder) id(typ Type) TypeID { if _, ok := typ.(*Void); ok { return 0 } id, ok := e.ids[typ] if !ok { panic(fmt.Errorf("no ID for type %v", typ)) } return id } func (e *encoder) deflatePending() error { // Declare root outside of the loop to avoid repeated heap allocations. var root Type skip := func(t Type) (skip bool) { if t == root { // Force descending into the current root type even if it already // has an ID. Otherwise we miss children of types that have their // ID pre-allocated via Add. return false } _, isVoid := t.(*Void) _, alreadyEncoded := e.ids[t] return isVoid || alreadyEncoded } for !e.pending.Empty() { root = e.pending.Shift() // Allocate IDs for all children of typ, including transitive dependencies. iter := postorderTraversal(root, skip) for iter.Next() { if iter.Type == root { // The iterator yields root at the end, do not allocate another ID. break } if err := e.allocateID(iter.Type); err != nil { return err } } if err := e.deflateType(root); err != nil { id := e.ids[root] return fmt.Errorf("deflate %v with ID %d: %w", root, id, err) } } return nil } func (e *encoder) deflateType(typ Type) (err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { panic(r) } } }() var raw rawType raw.NameOff, err = e.strings.Add(typ.TypeName()) if err != nil { return err } switch v := typ.(type) { case *Void: return errors.New("Void is implicit in BTF wire format") case *Int: raw.SetKind(kindInt) raw.SetSize(v.Size) var bi btfInt bi.SetEncoding(v.Encoding) // We need to set bits in addition to size, since btf_type_int_is_regular // otherwise flags this as a bitfield. bi.SetBits(byte(v.Size) * 8) raw.data = bi case *Pointer: raw.SetKind(kindPointer) raw.SetType(e.id(v.Target)) case *Array: raw.SetKind(kindArray) raw.data = &btfArray{ e.id(v.Type), e.id(v.Index), v.Nelems, } case *Struct: raw.SetKind(kindStruct) raw.SetSize(v.Size) raw.data, err = e.convertMembers(&raw.btfType, v.Members) case *Union: raw.SetKind(kindUnion) raw.SetSize(v.Size) raw.data, err = e.convertMembers(&raw.btfType, v.Members) case *Enum: raw.SetSize(v.size()) raw.SetVlen(len(v.Values)) raw.SetSigned(v.Signed) if v.has64BitValues() { raw.SetKind(kindEnum64) raw.data, err = e.deflateEnum64Values(v.Values) } else { raw.SetKind(kindEnum) raw.data, err = e.deflateEnumValues(v.Values) } case *Fwd: raw.SetKind(kindForward) raw.SetFwdKind(v.Kind) case *Typedef: raw.SetKind(kindTypedef) raw.SetType(e.id(v.Type)) case *Volatile: raw.SetKind(kindVolatile) raw.SetType(e.id(v.Type)) case *Const: raw.SetKind(kindConst) raw.SetType(e.id(v.Type)) case *Restrict: raw.SetKind(kindRestrict) raw.SetType(e.id(v.Type)) case *Func: raw.SetKind(kindFunc) raw.SetType(e.id(v.Type)) if !e.StripFuncLinkage { raw.SetLinkage(v.Linkage) } case *FuncProto: raw.SetKind(kindFuncProto) raw.SetType(e.id(v.Return)) raw.SetVlen(len(v.Params)) raw.data, err = e.deflateFuncParams(v.Params) case *Var: raw.SetKind(kindVar) raw.SetType(e.id(v.Type)) raw.data = btfVariable{uint32(v.Linkage)} case *Datasec: raw.SetKind(kindDatasec) raw.SetSize(v.Size) raw.SetVlen(len(v.Vars)) raw.data = e.deflateVarSecinfos(v.Vars) case *Float: raw.SetKind(kindFloat) raw.SetSize(v.Size) case *declTag: raw.SetKind(kindDeclTag) raw.SetType(e.id(v.Type)) raw.data = &btfDeclTag{uint32(v.Index)} raw.NameOff, err = e.strings.Add(v.Value) case *typeTag: raw.SetKind(kindTypeTag) raw.SetType(e.id(v.Type)) raw.NameOff, err = e.strings.Add(v.Value) default: return fmt.Errorf("don't know how to deflate %T", v) } if err != nil { return err } return raw.Marshal(e.buf, e.Order) } func (e *encoder) convertMembers(header *btfType, members []Member) ([]btfMember, error) { bms := make([]btfMember, 0, len(members)) isBitfield := false for _, member := range members { isBitfield = isBitfield || member.BitfieldSize > 0 offset := member.Offset if isBitfield { offset = member.BitfieldSize<<24 | (member.Offset & 0xffffff) } nameOff, err := e.strings.Add(member.Name) if err != nil { return nil, err } bms = append(bms, btfMember{ nameOff, e.id(member.Type), uint32(offset), }) } header.SetVlen(len(members)) header.SetBitfield(isBitfield) return bms, nil } func (e *encoder) deflateEnumValues(values []EnumValue) ([]btfEnum, error) { bes := make([]btfEnum, 0, len(values)) for _, value := range values { nameOff, err := e.strings.Add(value.Name) if err != nil { return nil, err } if value.Value > math.MaxUint32 { return nil, fmt.Errorf("value of enum %q exceeds 32 bits", value.Name) } bes = append(bes, btfEnum{ nameOff, uint32(value.Value), }) } return bes, nil } func (e *encoder) deflateEnum64Values(values []EnumValue) ([]btfEnum64, error) { bes := make([]btfEnum64, 0, len(values)) for _, value := range values { nameOff, err := e.strings.Add(value.Name) if err != nil { return nil, err } bes = append(bes, btfEnum64{ nameOff, uint32(value.Value), uint32(value.Value >> 32), }) } return bes, nil } func (e *encoder) deflateFuncParams(params []FuncParam) ([]btfParam, error) { bps := make([]btfParam, 0, len(params)) for _, param := range params { nameOff, err := e.strings.Add(param.Name) if err != nil { return nil, err } bps = append(bps, btfParam{ nameOff, e.id(param.Type), }) } return bps, nil } func (e *encoder) deflateVarSecinfos(vars []VarSecinfo) []btfVarSecinfo { vsis := make([]btfVarSecinfo, 0, len(vars)) for _, v := range vars { vsis = append(vsis, btfVarSecinfo{ e.id(v.Type), v.Offset, v.Size, }) } return vsis } // MarshalMapKV creates a BTF object containing a map key and value. // // The function is intended for the use of the ebpf package and may be removed // at any point in time. func MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, err error) { var b Builder if key != nil { keyID, err = b.Add(key) if err != nil { return nil, 0, 0, fmt.Errorf("add key type: %w", err) } } if value != nil { valueID, err = b.Add(value) if err != nil { return nil, 0, 0, fmt.Errorf("add value type: %w", err) } } handle, err := NewHandle(&b) if err != nil { // Check for 'full' map BTF support, since kernels between 4.18 and 5.2 // already support BTF blobs for maps without Var or Datasec just fine. if err := haveMapBTF(); err != nil { return nil, 0, 0, err } } return handle, keyID, valueID, err } golang-github-cilium-ebpf-0.11.0/btf/marshal_test.go000066400000000000000000000075511456504511400223170ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "math" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/google/go-cmp/cmp" qt "github.com/frankban/quicktest" ) func TestBuilderMarshal(t *testing.T) { typ := &Int{ Name: "foo", Size: 2, Encoding: Signed | Char, } want := []Type{ (*Void)(nil), typ, &Pointer{typ}, &Typedef{"baz", typ}, } b, err := NewBuilder(want) qt.Assert(t, err, qt.IsNil) cpy := *b buf, err := b.Marshal(nil, &MarshalOptions{Order: internal.NativeEndian}) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b, qt.CmpEquals(cmp.AllowUnexported(*b)), &cpy, qt.Commentf("Marshaling should not change Builder state")) have, err := loadRawSpec(bytes.NewReader(buf), internal.NativeEndian, nil) qt.Assert(t, err, qt.IsNil, qt.Commentf("Couldn't parse BTF")) qt.Assert(t, have.types, qt.DeepEquals, want) } func TestBuilderAdd(t *testing.T) { i := &Int{ Name: "foo", Size: 2, Encoding: Signed | Char, } pi := &Pointer{i} var b Builder id, err := b.Add(pi) qt.Assert(t, err, qt.IsNil) qt.Assert(t, id, qt.Equals, TypeID(1), qt.Commentf("First non-void type doesn't get id 1")) id, err = b.Add(pi) qt.Assert(t, err, qt.IsNil) qt.Assert(t, id, qt.Equals, TypeID(1)) id, err = b.Add(i) qt.Assert(t, err, qt.IsNil) qt.Assert(t, id, qt.Equals, TypeID(2), qt.Commentf("Second type doesn't get id 2")) id, err = b.Add(i) qt.Assert(t, err, qt.IsNil) qt.Assert(t, id, qt.Equals, TypeID(2), qt.Commentf("Adding a type twice returns different ids")) id, err = b.Add(&Typedef{"baz", i}) qt.Assert(t, err, qt.IsNil) qt.Assert(t, id, qt.Equals, TypeID(3)) } func TestRoundtripVMlinux(t *testing.T) { types := vmlinuxSpec(t).types // Randomize the order to force different permutations of walking the type // graph. Keep Void at index 0. testutils.Rand().Shuffle(len(types[1:]), func(i, j int) { types[i+1], types[j+1] = types[j+1], types[i+1] }) // Skip per CPU datasec, see https://github.com/cilium/ebpf/issues/921 for i, typ := range types { if ds, ok := typ.(*Datasec); ok && ds.Name == ".data..percpu" { types[i] = types[len(types)-1] types = types[:len(types)-1] break } } seen := make(map[Type]bool) limitTypes: for i, typ := range types { iter := postorderTraversal(typ, func(t Type) (skip bool) { return seen[t] }) for iter.Next() { seen[iter.Type] = true } if len(seen) >= math.MaxInt16 { // IDs exceeding math.MaxUint16 can trigger a bug when loading BTF. // This can be removed once the patch lands. // See https://lore.kernel.org/bpf/20220909092107.3035-1-oss@lmb.io/ types = types[:i] break limitTypes } } buf := marshalNativeEndian(t, types) rebuilt, err := loadRawSpec(bytes.NewReader(buf), binary.LittleEndian, nil) qt.Assert(t, err, qt.IsNil, qt.Commentf("round tripping BTF failed")) if n := len(rebuilt.types); n > math.MaxUint16 { t.Logf("Rebuilt BTF contains %d types which exceeds uint16, test may fail on older kernels", n) } h, err := NewHandleFromRawBTF(buf) testutils.SkipIfNotSupported(t, err) qt.Assert(t, err, qt.IsNil, qt.Commentf("loading rebuilt BTF failed")) h.Close() } func BenchmarkMarshaler(b *testing.B) { spec := vmlinuxTestdataSpec(b) types := spec.types[:100] b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { var b Builder for _, typ := range types { _, _ = b.Add(typ) } _, _ = b.Marshal(nil, nil) } } func BenchmarkBuildVmlinux(b *testing.B) { types := vmlinuxTestdataSpec(b).types b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { var b Builder for _, typ := range types { _, _ = b.Add(typ) } _, _ = b.Marshal(nil, nil) } } func marshalNativeEndian(tb testing.TB, types []Type) []byte { tb.Helper() b, err := NewBuilder(types) qt.Assert(tb, err, qt.IsNil) buf, err := b.Marshal(nil, nil) qt.Assert(tb, err, qt.IsNil) return buf } golang-github-cilium-ebpf-0.11.0/btf/strings.go000066400000000000000000000117031456504511400213140ustar00rootroot00000000000000package btf import ( "bufio" "bytes" "errors" "fmt" "io" "strings" "golang.org/x/exp/maps" ) type stringTable struct { base *stringTable offsets []uint32 strings []string } // sizedReader is implemented by bytes.Reader, io.SectionReader, strings.Reader, etc. type sizedReader interface { io.Reader Size() int64 } func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) { // When parsing split BTF's string table, the first entry offset is derived // from the last entry offset of the base BTF. firstStringOffset := uint32(0) if base != nil { idx := len(base.offsets) - 1 firstStringOffset = base.offsets[idx] + uint32(len(base.strings[idx])) + 1 } // Derived from vmlinux BTF. const averageStringLength = 16 n := int(r.Size() / averageStringLength) offsets := make([]uint32, 0, n) strings := make([]string, 0, n) offset := firstStringOffset scanner := bufio.NewScanner(r) scanner.Split(splitNull) for scanner.Scan() { str := scanner.Text() offsets = append(offsets, offset) strings = append(strings, str) offset += uint32(len(str)) + 1 } if err := scanner.Err(); err != nil { return nil, err } if len(strings) == 0 { return nil, errors.New("string table is empty") } if firstStringOffset == 0 && strings[0] != "" { return nil, errors.New("first item in string table is non-empty") } return &stringTable{base, offsets, strings}, nil } func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) { i := bytes.IndexByte(data, 0) if i == -1 { if atEOF && len(data) > 0 { return 0, nil, errors.New("string table isn't null terminated") } return 0, nil, nil } return i + 1, data[:i], nil } func (st *stringTable) Lookup(offset uint32) (string, error) { if st.base != nil && offset <= st.base.offsets[len(st.base.offsets)-1] { return st.base.lookup(offset) } return st.lookup(offset) } func (st *stringTable) lookup(offset uint32) (string, error) { i := search(st.offsets, offset) if i == len(st.offsets) || st.offsets[i] != offset { return "", fmt.Errorf("offset %d isn't start of a string", offset) } return st.strings[i], nil } func (st *stringTable) Marshal(w io.Writer) error { for _, str := range st.strings { _, err := io.WriteString(w, str) if err != nil { return err } _, err = w.Write([]byte{0}) if err != nil { return err } } return nil } // Num returns the number of strings in the table. func (st *stringTable) Num() int { return len(st.strings) } // search is a copy of sort.Search specialised for uint32. // // Licensed under https://go.dev/LICENSE func search(ints []uint32, needle uint32) int { // Define f(-1) == false and f(n) == true. // Invariant: f(i-1) == false, f(j) == true. i, j := 0, len(ints) for i < j { h := int(uint(i+j) >> 1) // avoid overflow when computing h // i ≤ h < j if !(ints[h] >= needle) { i = h + 1 // preserves f(i-1) == false } else { j = h // preserves f(j) == true } } // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. return i } // stringTableBuilder builds BTF string tables. type stringTableBuilder struct { length uint32 strings map[string]uint32 } // newStringTableBuilder creates a builder with the given capacity. // // capacity may be zero. func newStringTableBuilder(capacity int) *stringTableBuilder { var stb stringTableBuilder if capacity == 0 { // Use the runtime's small default size. stb.strings = make(map[string]uint32) } else { stb.strings = make(map[string]uint32, capacity) } // Ensure that the empty string is at index 0. stb.append("") return &stb } // Add a string to the table. // // Adding the same string multiple times will only store it once. func (stb *stringTableBuilder) Add(str string) (uint32, error) { if strings.IndexByte(str, 0) != -1 { return 0, fmt.Errorf("string contains null: %q", str) } offset, ok := stb.strings[str] if ok { return offset, nil } return stb.append(str), nil } func (stb *stringTableBuilder) append(str string) uint32 { offset := stb.length stb.length += uint32(len(str)) + 1 stb.strings[str] = offset return offset } // Lookup finds the offset of a string in the table. // // Returns an error if str hasn't been added yet. func (stb *stringTableBuilder) Lookup(str string) (uint32, error) { offset, ok := stb.strings[str] if !ok { return 0, fmt.Errorf("string %q is not in table", str) } return offset, nil } // Length returns the length in bytes. func (stb *stringTableBuilder) Length() int { return int(stb.length) } // AppendEncoded appends the string table to the end of the provided buffer. func (stb *stringTableBuilder) AppendEncoded(buf []byte) []byte { n := len(buf) buf = append(buf, make([]byte, stb.Length())...) strings := buf[n:] for str, offset := range stb.strings { copy(strings[offset:], str) } return buf } // Copy the string table builder. func (stb *stringTableBuilder) Copy() *stringTableBuilder { return &stringTableBuilder{ stb.length, maps.Clone(stb.strings), } } golang-github-cilium-ebpf-0.11.0/btf/strings_test.go000066400000000000000000000050421456504511400223520ustar00rootroot00000000000000package btf import ( "bytes" "strings" "testing" qt "github.com/frankban/quicktest" ) func TestStringTable(t *testing.T) { const in = "\x00one\x00two\x00" const splitIn = "three\x00four\x00" st, err := readStringTable(strings.NewReader(in), nil) if err != nil { t.Fatal(err) } var buf bytes.Buffer if err := st.Marshal(&buf); err != nil { t.Fatal("Can't marshal string table:", err) } if !bytes.Equal([]byte(in), buf.Bytes()) { t.Error("String table doesn't match input") } // Parse string table of split BTF split, err := readStringTable(strings.NewReader(splitIn), st) if err != nil { t.Fatal(err) } testcases := []struct { offset uint32 want string }{ {0, ""}, {1, "one"}, {5, "two"}, {9, "three"}, {15, "four"}, } for _, tc := range testcases { have, err := split.Lookup(tc.offset) if err != nil { t.Errorf("Offset %d: %s", tc.offset, err) continue } if have != tc.want { t.Errorf("Offset %d: want %s but have %s", tc.offset, tc.want, have) } } if _, err := st.Lookup(2); err == nil { t.Error("No error when using offset pointing into middle of string") } // Make sure we reject bogus tables _, err = readStringTable(strings.NewReader("\x00one"), nil) if err == nil { t.Fatal("Accepted non-terminated string") } _, err = readStringTable(strings.NewReader("one\x00"), nil) if err == nil { t.Fatal("Accepted non-empty first item") } } func TestStringTableBuilder(t *testing.T) { stb := newStringTableBuilder(0) _, err := readStringTable(bytes.NewReader(stb.AppendEncoded(nil)), nil) qt.Assert(t, err, qt.IsNil, qt.Commentf("Can't parse string table")) _, err = stb.Add("foo\x00bar") qt.Assert(t, err, qt.IsNotNil) empty, err := stb.Add("") qt.Assert(t, err, qt.IsNil) qt.Assert(t, empty, qt.Equals, uint32(0), qt.Commentf("The empty string is not at index 0")) foo1, _ := stb.Add("foo") foo2, _ := stb.Add("foo") qt.Assert(t, foo1, qt.Equals, foo2, qt.Commentf("Adding the same string returns different offsets")) table := stb.AppendEncoded(nil) if n := bytes.Count(table, []byte("foo")); n != 1 { t.Fatalf("Marshalled string table contains foo %d times instead of once", n) } _, err = readStringTable(bytes.NewReader(table), nil) qt.Assert(t, err, qt.IsNil, qt.Commentf("Can't parse string table")) } func newStringTable(strings ...string) *stringTable { offsets := make([]uint32, len(strings)) var offset uint32 for i, str := range strings { offsets[i] = offset offset += uint32(len(str)) + 1 // account for NUL } return &stringTable{nil, offsets, strings} } golang-github-cilium-ebpf-0.11.0/btf/testdata/000077500000000000000000000000001456504511400211035ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/testdata/bpf_core_read.h000066400000000000000000000431101456504511400240250ustar00rootroot00000000000000/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_CORE_READ_H__ #define __BPF_CORE_READ_H__ /* * enum bpf_field_info_kind is passed as a second argument into * __builtin_preserve_field_info() built-in to get a specific aspect of * a field, captured as a first argument. __builtin_preserve_field_info(field, * info_kind) returns __u32 integer and produces BTF field relocation, which * is understood and processed by libbpf during BPF object loading. See * selftests/bpf for examples. */ enum bpf_field_info_kind { BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ BPF_FIELD_BYTE_SIZE = 1, BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ BPF_FIELD_SIGNED = 3, BPF_FIELD_LSHIFT_U64 = 4, BPF_FIELD_RSHIFT_U64 = 5, }; /* second argument to __builtin_btf_type_id() built-in */ enum bpf_type_id_kind { BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ }; /* second argument to __builtin_preserve_type_info() built-in */ enum bpf_type_info_kind { BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ BPF_TYPE_SIZE = 1, /* type size in target kernel */ }; /* second argument to __builtin_preserve_enum_value() built-in */ enum bpf_enum_value_kind { BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ }; #define __CORE_RELO(src, field, info) \ __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) #if __BYTE_ORDER == __LITTLE_ENDIAN #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst, \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #else /* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so * for big-endian we need to adjust destination pointer accordingly, based on * field byte size */ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #endif /* * Extract bitfield, identified by s->field, and return its value as u64. * All this is done in relocatable manner, so bitfield changes such as * signedness, bit size, offset changes, this will be handled automatically. * This version of macro is using bpf_probe_read_kernel() to read underlying * integer storage. Macro functions as an expression and its return type is * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. */ #define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ unsigned long long val = 0; \ \ __CORE_BITFIELD_PROBE_READ(&val, s, field); \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Extract bitfield, identified by s->field, and return its value as u64. * This version of macro is using direct memory reads and should be used from * BPF program types that support such functionality (e.g., typed raw * tracepoints). */ #define BPF_CORE_READ_BITFIELD(s, field) ({ \ const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ unsigned long long val; \ \ /* This is a so-called barrier_var() operation that makes specified \ * variable "a black box" for optimizing compiler. \ * It forces compiler to perform BYTE_OFFSET relocation on p and use \ * its calculated value in the switch below, instead of applying \ * the same relocation 4 times for each individual memory load. \ */ \ asm volatile("" : "=r"(p) : "0"(p)); \ \ switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ case 1: val = *(const unsigned char *)p; break; \ case 2: val = *(const unsigned short *)p; break; \ case 4: val = *(const unsigned int *)p; break; \ case 8: val = *(const unsigned long long *)p; break; \ } \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Convenience macro to check that field actually exists in target kernel's. * Returns: * 1, if matching field is present in target kernel; * 0, if no matching field found. */ #define bpf_core_field_exists(field) \ __builtin_preserve_field_info(field, BPF_FIELD_EXISTS) /* * Convenience macro to get the byte size of a field. Works for integers, * struct/unions, pointers, arrays, and enums. */ #define bpf_core_field_size(field) \ __builtin_preserve_field_info(field, BPF_FIELD_BYTE_SIZE) /* * Convenience macro to get BTF type ID of a specified type, using a local BTF * information. Return 32-bit unsigned integer with type ID from program's own * BTF. Always succeeds. */ #define bpf_core_type_id_local(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) /* * Convenience macro to get BTF type ID of a target kernel's type that matches * specified local type. * Returns: * - valid 32-bit unsigned type ID in kernel BTF; * - 0, if no matching type was found in a target kernel BTF. */ #define bpf_core_type_id_kernel(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) exists in a target kernel. * Returns: * 1, if such type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_exists(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) /* * Convenience macro to get the byte size of a provided named type * (struct/union/enum/typedef) in a target kernel. * Returns: * >= 0 size (in bytes), if type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_size(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) /* * Convenience macro to check that provided enumerator value is defined in * a target kernel. * Returns: * 1, if specified enum type and its enumerator value are present in target * kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value_exists(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) /* * Convenience macro to get the integer value of an enumerator value in * a target kernel. * Returns: * 64-bit value, if specified enum type and its enumerator value are * present in target kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) /* * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures * offset relocation for source address using __builtin_preserve_access_index() * built-in, provided by Clang. * * __builtin_preserve_access_index() takes as an argument an expression of * taking an address of a field within struct/union. It makes compiler emit * a relocation, which records BTF type ID describing root struct/union and an * accessor string which describes exact embedded field that was used to take * an address. See detailed description of this relocation format and * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. * * This relocation allows libbpf to adjust BPF instruction to use correct * actual field offset, based on target kernel BTF type that matches original * (local) BTF, used to record relocation. */ #define bpf_core_read(dst, sz, src) \ bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user(dst, sz, src) \ bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() * additionally emitting BPF CO-RE field relocation for specified source * argument. */ #define bpf_core_read_str(dst, sz, src) \ bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user_str(dst, sz, src) \ bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) #define ___concat(a, b) a ## b #define ___apply(fn, n) ___concat(fn, n) #define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N /* * return number of provided arguments; used for switch-based variadic macro * definitions (see ___last, ___arrow, etc below) */ #define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) /* * return 0 if no arguments are passed, N - otherwise; used for * recursively-defined macros to specify termination (0) case, and generic * (N) case (e.g., ___read_ptrs, ___core_read) */ #define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) #define ___last1(x) x #define ___last2(a, x) x #define ___last3(a, b, x) x #define ___last4(a, b, c, x) x #define ___last5(a, b, c, d, x) x #define ___last6(a, b, c, d, e, x) x #define ___last7(a, b, c, d, e, f, x) x #define ___last8(a, b, c, d, e, f, g, x) x #define ___last9(a, b, c, d, e, f, g, h, x) x #define ___last10(a, b, c, d, e, f, g, h, i, x) x #define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___nolast2(a, _) a #define ___nolast3(a, b, _) a, b #define ___nolast4(a, b, c, _) a, b, c #define ___nolast5(a, b, c, d, _) a, b, c, d #define ___nolast6(a, b, c, d, e, _) a, b, c, d, e #define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f #define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g #define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h #define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i #define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___arrow1(a) a #define ___arrow2(a, b) a->b #define ___arrow3(a, b, c) a->b->c #define ___arrow4(a, b, c, d) a->b->c->d #define ___arrow5(a, b, c, d, e) a->b->c->d->e #define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f #define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g #define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h #define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___type(...) typeof(___arrow(__VA_ARGS__)) #define ___read(read_fn, dst, src_type, src, accessor) \ read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) /* "recursively" read a sequence of inner pointers using local __t var */ #define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); #define ___rd_last(fn, ...) \ ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); #define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) #define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___read_ptrs(fn, src, ...) \ ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) #define ___core_read0(fn, fn_ptr, dst, src, a) \ ___read(fn, dst, ___type(src), src, a); #define ___core_readN(fn, fn_ptr, dst, src, ...) \ ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ ___last(__VA_ARGS__)); #define ___core_read(fn, fn_ptr, dst, src, a, ...) \ ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ src, a, ##__VA_ARGS__) /* * BPF_CORE_READ_INTO() is a more performance-conscious variant of * BPF_CORE_READ(), in which final field is read into user-provided storage. * See BPF_CORE_READ() below for more details on general usage. */ #define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_INTO() */ #define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as * BPF_CORE_READ() for intermediate pointers, but then executes (and returns * corresponding error code) bpf_core_read_str() for final string read. */ #define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_str, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ #define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_str, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially * when there are few pointer chasing steps. * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: * int x = s->a.b.c->d.e->f->g; * can be succinctly achieved using BPF_CORE_READ as: * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); * * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically * equivalent to: * 1. const void *__t = s->a.b.c; * 2. __t = __t->d.e; * 3. __t = __t->f; * 4. return __t->g; * * Equivalence is logical, because there is a heavy type casting/preservation * involved, as well as all the reads are happening through * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to * emit CO-RE relocations. * * N.B. Only up to 9 "field accessors" are supported, which should be more * than enough for any practical purpose. */ #define BPF_CORE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Variant of BPF_CORE_READ() for reading from user-space memory. * * NOTE: all the source types involved are still *kernel types* and need to * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will * fail. Custom user types are not relocatable with CO-RE. * The typical situation in which BPF_CORE_READ_USER() might be used is to * read kernel UAPI types from the user-space memory passed in as a syscall * input argument. */ #define BPF_CORE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* Non-CO-RE variant of BPF_CORE_READ() */ #define BPF_PROBE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) #endif golang-github-cilium-ebpf-0.11.0/btf/testdata/btf_testmod.btf000066400000000000000000000242241456504511400241160ustar00rootroot00000000000000$$`61H/=G1 @P1`1k1pq1p@"Zjy11@1@1@1@1@1@1@1@~aD$('@/aa' =a=a@a-a.aa@a/@aaa-a. a@ bH #b` ;0 1b 6b* Bb* E(NbUb^b@gb1qb2zb+@b3@b4b@bb@bbbb5b@c6c2c{Ac@OcXcecvc c@c8cc:c@c`c<dd)d<@:dOd edwdddd@d=d2!"Z2@2 2 22@222B@2 33l3@3383/>3@D3%"!K3nT3b@f3$r3!x3&3(@3+3-3032@34337 )3-33-wZ@3,4+@@!` @0    @ ` # /;U>X AZ DcPdaTgy@` ef@ ] @ .g ?!O@!aH!o `!! " "]@"`""P#$ $ %$4&'4&1@':C@(C (N )X )d @)r)y))))*******"*4*H@*U*c@,g`,l,y0,0-@--0@../H/h/1@2i2j3j@33%3+41@47O4D@5J5Q5\6k@6s6{J6L@9l:l@:l:m:;n;; <o<p<q=s@= M=t=)>!)@>.)>$=><?F?R@_@@ju@x@@$A>ABBC C^@CC0Dv@DD wDxEy@E(zE9{ED|FT@Fc}FpGFuHHI'@II I I~J@JJK@K)K8LIMU@MeMvN@OOOOP@PPFPa@QQDRR R RS+@S:SQSWTg@TvT|`T T0U@UVVW@WWW X @X X X* X>9XE @YOYcYlZ{<\/h523@!P@Qr55`5h@5H/= @4755@556.6 -6906@96B66{ g{@Xu : @ '!! ! !0!@"!)!=4!@!K!@O!W!b!l!@v!!! > ?!Gamz+ @   @>$3@`z" @ t * 9 C F[7@Jc|> ! "0?9@ @D3 @ ` $'1@4:G@X>i" bh@y[@9%;=$$%1(''A@B C(66@6B6B6 6646@6Z66\@6_67V 7@7-777W&(24 ]@]`2 BF BH BJ BL BN B P =BR BT BV 2BX( 2[ VFE] VF2_ F22a 2sc 2e F2g*' VBk V2  m j =o Bq VBs Z=u Zw Zy8Vu@F<@'pu@F<@'@ ; 0~ 0~ 0 > >4 >  >  > >  >* >Y >7>>  9>0Hc@`F<'@& 0| 8'( 0} 8'N ~ 8'w    yy " 3 x{J x{ V]Z ` äw r ]Z Ƥ ]Z Ȥ ]Z ʤ Ȥ  % ͤ &F@ Ϥ &Y0Fk Ѥ &Y0F Ӥ Ӥ Ӥ   &F) ؤ &Y0FT ڤ &Y0F| ܤ Hޤbpf_testmod_test_read_ctxbpf_testmod_test_write_ctxbpf_testmod_test_writable_ctxearly_rettrace_event_raw_bpf_testmod_test_readtrace_event_data_offsets_bpf_testmod_test_readbtf_trace_bpf_testmod_test_readbtf_trace_bpf_testmod_test_write_barebtf_trace_bpf_testmod_test_writable_barefunc_proto_typedeffunc_proto_typedef_nested1func_proto_typedef_nested2bpf_testmod_btf_type_tag_1bpf_testmod_btf_type_tag_2bpf_testmod_btf_type_tag_3bpf_testmod_exitbpf_testmod_initbpf_testmod_test_writebpf_testmod_test_readbpf_testmod_return_ptrbpf_testmod_loop_testbpf_testmod_test_btf_type_tag_percpu_2bpf_testmod_test_btf_type_tag_percpu_1bpf_testmod_test_btf_type_tag_user_2bpf_testmod_test_btf_type_tag_user_1bpf_testmod_test_mod_kfunc__bpf_trace_bpf_testmod_test_writable_bare__bpf_trace_bpf_testmod_test_write_bare__bpf_trace_bpf_testmod_test_readperf_trace_bpf_testmod_test_readtrace_event_raw_event_bpf_testmod_test_readtrace_raw_output_bpf_testmod_test_read__traceiter_bpf_testmod_test_writable_bare__traceiter_bpf_testmod_test_write_bare__traceiter_bpf_testmod_test_readbpf_testmod_ksym_percpugolang-github-cilium-ebpf-0.11.0/btf/testdata/fuzz/000077500000000000000000000000001456504511400221015ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/testdata/fuzz/FuzzExtInfo/000077500000000000000000000000001456504511400243345ustar00rootroot0000000000000050a33736610b4a0945179db4c8a88e8247b05fbb25f50ed81e5393baf29bc5bc000066400000000000000000000002171456504511400347750ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []byte("\x9f\xeb\x01\x00\x1c\x00\x00\x00\x00\x00\x00\x00000\x10\x00\x00\x00\x000000\xf3\xff\xff\xff0\x00\x00\x00") []byte("0") 72534f53bd90cb52a017013499b11511535c1295bf0e22f856148c02454c323e000066400000000000000000000001641456504511400341460ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []byte("\x9f\xeb\x01\x00\x18\x00\x00\x00\x00\x00\x00\x000000000000000\x00\x00\x000000") []byte("0") a87a26efa64ed50b598ae8e333301d57d5f234527730f042d68ccc736e90c9fa000066400000000000000000000002001456504511400350040ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []byte("\x9f\xeb\x01\x00\x1c\x00\x00\x00\x00\x00\x00\x000000\xe8\xff\xff\xff000000000\x00\x00\x00") []byte("0") golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs-eb.elf000066400000000000000000000353301456504511400234520ustar00rootroot00000000000000ELF7X@@3a4]5Y6U8Q9 M: I; E< A==>9?5@1A-B)C%F ]!G  ]!J  ]!K ]! N ]! !Oe:f7g5h3i0j-k+l)m&n#o!ps U t U uU vU wUxUyU zU {U|U}U~ U UU* c!s!c!ķ  * c!s!c!ŷy* ط{!Ʒ  r* {!Ƿl* c!s!c!ɷ  c* c!s!c!ʷU[* c!s!c!Ϸ U R* c!s!c!зUJ* {!ѷ U C* {!ҷU=ԷU:շU7ַU4׷U1.+(%"UUUUUU U UxUxxx%(      @ J   , K   \    I  u"x  ! w ,  #    & I inttype_idssocket_filter/type_ids./btf/testdata/relocs.c__section("socket_filter/type_ids") int type_ids() {0 local_id_not_zero(int);frob local_id_not_zero(struct { int frob; });FRAP local_id_not_zero(enum {FRAP});barchar local_id_not_zero(union { char bar; });s_1_2_3unsigned int local_id_not_zero(struct s);s_t local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t);eONETWO local_id_not_zero(enum e);e_t local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t);u local_id_not_zero(union u);u_t local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); target_and_local_id_match(struct s); target_and_local_id_match(s_t); target_and_local_id_match(enum e); target_and_local_id_match(e_t); target_and_local_id_match(union u);}typessocket_filter/types__section("socket_filter/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t);enumssocket_filter/enums__section("socket_filter/enums") int enums() { enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE);1 enum_value_exists(e_t, TWO); enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE);fieldssocket_filter/fields__section("socket_filter/fields") int fields() { field_exists((struct s){}._1);0:00:10:2 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2);t__ARRAY_SIZE_TYPE__0:0:0:2:0 field_exists(bar.s[2]._1);0:0:0:1:1 field_exists(bar.s[1]._2);0:1:0:0 field_exists(bar.u._1);0:1:0:1 field_exists(bar.u._2);1:1:0:1 field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1);0:0:0:2:2 field_is_unsigned(bar.s[2]._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1);err_ambiguoussocket_filter/err_ambiguousambiguous return bpf_core_type_id_kernel(struct ambiguous);err_ambiguous_flavoursocket_filter/err_ambiguous_flavourambiguous___flavour return bpf_core_type_id_kernel(struct ambiguous___flavour); ddTb P $ %'%=%t8%X%x%%,%N%g%%8%X%x% %.%O%h% (%X%%(%,%58%(%ZPb%v%(%8%H%`%x%%.%G%c%z%%%%(%8%P%:h%Vx%o%%%%%%Z %D%NH(%oL8%PX%Tx%`%d%!h%%ZxP*%e %@%P%%%%%%%$0%$@%:(p%:(%_<%_<%@%@%D0%D@%H`%Hx%P% T% 1X% U\% % %  % 8% !P% >h% g% % % % % % -% W(% @%P%Z %  %% ],r(rHrhrr r r r r(rHrhrrrrrrr8 rH rh rx rrrrrrrb r rX rprrrr  r H r `r r r   r H h  r P4 (8X ` h x (HPXh    (HXp     0 H ` ]x         0 %r %(r(P{0R>fX",<L\l |  0@P`p(8HXhx(8HXhx 0@Phx(8HXhx(8HXhx ( DTdt$4DTdt   , < L \ l |          , < L \ l |           , < L \ l |           , < L \ l |           , < L d  | .text.rel.BTF.extsocket_filter/err_ambiguoussocket_filter/enumssocket_filter/typessocket_filter/fieldssocket_filter/type_idssocket_filter/err_ambiguous_flavour.llvm_addrsig.strtab.symtab.BTFLBB2_9LBB1_25LBB0_23LBB3_316n@m@0Dp0`X0X    @  @)h oL6h' golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs-el.elf000066400000000000000000000353301456504511400234640ustar00rootroot00000000000000ELFX7@@3a4]5Y6U8Q9 M: I; E< A==>9?5@1A-B)C%F]G  ]J  ]K] N]Oe:f7g5h3i0j-k+l)m&n#o!ps U t U uU vU wUxUyU zU {U|U}U~ U UUcsccscy{r{lcscccscU[cscURcscUJ{UC{U=U:U7U4U1.+(%"UUUUUU U UxUxxx%(      @ J   , K   \    I u "x  ! w  ,   #    &I  inttype_idssocket_filter/type_ids./btf/testdata/relocs.c__section("socket_filter/type_ids") int type_ids() {0 local_id_not_zero(int);frob local_id_not_zero(struct { int frob; });FRAP local_id_not_zero(enum {FRAP});barchar local_id_not_zero(union { char bar; });s_1_2_3unsigned int local_id_not_zero(struct s);s_t local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t);eONETWO local_id_not_zero(enum e);e_t local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t);u local_id_not_zero(union u);u_t local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); target_and_local_id_match(struct s); target_and_local_id_match(s_t); target_and_local_id_match(enum e); target_and_local_id_match(e_t); target_and_local_id_match(union u);}typessocket_filter/types__section("socket_filter/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t);enumssocket_filter/enums__section("socket_filter/enums") int enums() { enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE);1 enum_value_exists(e_t, TWO); enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE);fieldssocket_filter/fields__section("socket_filter/fields") int fields() { field_exists((struct s){}._1);0:00:10:2 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2);t__ARRAY_SIZE_TYPE__0:0:0:2:0 field_exists(bar.s[2]._1);0:0:0:1:1 field_exists(bar.s[1]._2);0:1:0:0 field_exists(bar.u._1);0:1:0:1 field_exists(bar.u._2);1:1:0:1 field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1);0:0:0:2:2 field_is_unsigned(bar.s[2]._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1);err_ambiguoussocket_filter/err_ambiguousambiguous return bpf_core_type_id_kernel(struct ambiguous);err_ambiguous_flavoursocket_filter/err_ambiguous_flavourambiguous___flavour return bpf_core_type_id_kernel(struct ambiguous___flavour); ddTb P $% '%=%t8%X%x%%,%N%g%%8%X%x% %.%O%h% (%X%%(%,%58%(%ZPb%v%(%8%H%`%x%%.%G%c%z%%%%(%8%P%:h%Vx%o%%%%%%Z  %D%NH(%oL8%PX%Tx%`%d%!h%%ZxP*%e %@%P%%%%%%%$0%$@%:(p%:(%_<%_<%@%@%D0%D@%H`%Hx%P% T%1 X%U \% % %  % 8%! P%> h%g % % % % % %- %W (% @%P%Z % % %] ,r(rHrhrr r r r r(rHrhrrrrrrr8 rH rh rx rrrrrrrb r rX rprrrr  r H r `r r r  r H h  r P4 (8X ` h x (HPXh    (HXp     0 H `] x        0 %r% (r(P{0R>fX",<L\l |  0@P`p(8HXhx(8HXhx 0@Phx(8HXhx(8HXhx ( DTdt$4DTdt  , < L \ l |          , < L \ l |           , < L \ l |           , < L \ l |           , < L d  |  .text.rel.BTF.extsocket_filter/err_ambiguoussocket_filter/enumssocket_filter/typessocket_filter/fieldssocket_filter/type_idssocket_filter/err_ambiguous_flavour.llvm_addrsig.strtab.symtab.BTFLBB2_9LBB1_25LBB0_23LBB3_31n6@m@0Dp0`X0X    @  @h) Loh6' golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs.c000066400000000000000000000132271456504511400225430ustar00rootroot00000000000000#include "../../testdata/common.h" #include "bpf_core_read.h" enum e { // clang-12 doesn't allow enum relocations with zero value. // See https://reviews.llvm.org/D97659 ONE = 1, TWO, }; typedef enum e e_t; struct s { int _1; char _2; unsigned int _3; }; typedef struct s s_t; union u { int *_1; char *_2; unsigned int *_3; }; typedef union u u_t; #define local_id_zero(expr) \ ({ \ if (bpf_core_type_id_local(expr) != 0) { \ return __LINE__; \ } \ }) #define local_id_not_zero(expr) \ ({ \ if (bpf_core_type_id_local(expr) == 0) { \ return __LINE__; \ } \ }) #define target_and_local_id_match(expr) \ ({ \ if (bpf_core_type_id_kernel(expr) != bpf_core_type_id_local(expr)) { \ return __LINE__; \ } \ }) __section("socket_filter/type_ids") int type_ids() { local_id_not_zero(int); local_id_not_zero(struct { int frob; }); local_id_not_zero(enum {FRAP}); local_id_not_zero(union { char bar; }); local_id_not_zero(struct s); local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t); local_id_not_zero(enum e); local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t); local_id_not_zero(union u); local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); // Qualifiers on types crash clang. target_and_local_id_match(struct s); target_and_local_id_match(s_t); // target_and_local_id_match(const s_t); // target_and_local_id_match(volatile s_t); target_and_local_id_match(enum e); target_and_local_id_match(e_t); // target_and_local_id_match(const e_t); // target_and_local_id_match(volatile e_t); target_and_local_id_match(union u); target_and_local_id_match(u_t); // target_and_local_id_match(const u_t); // target_and_local_id_match(volatile u_t); return 0; } #define type_exists(expr) \ ({ \ if (!bpf_core_type_exists(expr)) { \ return __LINE__; \ } \ }) #define type_size_matches(expr) \ ({ \ if (bpf_core_type_size(expr) != sizeof(expr)) { \ return __LINE__; \ } \ }) __section("socket_filter/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); // TODO: Check non-existence. type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t); type_size_matches(volatile u_t); return 0; } #define enum_value_exists(t, v) \ ({ \ if (!bpf_core_enum_value_exists(t, v)) { \ return __LINE__; \ } \ }) #define enum_value_matches(t, v) \ ({ \ if (v != bpf_core_enum_value(t, v)) { \ return __LINE__; \ } \ }) __section("socket_filter/enums") int enums() { enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE); enum_value_exists(e_t, TWO); // TODO: Check non-existence. enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE); enum_value_matches(const e_t, ONE); return 0; } #define field_exists(f) \ ({ \ if (!bpf_core_field_exists(f)) { \ return __LINE__; \ } \ }) #define field_size_matches(f) \ ({ \ if (sizeof(f) != bpf_core_field_size(f)) { \ return __LINE__; \ } \ }) #define field_offset_matches(t, f) \ ({ \ if (__builtin_offsetof(t, f) != __builtin_preserve_field_info(((typeof(t) *)0)->f, BPF_FIELD_BYTE_OFFSET)) { \ return __LINE__; \ } \ }) #define field_is_signed(f) \ ({ \ if (!__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \ return __LINE__; \ } \ }) #define field_is_unsigned(f) \ ({ \ if (__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \ return __LINE__; \ } \ }) __section("socket_filter/fields") int fields() { field_exists((struct s){}._1); field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); // unions crash clang-14. // field_is_signed((union u){}._1); // field_is_unsigned((union u){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2); struct t { union { s_t s[10]; }; struct { union u u; }; } bar, *barp = &bar; field_exists(bar.s[2]._1); field_exists(bar.s[1]._2); field_exists(bar.u._1); field_exists(bar.u._2); field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1); field_is_unsigned(bar.s[2]._3); // unions crash clang-14. // field_is_signed(bar.u._1); // field_is_signed(bar.u._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1); field_offset_matches(struct t, u._2); return 0; } struct ambiguous { int _1; char _2; }; struct ambiguous___flavour { char _1; int _2; }; __section("socket_filter/err_ambiguous") int err_ambiguous() { return bpf_core_type_id_kernel(struct ambiguous); } __section("socket_filter/err_ambiguous_flavour") int err_ambiguous_flavour() { return bpf_core_type_id_kernel(struct ambiguous___flavour); } golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read-eb.elf000066400000000000000000000223701456504511400244450ustar00rootroot00000000000000ELF"x@@ k,q/qUD3"VV{* !0e00 0 i00yqag  <w<NU* !0e00 0 i00yqag$  >w>QU* !0e00 0 i00yqag'  >w>TUw* !0e00 0 i00yqag&  ?w?WU\* !0e00 0 i00yqag)  ?w?ZUA* !0e00 0 i00yqag*  0w0]U&VV* !0e00 0 i00yqag  "w"`U D3"cUfUi{   vxz|ބz x$ & ' ) * @   %)-@9G S intread_subprog.text./btf/testdata/relocs_read.c__attribute__((noinline)) int read_subprog() { struct s foo = {sbachar0:1 if (core_access(foo.a) == 0) if (core_access(foo.b) == 1)0:0 *p++ = 0xff; // a, b, dbitsxcdefgu8unsigned charu16unsigned shortunsigned intZEROONEu64unsigned long if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1)0:2 if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1)0:4 if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1)0:3 if (BPF_CORE_READ_BITFIELD(&bar, c) != 0)0:5 if (BPF_CORE_READ_BITFIELD(&bar, e) != 0)0:6 if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656)0:7 if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322)nonexistnon_exist0 if (bpf_core_type_exists(struct nonexist) != 0) if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0)nonexist_enumNON_EXIST}readssocket int ret = read_subprog(); $$(Y=5d (0@Xp;4;4;4;4;4(;4H;4`u@u@u@u@u@u@ u@8LpLLLLLLXHXXXpXXXXd d0dHdpdddGpGpGp GpHGp`GpGp{|{|{|{| {|8{|X{|pQY` Q/(Px(8Pqpqqqqq(H pxCCHCPC`CpCww w(w8wHwh U8Y@ Q Ip1|AXHtpy0lX qphH)0iH9@! pdxa\ HP%< ,<;P`p 0@P`p 0@P`p 0@P`p(;8;Tdt$4DTdt$4DTdt$4G.text.rel.BTF.ext.relsocketreadsread_subprog.llvm_addrsig.strtab.symtab.BTFLBB0_9LBB0_89LBB0_79LBB0_59LBB0_49LBB0_39LBB0_8LBB0_88LBB0_78LBB0_48LBB0_18LBB0_7LBB0_97LBB0_87LBB0_57LBB0_47LBB0_37LBB0_27LBB0_86LBB0_76LBB0_66LBB0_46LBB0_36LBB0_26LBB0_5LBB0_85LBB0_75LBB0_65LBB0_35LBB0_74LBB0_44LBB0_34LBB0_24LBB0_14LBB0_83LBB0_73LBB0_63LBB0_53LBB0_33LBB0_23LBB0_13LBB0_92LBB0_72LBB0_62LBB0_52LBB0_22LBB0_91LBB0_61LBB0_31LBB0_21LBB0_11LBB0_70LBB0_60LBB0_50LBB0_40LBB0_20LBB0_10@ Y@ @H P  0D @X 2oL XHx<golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read-el.elf000066400000000000000000000223701456504511400244570ustar00rootroot00000000000000ELFx"@@ k,q/qVV"3DU{e  i!y!q!a!g<<w<NUe  i!y!q!a!g:>w>QUe  i!y!q!a!g7>w>TUwe  i!y!q!a!g9?w?WU\e  i!y!q!a!g6?w?ZUAe  i!y!q!a!g&0w0]U&VVe  i!y!q!a!g""w"`U "3DcUfUi{   vxz|z x$ & ' ) * @   %)-@9G S intread_subprog.text./btf/testdata/relocs_read.c__attribute__((noinline)) int read_subprog() { struct s foo = {sbachar0:1 if (core_access(foo.a) == 0) if (core_access(foo.b) == 1)0:0 *p++ = 0xff; // a, b, dbitsxcdefgu8unsigned charu16unsigned shortunsigned intZEROONEu64unsigned long if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1)0:2 if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1)0:4 if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1)0:3 if (BPF_CORE_READ_BITFIELD(&bar, c) != 0)0:5 if (BPF_CORE_READ_BITFIELD(&bar, e) != 0)0:6 if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656)0:7 if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322)nonexistnon_exist0 if (bpf_core_type_exists(struct nonexist) != 0) if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0)nonexist_enumNON_EXIST}readssocket int ret = read_subprog(); $$(Y=5d (0@Xp;4;4;4;4;4(;4H;4`u@u@u@u@u@u@ u@8LpLLLLLLXHXXXpXXXXd d0dHdpdddGpGpGp GpHGp`GpGp{|{|{|{| {|8{|X{|pQY` Q/(Px(8Pqpqqqqq(H pxCCHCPC`CpCww w(w8wHwh U8Y@ Q Ip1|AXHtpy0lX qphH)0iH9@! pdxa\ HP% <,<;P`p 0@P`p 0@P`p 0@P`p(;8;Tdt$4DTdt$4DTdt$4G.text.rel.BTF.ext.relsocketreadsread_subprog.llvm_addrsig.strtab.symtab.BTFLBB0_9LBB0_89LBB0_79LBB0_59LBB0_49LBB0_39LBB0_8LBB0_88LBB0_78LBB0_48LBB0_18LBB0_7LBB0_97LBB0_87LBB0_57LBB0_47LBB0_37LBB0_27LBB0_86LBB0_76LBB0_66LBB0_46LBB0_36LBB0_26LBB0_5LBB0_85LBB0_75LBB0_65LBB0_35LBB0_74LBB0_44LBB0_34LBB0_24LBB0_14LBB0_83LBB0_73LBB0_63LBB0_53LBB0_33LBB0_23LBB0_13LBB0_92LBB0_72LBB0_62LBB0_52LBB0_22LBB0_91LBB0_61LBB0_31LBB0_21LBB0_11LBB0_70LBB0_60LBB0_50LBB0_40LBB0_20LBB0_10@Y @ @H P 0 D @X 2LoX Hx<golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read.c000066400000000000000000000047421456504511400235400ustar00rootroot00000000000000#include "../../testdata/common.h" #include "bpf_core_read.h" #define core_access __builtin_preserve_access_index // Struct with the members declared in the wrong order. Accesses need // a successful CO-RE relocation against the type in relocs_read_tgt.c // for the test below to pass. struct s { char b; char a; }; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; // Struct with bitfields. struct bits { int x; u8 a : 4, b : 2; u16 c : 1; unsigned int d : 2; enum { ZERO = 0, ONE = 1 } e : 1; u64 f : 16, g : 30; }; struct nonexist { int non_exist; }; enum nonexist_enum { NON_EXIST = 1 }; // Perform a read from a subprog to ensure CO-RE relocations // occurring there are tracked and executed in the final linked program. __attribute__((noinline)) int read_subprog() { struct s foo = { .a = 0, .b = 1, }; if (core_access(foo.a) == 0) return __LINE__; if (core_access(foo.b) == 1) return __LINE__; struct bits bar; char *p = (char *)&bar; /* Target: * [4] STRUCT 'bits' size=8 vlen=7 * 'b' type_id=5 bits_offset=0 bitfield_size=2 * 'a' type_id=5 bits_offset=2 bitfield_size=4 * 'd' type_id=7 bits_offset=6 bitfield_size=2 * 'c' type_id=9 bits_offset=8 bitfield_size=1 * 'e' type_id=11 bits_offset=9 bitfield_size=1 * 'f' type_id=9 bits_offset=16 * 'g' type_id=12 bits_offset=32 bitfield_size=30 */ *p++ = 0xff; // a, b, d *p++ = 0x00; // c, e *p++ = 0x56; // f *p++ = 0x56; // f #ifdef __BIG_ENDIAN__ *p++ = 0x55; // g *p++ = 0x44; // g *p++ = 0x33; // g *p++ = 0x22; // g #else *p++ = 0x22; // g *p++ = 0x33; // g *p++ = 0x44; // g *p++ = 0x55; // g #endif if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, c) != 0) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, e) != 0) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322) return __LINE__; if (bpf_core_type_exists(struct nonexist) != 0) return __LINE__; if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0) return __LINE__; if (bpf_core_enum_value_exists(enum nonexist_enum, NON_EXIST) != 0) return __LINE__; return 0; } __section("socket") int reads() { int ret = read_subprog(); if (ret) return ret; return 0; } golang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read_tgt-eb.elf000066400000000000000000000023601456504511400253200ustar00rootroot00000000000000ELF@@llz     " $'5 < I M\ae iusabcharunused_sbitsdcefgu8unsigned charmy_u32unsigned intu16unsigned shortZEROONEu32unused_bits.bssp|.textunused_bits.bssunused_s.llvm_addrsig.strtab.symtab.rel.BTF/H@@C@? @ !oL7@Hgolang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read_tgt-el.elf000066400000000000000000000023601456504511400253320ustar00rootroot00000000000000ELF@@llz     " $'5 < I M\ae iusabcharunused_sbitsdcefgu8unsigned charmy_u32unsigned intu16unsigned shortZEROONEu32unused_bits.bssp|.textunused_bits.bssunused_s.llvm_addrsig.strtab.symtab.rel.BTF/H@@C@? @ !Lo7@Hgolang-github-cilium-ebpf-0.11.0/btf/testdata/relocs_read_tgt.c000066400000000000000000000013101456504511400244020ustar00rootroot00000000000000/* This file exists to emit ELFs with specific BTF types to use as target BTF in tests. It can be made redundant when btf.Spec can be handcrafted and passed as a CO-RE target in the future. */ struct s { char a; char b; }; struct s *unused_s __attribute__((unused)); typedef unsigned int my_u32; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; struct bits { /*int x;*/ u8 b : 2, a : 4; /* a was before b */ my_u32 d : 2; /* was 'unsigned int' */ u16 c : 1; /* was before d */ enum { ZERO = 0, ONE = 1 } e : 1; u16 f; /* was: u64 f:16 */ u32 g : 30; /* was: u64 g:30 */ }; struct bits *unused_bits __attribute__((unused)); golang-github-cilium-ebpf-0.11.0/btf/testdata/vmlinux.btf.gz000066400000000000000000054562011456504511400237360ustar00rootroot00000000000000TE{'0a9gr9D! $ 0!*D@Q$$1}ou4-ߵz3ԩkW< xԄ ɳw㛗ΰ(zܐVYϐS=GNǟFyu#Xr*8N${1g3F\#3yp1#.c|9'xA 8yJ?($pp!\i;\i;\ikcUxY{ yZrznN·tJVpҼmPu$KSK=IAY2p:#Oy^ ;Rr9"ցd ep %4O"ພցZt:\:ÿdy𢡄DMޯzclzOXlW_>uf$>UICz%V,q J8rوqw8_=:N#tCяc=gߗ/ޓO#CٗivCxn#7Ox{{(erLI%$v 82|w>s.rԁd;[R9SId*Qr]di"@ ~N#4o>m_&*g`_,I9̟s(Lb>$|I?%]Y#GG.y%ˀD/ LHcNr+XLeoN-{ rSdiVF?Sb}- zoqx&̾myIN|9aKaɟ8qV~'L6'/:;yB9IÕk̿!P&kF IgI繑27Vd ϻ薔1OJ+&'7Cn An/g$MF߮hhPF[fFfqF/h%[*xoRF_]x3lYJhY2|Fߢ˄ T_[&um%kwr^5oO}tB.dӧ]&ݑɶA36hx{?+mL,dتL6f.M3ΑZ*T!Qv;fg6dYWNgl%3"??6,r,?1~,7YRdϮC?s{8<,%??Sɋf\v\q!eud(8ݞauy?)s;x54xM?/8,rG#}?q6^񵗜ߥCZ f&'ᛇl#a_>n% }Eϡ[xn$9F5_yNy׎:)&ܓSK3O3 _BΩs y&ޘ 9iၯTBb:nߤؘr곤.ʈy0o8iq`L.m3e!9,mK p\ ?4!r}# Rp_p*JOsջ,\]LWIؠd3j(r/xK.MT\Z'qp|n^` ^>\<,ydLNߟe P r^) md8D1!pʱ8pqr<&9:/ʣKw{E-9^M;9\InS 69o }hc% ]Pnρ[ ]= x<oA8<8!eݠ+ Q?S](eCggIɳ q|(%T!s 隈dfl'[H4Bz!9">+)^}xfa0ǽ@Apy^z`w ߢ0۸>Б_^4{}Q? A Y2U'ς׬,CfTU*lqš 949>GnN@aT|f{k;y8'p2gG^OPɮ0^ W0`5׳Ӈca=E7oa\2a9{,rr} Eu=ggd<$=n+C튐;*b xg5;a;Z%-}[?-b:#HI3a=40Sa"üL WT2d4Yd4:NL$AܽQĖ-Et|vu*V>ݼ\>2b|8˩"ZR։El\։MNxz EyoLpc8sȚvHN<NvLcM_:,22=D$_t`Ţ S~eϫW4\AaQǙ2._ 26aWE6™1Ȣϓp?yad,}YeࣞcZ9A祢o$:#\ɼwcY<;#cox_{ٱ_+Uoș9'tBIl:nn\$$'$l0ɶoM$Ў ;-Нv*0]tOuoI6gR^I:?&0dחJN3/4إ'~'#-1_|=4=$Ox-(Ũ@y͌dIuŋg=sY̮eM.fײ3;{U(4qR܆'eⶭRꖻZd'}8Mk@>2&ft +ccxG1E)m)ˊ.ς#w/5/\OmWփovҤ3>];}~\~fK~bcJ(˜CسJve͐AOl_D&9}ñ%l.Q"V9 e$>K~0f)aԝd vm%l, E~9 .s9]@-"c~ oKⱇ-##oɁJFɧ~ 27W' 1\o1̫&%mzJRKI^v 'Ǩ7֝d)BJ/ϕԶY ]dYo|)?2NԶG>J6S=^R49xgens ,?TL>Y] CR/KF5SSW;$ob;Mq|gyd)*K6]);6BQ1N܋YY_4'w*e7TWI.HǛCQꦥҁ2 L&dP?.m a(]ߪ/O?͸CI^#+ܗҭui۟x)~nݤ+36_CJʨҺGJ9yy03rdN/tMogeiōK^}ݑyt5bK6;ycj;:F]餧Ͽc4t'Xd,co2~8c幅et|ʁ++kkm'&}.el-c۝N)R:b2%S +cض5U_/c틏4`>/㌑Ѯ_ߖuVxv_)cF~lbwÃe;)Z֦w73x֟em?QDLLȷ7fZ->dYC8d6~甲9w3Җyj#ǽγvM%j~ǩv"9Ŷ~ əg(kIrv-A0P9v\9vj9MN>|||țWenPΎc-D9?YNE~e9l/g}W9;>:n€?g>r-vF9dH~V>`yܒsny[Zsy[y\oӍG+T6 wtL w,Í 6Ui,iUMzY73i VmɒV*;;荷ڡ:Jڋ1XϢvp=S.;|E;"mN,YjEimCm9Qіm9-'+rkE[h֬y0zWW˒_K?C6Hv~-&^}e2o֟udן!nD6O8c+ñ$ 2?? 9?0c'q}; 'vܿpg/2FFvκ9=s:/<.{,_9k2DJ3VtqmWIcE+CHV(ty{;-d3^X,㎟g'[n<&'}ڍ)akZe32E8a<7$Ns:<{GC5UѺ#{rUѽ~FF؄I^dұ%vYpIm-\lA'2[~ aX Fo?>6ȳCؾ}UCUt;̸ |Vcwz Yen%Wy>*nSZ ,2v=_ H\ՓA] Av~r!g |$ݓe{'Ι<'q|}99A_Yn+?"(H7{hS|>7q-뿸\7q W7ͺ_'_#3ɑy5X*|f@.Uu #7l\Yz*YoE<+ݤ{G̡{./ͱ)sy^Fy+dY㿙,kh>ud$K[|!@@lsGc=s5g-E.[͆n|q+{ !/tuOjv\3xFHuO xfHu=ǤY Y31Fu)bT5(.-3eCڧ\(Cڧ|\)}`Ld:pu5EL #%~Ha˒e_v.bS;=OF-:6-kM;F"{}1t7=(~\W^ӆNM~Kę\Nm [ [JMD? iLI,XelfM(L=fxmSb٬=f 9Ij3 Xu5)9y]#:zP/[\۲r慳0QίoWpB-.|,~)ZN:H%L改!֑2GtE̿2ײ} ?,2NO-]Pl|.ͼyYZqJrINaaޗ^_5g͸k&)>I-kuvg-;dSK"mgXDU?8| 7W-=K|>).=S/X*3򾪵mնiֶiqγ2Sc 86cKr#f;3c8ٔ v͔'IS9&WPҧ6)uͨS 6Țݬs4}a LɐaVm[ֶs=j|yY_1=|ߠںgLqm=c@}Ie~ jum@Dπ#p?̭:L[p:vR;!tp/u4%ox46zv\g_;6ۖ?fǑt3kp$Y#ix_;sS5-*yϹu͹\:h*P)IQ}1UYd/:!9'C悊ӿŝea+dP\Gdp\eUmlYf {l=B=rA_Y/g`Xhu1Թ@]{$;9sַ9%^qR kJO]3pT]M7P]" s%'菜eV> \u_{r]!xC]Y~U{pGǝ|>LY'T*|֯Wo]=;eH=Ozvg8=CN)#zzVOt KE,zb?ZO=10#/ճ x XƸV?XOz9y. %$#g,MOU ޴5Xn[t}ַkշkR_e>7۴_}nW|[‡7Ě3k8&@e^Y'Vݑöv|yɞw}_lߍ?0Ӟӆ9chh]/goسۏO=v^6ah .0t7 j\LCRGmgl MǰZ~Lh Ƽ` RpU+Y6|]"a{MuN|jH|@ Yr)K]GSσ%ݍ XI= zy'v/gEP*͙kia] }y~:|nlh᥆6ӊE]-~XIXʒ?7ֳDW3$_d~DnZE5e|6vY$px;.ud)dc qlriɶ,Hdnl}jqA#/F=qSJkoF^ʹGn' Sa;^oE a7c<n׍7/i<~V޶'I 2&!?}e a3f_H 3 4e\4ܥin3Ui`h3#Lfm:fOiU 26mq(\Y>CsMyarsq$]{=xϞz}wuQ-ԯV-ԯ-ԯZ_3Z_ϴн[hڼтc-EZZ.lRU>ZK-Y=[FgMnZR>k[g[j>J[[V:cb+E;jicߴ9εwJƴ^<ʻVJ*_ZߧҴе_!*4?-l\yD+ylk}ɕ6ml*FӺfMm4{ѴFz\MimoyO7Vͥkz]׀5ljwm˲bxP{ikXmk hkmMmTwxWm{E>絶{?Z㞳wn<,An@':z!9â|Y?}4Ab5ӈg~aΜKbϜz͙siigI ̙srƂ9s.M bʣe 9ȩ4pq#E]ȃkyp#ÞU& Y9.U%K ,IBQt`BYWy\W_ov>b9gLpQ􊳯|=[NW)G_=ctR]گt?eK\c"vrE'K=JzTۢӢ[tdy}dSdSl0G ^<ƯGvqY ^ w˧ľxUI?W}n O*Qk~//)1zH 6uT[Et6cg|}%oW>c/*S³#>oiߗ6.w4\Ct{ݸq= d3-oƝcK62ٚ,$ݺwSC2:{>P9v Y`[~|5FmrIc^ٕ=.qSK##eiW I9>@ d)cR>v |+7;BYJ\U[ |oCA-Km>v_0܉pn!?#iY0M,Mfmݴ t% +1Wc4o^|D?%;}d n^|ߍ$gq,x$׺ê;t< $Ww[pa1ZJvy`yԅݩoQoHI1Z/vWߝ媠 ӎ?,Z>eot$ݻ& rlwG\]xo<]Jtwgs`Iѝ8P߀ `\ k{p_O k׃%pT rZ g1 zk`B sj)ϛi[laԃuit Egr4vSWv8Q҃1羭bt~&;߿u~~̉c:r79Bڠ'o_'{5e4φGy=]=^I{g{OGst uŹ\;r S2!ikig{ڳg!:Ytl[rJ5c+~KN=ՖH g<<;={ぞ?çԾq^OdIOkoZ+L{j~F9jO;%6ynNHy9WM^,HO=*9ӮԓwGIԓZ^vl.ba'7wɻlY$ϥ"ˎi?wJ7 yLvBp)Y^v{~։a5e'm CA?p~f^vlc\t}E/`v_Mic;Q6.p^v=ɍ^,QOF? 5ݲZ`6G2\[r3.Cup.c#?ۦ#VЭSoH>MA'Z>mԭA~77^lw5۰MHu<|#>^"3N~xUp==SlvM?Bg\Ng 1^:!{ {f㵟 l~ۮ9ۮԛ`ѿA]ˀo@[9FnjcX }ݝNO)!:+ urKSFǂV'cK;~Vc[cS>}Fi6g 򹿏=>ZN h97i90GW0~gmcR6X.FbllŽ(Oscn:wpKL/39h62%wg}l{>Zd}'n8*֗:!Vd5k֔H$k:i.D7e䌣G6Ig #t__;?]ݽEek>oz6E_{W}zr]%T;4 }ޔk}pL?Oɐx~Y[D?[hq\xmJwp~dlqAX~VgL08?m?7xe?;Uچ32.N?;uډ/MޛL=i!ߔϯ c43l+ut]ж:5x IۡFbd [5-ܕyю,ӛ,i2#KwvR>O s'ʹ}FN{twZ9 |HLi&ac_yO83i> q <0Fy |g .V%tc-]z;bt>QpdGIC#5u[3$:w"{PsaQ!w~{;]knLywlL#w3 gYK)U^Y]YGt;캡w:WD)vl`H8O`Z%2N񕱃O9N'JQ*kLIax<4{W89 {}f 3#m';rc]Gw-ֱ7:QcOMH:4:!?pG#Vnz_4dOlj΀|X͏9±: grB |:pXOx\9V6dQxc2϶\EC^Ⱦ#S~IWj[=+[䥼G>yOd89Sc imb?tltn Nqt[?_ o~Sϟ91wy<,OPߗ/ ~q~1S}&md1ڶxm^&}D|n LE^$ZQtY3IΞGc߳{;mcQi9srߏ];sNx=.Jxfx?1Et\M-u;iKIgW:Jznz?ґ[mVpE{&'//%Fn#&Jw{8edDKWooD\Eޝ| (t38-'룤MO'6D O|̥%;|f!vx g{ކj{f}c?O5( b "+^748lc|[H nv" iH}  ҭO1%ju I;SF2psa'{{%nGzww;ǟrQڦHV:EuSw<]uFi{0Ν/aC8|Qe.n4Jp?M?k">KNnϠ8꧞}\AYÇ>nLqjGf Mopw0\\w,ymnO/Ƥ Z17U2gpx+O=ZOӶKs}8$VRwݣktcp\l+ z,~mll+|,:i Yt l+Ob[yt<5:)vg!%^vw{90/6k" ncП{F\ޗeYxy㗱qlɄ[%l"|Eg?֟9ՑK[Gwv'\fccuD36w 6r&s(y+_$|%9e<<~/\-#u|4o-;B7y/en֦NR#\Eyw-dE<*kۭ-tAxWs̹ԈRkG[Y唻ȵ֭uމSj;yS?J74Jl]h[#o rϋ_[B7rԃ WC⾏E:lsoU:ݢ|Nj蔭.AcûϾw';"lv%M<7c_dqy\(q7~Tn%iٞy~PVLs?ѝNqPힻ3a(:ۿy^>8~nNT<&J:?3 ~e|6:rkʸN>?iOtj2MkwM#NL[n!4%JOwufs玜`RD7^xY_Ze?INOҿ=^ޫ: 5߲y tHKy2R0$=@ش΁G}'#G#^4R[kxƑq5'){gg};JǮ~g]k]C천yvh=h=zv:e]6o]h=zvٶzvFeGe-߫µq(qWQqEqܫNU^Ӷ^m+^ŵ*9Fg\u*ǪĵW9}\r4>ppq]u3Tbbsq-qk+\ܯgw_*G=5Kq)#67BWg w_~zJ2gaH9v_ɲvRuo1vr1"k7. Bh\sCm-@' %.}mXg'CNxL@f7F|Rx{mϔgzcțswYT)3F"3w u*~Rjރlmzc䷻ɲo,YMvdy!Mܲ~xȮO]8!ip9Y*"!UNNs*˗}aW픱LGV?˵[D9s>4p;6^0HM~.RՉ9'92K_t-]~&vMΙ:Ir/l5o;Oqߦg&庂;~᰹_8ﶇ5n)ӑq sygg"c*] ^~, {^i&:#c"1(Oy6WBۛyDqh szHU瀈?iMc cyߛC%lsI1:f$~frj|!2F$u&^_簂GL9γ <u߹بLy0$|ŶRKz-g;ʹ vvl0vD2g m Q3{3~o'r8WV{M2?wb5.ﲭ^]Y߱|&$%d[ 6ɮ/@>.I?IS,Yae]v; 12kPeIvLOqjߝWe*\ZH 2w=sk$.&=֓Iv-.ʯӼn=Ik}mTGJޘDAI Ӹ{ĞY]<>V^,k|E5 1QeMɢڃ3k"~D(zmYO{#Xs9Ky xS Kԃω;z/NT q:$`U19Fҡ9&8OCȴwnyG`C ٤]>I.?9d-K&k]am=Y^'ƒ9YϢ:\M趉4m=V#ɶ0 d[vǟ'K6=_ھy_goWK1L yRyT-+I߅u^ ǜr<bΜY,3m!ps7äG)Mazt.{),29hڟgDLvsuTu1n z~;Y>MrvO)#4SXm_OQAdOѱI#K3%bmmN3Of^L`)l#3쟋Wpm)v\3k0spR& L:}|~bT.OI?Lx ܦ>CV/ҏk(p g"Z揳t E3%j0JS5ZSS}|LtYgYM3Ukk̘qxvq9u"YکAiurA^sޛjmzyoj#engӗg%{)lq´)}_OGʥ3˄Ӻ2ՆM>YivN&44[ij4i:MmmӴ>a4Mm:'SxԞ tk Fop~w1MF?>c> ֞t{h*xq(1IG @ntkDLWO pԘu3ghgmwt.壺rأkg?ʣwcZ>[?Ǵ|{L4 /?scZ>{LǴ|fl-ɳ|Z>gl-fk1[Z>u2s|&wh?GkQ}x >7G\sump6X>vMryݽs5fմ{nvj}7W#4Ӵ64ni7zyvӴ{ky4~i]5?iqMAkMx\nvgkk?x\qe\|qsϖ~~6_h7_;_>_x~,|M-5tnۯ5 Xh%-t1&  4Lh[a@ k 4L[h.0]a{I> 9Pf>cB .gPy>㫅 '4x|B '4ޏ>~ -/ -/??yEZ^r-R~ۂEZ^z/b[HEZ^,"k_-Һ"kZ .ֺVmֵ ]umbk,u,u뺋3*%]pD?D__YiKt7Kt%?R]_voT߱T_K::KuoKu~'_I]I]IcqO:O:uu>c:t~e2]?v|iKõ \嵥q~|S"vŵ.p튫Jj=?\&>W^ԫQWq򴶁ݟvާ)z;/z<\;q?uU>9=P\UكRp#+{$qc*[^up-YX}n~)x*Kf3=#鍫,] WYsGWۀ+a\e:syk{Utx^uS<:UoWr/WPcU\:`r-Wa=U\u@ʭPv+TD[i`Ζ3g=HߌGJ\S^V俉"o4 FWßMtO|_o{gpJ:9[Kr-:!'@3;q9nS,6~m!xK?O|&VqcΧ8q_9 9|cvk:-x ;&|%Og_w?/P,{T&VWV|3mela@,1:Zπd2=2fpi>s16}dj_αk[# i󁋭83: ie<Ώ^6)OxL)5Wijr{Q߯ѱ4wI</m~Lvk߮kl7X_;DN>K)\tk&иw vfuY?Ν%KB\6%.qZG2,e#8&NSE! i( 8-R6Ȉ#O&k>F˂gyd/rwR6>i0eFw2>Mye4Y":e.ၗ<,yKvM'eoȦOO1 . /+g!"˺gȲurk9 7"9a{ ]_$O!˻b~IȢۃkp0p-NȲ`"yxZC.>9. ]HWU?8\ܗ\|/.1SϑȝȽS^cEo#/(+g2ed. }G{g$ +_!)}ڜdٿYizyx^OWu|q&Z.x${{ldvr 7d?Ǜkhor~ 4$oqWd1ϒh%5>W%ӳSr (߶\X|O$?$: u{*x;em̞u\>)a;`zrьvܟlU^g;|qo2(.!g=x;\~?c\'/"yXLNےz;$7]}2OP-NaK;llpi .mo̻l{skwB;M˼p":"c6vO:|ѶE x 6؃/+}z<- & ZN$ONعι}nдkk7?6jWmvn FFݨ~L3yҍ:ֺfߨ[7<=cp q<bcQsAߣ-Slg]8a 7r z#y=ܬ42 Ӵ<{Z3nt)I:Y7i:ݾIǠlzl7>ٸɮ71>cؤE3~lM~ {/c6bG&-ߏ{*Toƽf{v-a:<|YƝǂ2^Lm7܊cdislG_~j]/Y³,vYYe:a S[N-k\_mo۵k߷kozd<8el~/6iko3蘫,Sgs-vIsS;̣u%˜X- J>:g(/qKwoL wO%EHl[px-Y}Y,ڻŦ7d~½`*7̽f=* J]V-Nzl[~@?t,glgQ|<,JdOe l#KP@@PB'>!}thO,*PѬ$pY'*A,bD;}# T[LOެ}Y_ꆫO{j^_2+<+#=dU&H>z5|&܁g6H$UkPƫ3n>t8$y,{&xÌivH;/epdFƩ ~9Nm+Ԇ^vqj7?בn~=Noi`6M#)m '8F.H. X; E..5Jٺ0\ Y'@srp{rSp5.Z9?~Z$rR}c#&Z܏?=x/=9>O ǁslW0*tnAAF G9{tj6xxE=Ey5<<'۩G`pAd,@7ISxlΩWӼgpؼ6̝Kٖ|*5>\8Ytq+.dŃ>O8W/eݹ^5i*kc{ϤI;I߁nclJ u'iz= eE{.ݤ[9cEyTG>.{C8-5 l惱g$L$u;3N>5GrD~q龿b6]qG19M"ch{WӺaƿVT}eg~]'ξ%N9?g 3\jCHha|eet |{v8>c)޻֑}P2eeæf߭d{n;R~mkwxPƻԃiuȽ;wE5\/_&s n[>y[Sէ܋1֑YI CS-a;~ia<~7JaN:wSժXS@L-3Gl snlަy6oEs\ql!3[^!>48FOٗ/2 1-.OY.{?>K}xdчԇ~pg௩QFڽ+l{4.K&{_! $ 9 mwzBlj'}KD}[9[m)BӞ&kf#t4wcGnq~M"~đvϯnsg3sw-ֱ)"LY\?[8oqIKN"w[ȹn0#>pp{yԑ{o{LzpkVxs{'>'Q})cOLu9NTHcM4#|=v&gLa}__I iOE9'G#B(u%9=z=6ONq]0$^ Z[^}GɤjCZs|Bzg?/oz.-Z~ߵm1n2/xlH^e5cr7psڛrL/I{}N0a < Y5wo-b[Ϊ0!t#sl==~^y7;!ls"I;yó^ɵЏ٬۝ĥǗ-+HK2e*}n/#E>}S ' gpGYxv`  &b2n;}Gߋ4ӤП}z<3~7F9/\,yA@/se^;ׁ":Vn,o[6bRfvKُ$woWHN j?OWwfXోKiys*53qM%;z3=DmnHWO0PjI"*/iMeuaU(;/(qv焜6HNTe!yV<`]p_EFB^3Mlۮ;`i>JpϙGAϕ y( V\rOǹd8%S3~0i{Y/8w/Z}zd U2J%xdJvs2K I؉_rh֗Lۜ:KιZTSF>g64/9'[VO >_r< ܼ*$edENuYOpIC)rm&wp5;pMp} x+ޛ }PP2> 9ruj!~4 [@`< A^'~ېF,r{j}?Ko& pOEy<\sG냪t:R"[QfJ=wWfN"ިLM%+SYC"ߺ뢫ᔹJW|_ d}Gb1V&MQ:vj=|iGD?4#.z~eÿOw Rwd^4 62H7R} ~Ǹøȃ4HП; 'd4.R;k2~1 e X])k=Ԭ yݗ7yg5&ս};RvCvCCvɲ})YpQ%zVo8dp!?;T!K#yȃ!{u0Ig*!ۜ-t:Bdd#N[a۰:9J82>8aSׁgV(ydWԅeƬ fMVV~iCy|I5|[ d,yM5=ς2.Ny|yģ(xLՎ졭#2V"dd$ N9´ͦ{/>΢?.j)ޭ:{>yD딬Ilze>~{}B\}qޯY/+}Ͱk;ku39<,<CS>); up2u7!YGe>>8/.)3o2",gYƞ#KY,Io_pa۾ԼTRbYOþxQƓgW%h)?r&wuX7| u5s9_k>iK/5O%=Sw h>峐ҮR(]P(wƫ!Ln|D 7+yn-|Dϛ ILߨ܇}L|K跻*^Gu߁i$KYDȃy'Fq;bU]%>W ,dV)-388Y/H>B 轊2<2/҇%K;Ս,mGxG %7<=YE #dd)LxR^䓥&ldY+uQ?x ##ȃOyFQ_re_w4mUԩFZyFǟ1E0u;Seڧ }Z3(6yw~ROYvܖ䏓[)KYNz!) 7iX[;X Porz ){, ܐύ9~$1&11opk>5{wr8`{L_{w c-E{kJV޾iKW~vH7ױF}oNnmwOBDbW_gd{=#Dl4pxՑU/^)R}G]@=OIkZe{?Y"9s.A2^eC`%wӂ1y?жq_C;rpnpxm ҹ$x@e_ᄵek46: ۷4ꭻ#^\N"qBJtoGq ;^32ܑ38ae9z&j,?a}ݩ֓Ep:{~px;A}t;{Bǧ$:c%nZGKj^I~'m:9iǭ~'yΊs<IM|3_r9sm~RǾvPt?5)~k`,wd8uxwH ??z]Ӷ_}if{ꟗ:2#V3-qx3u?Zgߠ?I0Kc%[i;$G{gf:8y;gSAg c<釜IEIyBxW2'(>-s=Ipx|d_wsܻ}kyϬIϰV7=[$yO8XƼ2%yZٹyz%$YOD>o8ϱMz^s fy t |3G YEe s&<3KJ-R+m{Fx _d] q#^ʢelP_C`6Ơf\P^B  6@8C_`Dznc*S_>uxe~oWVn TpAq='H>(]"Oɯ"dɯ =,}~yE;4ECY~eE;0ߑ5XƞqZЧ&Eօڶw]Ծښw7ش=t6dы萌uރ2y"Nr{᢭KO2YneL{MȖۗ$}Yn3a{%erUq}~=e+!-q1ҢR߷x+eUvHu>2Zϙ_;x#X㷸hH9wwXy HS^O;/ *;%<KEy8c!ܥ(ϳU YLս)n큂Z0cB%qc癠&^8鮦f>.3$g^38w4{$ߠ?F83.M-"mdEq!tz%ަlIƂLZZo4v%/M>f{KvK] Mbs2%-A6sWJj`xmOd'\avنH'zYQ?e@>L—e/۳{uu/۹g@Ʀ?Jp gp+&8{#dz:2wޜ3G_[ȤIFש:őeH;&3-x&2ꇁ2 L8OpW{,mǗgr.*|B-Lv8o=+_nksxO{KţVlkAr3s]ivЗm d3RCS"܂[  x13X:խ2`tX2i5uNrEZ5bHڌcF6-]T*}yGwĘ̠I1)fU[ٟh[+v3_sI6i04ķWkzo[#214 a8YʌY$I>I(cL_{=3ܖ7wڎeIOylrkR_{MWۯ0S+).% 8_:/,2kp$b\k2\5{^fOP 9\*0:%|mBX9O;c?:y XFͶbU['tߢ:,|KNlzSWrWU6jJ\sqƵ՟ZneyڟvϦ⽀i)g+Lg2n!3q63_I:gWy>_i{S?(}L p~ۏ~|LecQ?_"NX;:DDi[|̅ ;+^zP~?~3nJl?c"gd{ݼՙ Yr{M]荇ry-1ˣ\'-LkyM[8rHS6| ?ASV*c)܍|ڀS%F?Nwy*R]#ݲ,Aov\)Gp;ι$J?w{}CCvukzy=Brϕ3>44$ П0=qMܖGۦ<㔯ޞ XIvqNc|eq3-ᑹ,?S0a3'52ggIY;r|+eM޷5ezc׎3?nehvݎ =5U?eױ hcd@c6F.vEnЖe8S'zTgy}nx=ϭp%ϭuî7gJM[y_fMN2Ytm}Cwa97 s{>_ zGn~dEֶ>|F"31ݰs-?R l!<߰^1i~KzCPIܿHs|7xR2!JyK9@ꈟ?npmB~o!d;5fG> iCrS2T"K y,kR{mͽ^cya#u]R##} vy脯ȲP21ܟsK:1i&Iy|?lz{lnĽf[i=M}0osK2f|S~dI _N (nrKKKK[KQ˂3h~bϪol׉Ǣⷤ43fq){Yg=0tkrRFlƀ#mA,˃3ud qAlȶc7 YR]9~k&QƂڎߙAJ{@L-tAAΓK'mf w=x32.=gP=$h~oOxYP1nX9O@%!ˆ落==̣!lla,DfY=?k<[3w P>/3-iwR+黸%^22!/e |wY Hw/9a}G=|O < ;XƷu~pl:R͠i f]dO57ah䏱%ɯ"p$S~y1B+s'iߗ̣sIL@=&`l@ Y$˞dS0'Fn%[kVk^?+ݤǃV~@>1l|Fl10|7<9e{- F3]Ȥ;cQoӢsNOXʛôLepb3z,<.;2|!w 7 ؇oq=vK{>YRTlByoݱTC0FK K޽] Wi_,K4cF4_*I8eՐOC~]*կS@nRWki;jW0U[e>cX Ī8O\q[ Wyx#\eMj\es00 qdhoڕ\el+\zrN|k1Oü$\azp-)UgĵP"\NXk9\?UlVtµdEBQ> R'Po\zqk \*L~W9j/yp]U첼^=\kZv:ɹgrWzWyJ9܏_pĀ'<%pmk\Zǵk+O\eB\e Z7z1\{\Sp+n^V7p-+ԮWj.\圈pmJ[ WJRpG‡lH p}ѸnUv}ZױvI/\qEs}s ׇpׇqgqk?\'+U7d uK\zשfCyqk'\gH9Q\u&/ ێc̪cɋ\wmWY 0q].OH~ƫ).I\U³p} p}Z-sJyU9UMu*ccpȹkp]e\嬩9K>kG\_uk%}p}U3I>'g7p xoZ WUn+0\oos=%rE\e+G'p5\rpMɭpW 5M Pޫnc)Wn\?5sހWם~Ƶ7~,?:3Y\7*s;{#yfN69 g6۬ϥM,{Z{ن x83ǚ- ;H*8C^s .oGB2> wOi[q>mӾ˧mT&W_^[|Oi___~*.y~W@.X~P;Վp׌O;f./}C hp=O_UU1XS;mncY# j7?R>[ 2w_F^ 2󐲾ۈܣKA?7A;1 |,Aqpz"U0i_ L,o;]PJAMB i^.ah]Hm~z*q㢲<뇜sC];$@ ?<\vSXۅ~VXׅ~FwО&9mg{p 12p"6IߩPsY6#KBF6&ȃyk$P>N=OH'~E/lMPH2/e@%?~ޕ*\h1vb#xn JuQ Bz$MY.gY&;1h n)|%ͺ73fƕwfdY'^甆vᵢv៱v-Wcd'lbydv0 9S/} \,qJ,qox,dLY9Y2gJW$ g6Mt7$)lzW{sެlK?G$vݿr/9Cx גE?m%K,cR5S1B,cɢ jeM1K.Nr^Az,9|pN Om/q~&E [s+NXG%y}0mێ;ϗFKG 0 /|p'+nMcr2 Ke'Q0w%jGY[{[+3#5N2Y'du.YN<IlAI|NޖAɆ'uΤN6f*uREk:$JlAZN63 .pÕ.pi:\Ww&YtMW2ߚ{[gm,-򑾆Yx zx vN}r?F+Cޯ9|/?γbJge$˳l#K”6F>e-ˆv,RڳlH!e#ˆN,R:gq%eDO :ڻ()o]3ntTg :*峇ȣǽD>Φ4)y.p!:\Rqp9+8jr ;4'o,Y]&ߗrۚ|_]/͸~(vZ.Xws:/QbЎ/h>;Ǵ6nѱ:qoFKXȸg܎?wl&|v-ϑ1SF9}#FFk%7xW)x7^;>]wĤW3OfTN硼9!?n_p엗E%ճKR_d)ebKLR*Sl -}Rڇ|B+JN ~3by3SҁpzxhF]b 7Qm.w9~A90 9rm92$#ưU)u[lWr]J3})w kL_d95Cvi;D/ga3xL{b$xǑA8)86q:6n]n!7y޽O=HDd*Lu2#>03"ӝ0 'WM2>//;Lygc@xҁt6"ƇZYpةzOSNc^c:E Ss[7,i{Wey_V%Bޞ}qp7=|f CR'tʞq44]ڰ$O/{>-n(>9d\Ld>_W ѥ`G9SX@ss8'Ola4u*? ǎlv^g:[s>tx#meg)wr;~e,o9\>}|M_jMɔHrҾJxrC םw:2' Vb+c!{].57Y.|L"2"=6R>>3r&=~\F;hVYos YsG9l#0}I/yÔˍM%Wl\FozE-U2O v]#=JQ(\kJ,VڬI֪dZUc=*sNO(#gdsˎuZR_YB1.Y*J{ћXIU~R]l5r62fm%\gC˻W𷏘.Z`\gݴ >`޻%i!>.6ID5BUNLbDv?-{D{> Mg瞚g-/ϒ]~q%=PYu w倻ހ Vִ}-ro Ǧvb.WKsK[t`8\?Bw'9"^rKlUc'i^5Vt[XYʚn;n?2tyڛU#E>>U6j}ٙܬj$. ZFsq֧\kȵuPŶc6~IZ{A˪h،s!Zu*6 9v,TROQ͹7Pral vi[$cE?T){Dy-SUW9ۯE죐ߍnOrr.{~F3 xGyuR30[3Hy%>d=='mae 8}gUDU9ݯ과)UίuZ^uݪZW?:noU]+szUuG VM} W=t@j:vԡWM礆W=T=S9tӼ%c;o4~f,T]Y[z1_'`5l%*q:7Ի:Ǐësxhf'V]\/Ψ{,{WŽrǔA鿜Cu(/B5^^XVˬ놲VfWC" USޥ\#!Yޫ,}58:PC8 q R`ҟ$@ޫ~浬? %} aH_&j/^8   g lM.Xl4p˚\SǶQ)5~/0 ;%0CeB /Ե8bOm!W^q"ye0 7/g1Spar *X{IשYnF~ܫaZ\O[KPqeߢz XtTlݵ%z2)rzyj+ŕ7FBZm7"i $/iNו s/9YGρkgtՏ23犐VWHxFS~#/ϛ^qLP11mwg>w%Y⻚9.o T׮O+|ɲ=j ,Af>#xx!< "Nb_xp_jI_tǙtuxs~aiٹYm~< S稟inW=pVWgHZρNC;A? _'9tJQ, H&mJO `DWdpYkYRu2-WC>/'}tJީU1jSBF{7,̒zv'+s-t&[bC}=絛gkH?}v'~~ B}m%0w2gD/dn`uvΪHK=e+MK!KSY:W%úO\u] AcI䪦"8x ta_C:SXඬL'XE%,yi%efXKwË)_k*PLxn;<̹N6?6ڿ5 / k[|x5k7̼EZ<.Eދe_e dcL6ىgmIԄ߂?4'w}/YMW!I16G☉,wt8Xe)#w'j߶)3G#/D|@syGUM$Kw5ȃg]!L:'hF7W|-oC? _Iwm:)>#ym9;~uxЊARnӶOƫ?M7} 񱚧?_c yz|y*mqi!us֖ Yb[MGλ[Q3gſhrZ&9}S"w:-n>DF̪4/#zv/8-[;.Vϕ?iC{oN|W;tն-9sϡ[ygs6'-DFC_M)%6vplFM's> ǎv> i{$:kEߐhj_7`];!QxߐFȰѹ);n_ -%׼5YCΡm*,> wncHﱽ7m#i޴٢Nc{olifgއcA~a{ӈ18W6Vc9;˨ѭfѬ6quL-1:]SN]9<8Fp3W꧟oQ]}rڈh6gR/FUG~(ޗZGiO:g7id˨VGW?i˥ 咟Qt[w7MFi]EѩwMnnM0N&%&R}nbz,/I72o;2}p|CffneL!aiN^q E)& Yʈs 4c?ͽw#CTFK] }s#'"W&}Z﬍u |f⍵]XnkE:Ols5L`k__73hgz}XGc= +&YϺʚgYJ3%Oue+Yb5xNv,HIɁpΖg0&ۺ+v=;v}[gIM$&KX$e!r |37KΖQ3 e/fd3 2RѭzMfI$}iXWD9X='8y#Xpv]Ҟ#K.2y2j>?D+yZDt mgx VQ') Ks]K~:8񿉖Cw?<~n[elnhf-[xU7 :Z^4$/N5U% \YT\'Ƕ`~ pȡ~#.-oT5ʑgHnyRp{ pj'چRIHCrvm ~*mB\Y(xI觜տ~/N<ӂk;ZP?0>J$\Ǵ xH>pGYK=Tm"~N\S)jAٳ% LN?,e Fĩ3;fm5(NDC SۺcJ{sVȿ}͐/z6&" 3j" 3+g*چf{[V5dz;܌U2!Iz;q"pq\acOý0k=2j]y^{ߌZgϨ$?dnш7/F-sgx9S;iW<6KynզrӇ!'[w eOiG2V8ܣ3+QtQT p=Ͻ1i+eVw_g!aq>wۡ~{5Y wI,:i:z֝YE2ƞC I\dbwsc;5S 9:s+*Y.iS,\ dwng;& |r=6Zmٞ۱~uSvvd&x9Ghgl 訇z=LAaVuv=IOkuee 7YDo$-]>+ݳ7{t$CFw?4^4.bLV//}GwѲ$3sv:Ds _lfO`bJ6τfuepx:.ߒ]Ȓ;E;Nr)/'ϨEO=ï=9 NiY u[TJ<`dtx=mFE_|gA7Ŧ2es46鞶/yYc fI"lmHC3n5y!䣤Gу4wж8#Om̥Zfzi˞qs^6MܸeӼs6++JΘne3MyY&zPY]?Mrw/%=1~{{ٳgR鶆.]˞-'19ˮe< GM?1 C\o Cmcx{g<{!=}o5i/a4|~Hr{@W` w齱b{ /qI/Sɳ{۶X{.uuKto6iY~3;0s8=OyD~-'W-#%hGf>n>} 'I/Q;vs]ϵ0g'b]WIaW:5uI_ }m.{=Gy}A_#}3 b6zb={E>gof(_;olˁIf<{ǟ䲐?IO*GSW~6:P3d,~\[Ew&|ysLmŌj蘭l_~1Qʒ0֗Qַ+(=72j~8&YK;5v_ ΔIm4pLڧ .('2xK3@ˤs0Hy.$B: du!)B? X.*'2z%ECpYy1p%"׍<j".!Zg^U%@QD@ PA+XP tQQ@ RDYfޝ{r?vgwgl9;"=) =ྀEzC?'~7H!=?҃8Y|sH"M/"=JM/ epK_CzC9do =x(p$҃`69W h8+`c0}ONDzGz'!='#=ـS^?уaϋpCEn)dl_aBO~:E|k{Gj%ʸ橏5扺"ұdVsj6z3=?w^0H!mE M$x<ҵp a++~-m'p|Oc^|wAPBQ "-.蛈Ef"nsD:E:D;vhbgHCjדL;/(ۗs"^{/(w(O/GN"]{c;t;<{LЩ V)O{yoo<ԛz#J^2ݹ< <Ա͞J[_~^ƕqW Hi^B"ー )߅ܕ2iz{)#EA=.p<NJbȵ"DgA-+te⽤=ϽH^+&x7IKQ' 2~ ɱ0FĝᕇB 4MeKeL?b|ivj}F8ԥ +k1L_5`9qX1n~-oϫ4|yo!>ia8fĻ mrNw4v.8tEI-2N>xieU{_LOϯ32=`1]Mya>o7_ׄ}rMNm`x1ڄ)zFr8=M bk{^=/ C-;#~5ImNg#m̸p_n|֡ix>p0~MC 3?8}FQHt[C4&q+Ǭb _ݝEc/e+9yYM׷~Nⷌw-x'>+.: GL=~ߌ?~ߪl#ЏlmmGm C]' C|ď~d/ϑ1_D[/ت+cluӰɫ"͓E^/l.v>%QQn2|.2qWGFD<ƨqm("y=ɠ(' Q$OfGWse1.E|ៃ1Z?c~wsbBo;= g 22 =k5b΃L@7`~Ss o!i8fsgg|]GHwp:_҆TـG9ibMʁSƮc?dh|Qԋjk7AwɃ HnxC|oXA.Z|+l?g^p燸MNR穿7<%yKA,ɚV$;ƒ<K_,!Dgm9bWwIF$c|@Kc?_mNOu*+V+w=)n'e `w&t%$cv~qoyyr|CQi{cX#zw=3'>Iegt^9xV19N?9@qCM$ c-:,+L ŵiާG`AYٔ1GOkNq<3ƳŮ3bO Gg/ NwO,+7}`nW+Uw$_&U)d]mOm!j0!- 7{H_ 7WFp#t:?kNK3]N7GBg5oC1ʍUqX.ngiu@m4o~~ AAcރIڨ\2,Nz#Ji6GgWO`f*KZXvG]4xJMa4R1’MDyk{As/:8I~5]//.wq!>o?p~qׂe#ی({[PITO2F;#[򻡯 _׊1 (ӴoA%.tڰ\Źj;ݹ6\o b+g(M5ry3^|g;͚5/S͚>4_|Po`d7k*4?IfD7_'ʴCo<"=4=`YՎ]@O~AYp>gx2,<ğleT5^wYC?'Yճ<[>׍OѴv>1L,7|4C}~?g!xP,/GHZ[şζwl+jM d41tG{CBeLtEvztM)<rVdWbyMwZF`'{ B?W|#<+"GENrՉg -ӞܕwCl-l@fh Rg_~K95gxM=_mQ)|E39zzmdR>ooN z!~ K^ʭ%;#{YKMx鿾n_S_#_ :q8 hwG`1Schbf\ 6DFc)k 8G(/71<7D&Am+>4%rNb&3Bf[ Q]TF boce3Ƴc<C{!8~6֘0cUoE7ހ9&9~<913U軲~%3F%JEʬd,{a)2Dp>l7Wy;3_xF G\ i2+qM~ԡimOv_ck<ٍ>';#v$l<뿀/hRg |F}>CYNѸ0ǿ nuANu.MtfvK1y&0s1=x\5&zeV~ 1.[XN>R:>N:Q̀۳Mvg|k/HXW.A۷&h˾_'軔&mL4t^v@~h&S"'J:9lCi3*ʕHƌD5f%qn" ihe"$юD;ڗHD;:H{G_=cw8p4}{?O 5z!Yu8z,}Wf"PF&M.zpw Ͱ|u8?Gc3Vqgt_>=W8^ppF:܇k kV >'>gE p6k*p7pt|pۏnv%paYi1.nwܧ-ipׂ p{{\|8nՇ+6ߜ; L5p*&oJym2_`Z@hS9H&_@; w$ʿ_=:wn IԿ%QDf2Կ$SNJ(dnޱdd\2k2W2Xe"Xc)Dv9 j:Q>f!>ۦ4 gf oߞ -LƘ ,@y7hN,0wYl YnǦ豛ynRtI{%[9W4{ML6]C9uܟϪ_AcN\/ވrj{}Շۨf*QTjGSzTKhG0m2Ng|]J1tݩI%t*ɞRIo'in0 }(i4++ZFyKFy}FyOnQ^uIGkN:ZtўO6 M'-!t-M5tZ|ktZɠ5H +L2,}2,odPYb3,2,T *˪ * *GTSTTTT*KT=^3!'O |J5S9TR4>d<mv_(ĠTTqLM~sn H2sW>.DsW2oTPB9Ԍ ~:K됲l.OoC!ƐLD{7~OF1VFyaQL=Ě䜍Wec{kΦ_`|%n^Ɍ0@RgM!:k+9Omv"esqxWCJ@;ztSlv"%5jc7V/dݣ=ӽ~i2]WQQuvPZt!9Ǿ ?PwLtO~Sı #?d}KYZwp/ӷ@@,ި?rrس}V۠0 ,s;PWo;,ϋ6G{ǚ$nl__|p`5ʦs7MLCcq/̉:o>m.k'#6{F6/ٔlIF&O6ug7[ͦz̦gSZyn~;vӞb?hC4sW !F Neʧϧs?r{r)޿rʥuIh.KT]<)W@hܯmBBCٸ&YNc>zJgiB\v8{>/%6txA|/W3\͐#{ qPn32"Wϟ?Һz.j:s̡͚6&7o4^C99zNR}o,a-L}ְS 3(ϖփV X';W6o@扖Y!AS45WEs+ BV}&ok{߮W;GWё -q_#8|My[÷ /kӸOO('c{<=NKyjfOWUĘ3)mmT|ۀ}Enuljx1!4ώͧg{ n{p_ssgk>+']4']٧:TźY@i)P/P7 F2l$S hv.).؂.$p]H4 tMK4A.$]H rFk r6-0F6Fυ.=ҧBu'7ܞq ! tBBf~+k?UV)z씋LC*CQooHDOk~eC%'qE͑9h>H߱8~Yb3c|~[1P ʧ F}c3>x\^JB}rúﰩ=ƿ/$GhA煺}GdSwo*K9 _`;MNj\V@H]oAOb30ܗ[FCHK#~& s;oKct G=N̓jND%zDp~ FKn#(o%9!?P%4j>Hw;ڿF;oKH@;_tRT1ur}^ͩ׍xdzϧ[}ؾ}.wץVȏK KyolM+;vbڠ֟[:qz.{GjAҜZuK'~s3x4K,As#uF etg7,7 G7%+cԣP%i%G3^{C[ח} ]%8IО b\[xOW}5Xv:vJ>ơ?bz's}tOg1. Zr@w88d_WvG1{]3nH>+蓀\“逛?b!`9!xr[ۖSQ86O,~csuS_jxT\_Xκwuü8-if 0 )i1ܝtj^ eEu-O[[o3, (7WpW{40a `Jc)? 0|;1 {2> %_֨f3G3ל֐,0ߚ+?`YóI7209?C+y>e W@cPeE |`BנUPy큦 e:7aJpO8u[p_B~!L_|))l \#+Z/7IMsʕwlt~<\DSKEv: o ˃D*?19tj3]Pmcy6W6^?(kƲ-mJKUŞS g;8M{M%ólVnz8;|uFҶ때9ye?a~Lm{| }Ef+jH+flc滦W]ƖWt'GIwHU SyƇ?$Vk\oSպ %hT>N^ߘW!L OUzrW h,VI9< »,C`,xSE]ytQ6:k _b= @8=JlDsfyk Akɦj- ֒|Zǯ%[d+_l%[wkVZZGבud+XGud+/ZGV:_U^^^g&MƘkz>c5cHTic qк!c-\OܣΏkfz:;7"-PΠ:J|w/Zzgz:ǂ띕J)6*U:Z<n5i9&ҁ8p pMƻgdk޶Gש>q\g-Yol:?n [7m6{O |xѽhFtKsy*|'(&9gWV Za[?\=#KŞ_,hRFl1kb1kV1K+S/=]=˷־]]|k t-ֶr\j)b.F|OӀq};R*nз([eRBe}yPY'PYJJho] CP]~SBuK ]B.4*9RZG>YJR4RZJJimm*GptoR)ۥA2zàI]б,YFoZFo / &ћ9e2z`S=8YFo \*7ۯovma67Q u ~7R}b近 H} Yt(")|XHl)Ot|+8Wx7b|Z,C18YePqE^DyYK{':zIG-yOgL_l&f?o&ff9EZk< $P7ɻ,/ ̷) 7+CsVrM'|\=rN29z2?\>b;E1HN6(a_I\_W=P~( 1A\i<泃{tG3kNͨʵt\AUNxdi>;lf[٢l Kxh :ж 6l66uN(TSN8A0aT➺6jce[~ C밅֊E8Ӈ?PD2wl q&0Ƕ4K:Pgsa/*mlc ;E/ xR?>Ty6$N13ݗ'cXّ Ov'5IpKiZ .]Lw _[[Zh3Ogj6 ?g7*)_Ufaޟ7}lwAۼ9^`|}UU^'>Ltjq:xs9/bbQG*qM ާ;t[=VwV6I})7|V8voNǘVƩ}.e朿ra1]K [ѹ2 *D(,ta'am7s.~7w.t!UgņCQ|jO yˢtQ_@!;,1 ޼EcA[eY2ܕxz^) 1BS[d]]/n)3n ~fuK\O|KIėGwA |'q?ha່ǁ& 7ceX7G)x<i 98I9? O&O! J<8Ht ď&~ sk#~<[g%.!m9.X3Vq{$nwx~`>T Xܟʏ[~ 5qdž[`B<=ALٰ/ e }P':*\OocxS;p_ Vً̙d?61?@]꩸ <7:Lgፄ*o^}1Qx.-0ݟ]h?'O@x),,ꭩES/tU9󉍔~>I!c-;YM)i!cа?ncyas2e}d`cI$s0&7$Ok{ѷ{{ߘ$oc" Ow}*{R__5$x}o k􅎻ܑ;v=h ޺` /O|B;S$7N$Y]leuVf1`=yrQΩN2luzy>Ys^ru_(ޝlu˒nL]bumR}g3X'SgX7j{C20xOPXJCgSD3m8ga,p껼zsMc"^ ~xC gCIgġ=`k)p۪:p N0{h< [<>GmIfm!x6[K)BqbS a}*b\_:Nݻ{D읪m6Jw7frŧY4Mw#LEvXI##8$NpOq=A1~f4[^i2}!)6g?6nV VB3q_r!:,+/WA' ̙8((cHۅ8o8p8!v7t!k"3dQ CЏoL9\y[aςo9 R~_3lk"WO`- }y9e=?S.0$@\_c^?gڸʴn3mL/cL5qjU=|g]3Wc8NibTմ_s|h&gzkvj#7xNs>6y*3g~%3]0Ogn_E L]S7܅xj`g2Qzhg|?+X^mwspۿQ6=?؇5avطh> Pݾo݄!{v~FcFo=-lD|Ŋ).U\H1'Qi㶍cGW,InƱkK~=X1~Z מSIpLcٌIL؅;q5`֪NWDe//.$+ˎ;FfP4u*ޒ0~#+KPχ2`7yOƯ.;oW24c@#}ɏeJ/7y?귊u)^fnAMƬ !tqŴnF;ud_KNrbMCGӎ0r?f͛yUHrnɺbm'mvLmމ.O/sZ8dKllmƞlmm͖~leZY#GFȞ9,ɑ:kr5GҜ#{Nwd%+{ʞṲmz(W]+~+ΕwX{.W8OyruyWnp;!O˓{_ȓs/d]J['{=r%{dO5{pc`2(1_N{V&9'2nd$?Afsd6#oؤLg2v{1/n* 6lulR}&6)5lmɤxdAf3*G̗xfKOK:/G"^dQ!&?Mo!$x*L6d)>tw%G&3Oa{Js7K>Ƴ=U2W!ˉ_OOtcI!6bIJCyf,]w`& ğ"@來-SZ(*pWO OHxw@x>Mrp3N"6#{ğE"z)w]? 8{fg:=q:(}obbbwuMX/;gt<${!IDV"JD~Nȯ,M++oa߂?,h_*\S*S*,*p.pK%R  g g ##α#G%z,3^UG%.:&s$Iw5x\sqgqyrQJ|9`/ܯJ0ոgaloWYn3!ؓ/s23$;7XuA-Y`U[jlUc]k&`to!|A?#x 3}|n)3O.=w֒FŠi|]9@W7da3Wwu ym]5vHUQ#m[A3{8^}F^YksmuQ. ^1Lunk 4є#4.SxX!C;CtTv]R&Ce]Xcbo7!ھPyO]@F&]boڲP;cnݸsj[oŬuQkB|y|7Th[5a4zFڇ/U߽[6t8zgBŎc'.~An<,ދp0u`*n}rg}ߪ^y.5/UsNnUdzu#ޮxYv٤8Uq:0ئƳW-K[ _ESۅO>K2϶Rt+!_Kn?}k]?TA4[o߇6_iRqOwb.:'~r~|T! וݽ}NL}kwãrMg@f<6s0L7H㕛`/|S\:a=XAbH1g߄"߆仸6=.,4}Ng>22f[d܇dr[2/D&s~T12yK~YG29sy 듰$> O^⓰_I}g> {O.Iy>L¾L.QdrO&W߹aԞ4\W7  ]) пXp\|IYgF o ƞZބZÐbz݉^Gɽ8|8۞]4Ea()d-9o)Sk;3!:ݔ\u {/@_CA˿k"~xŽ_޿C~yN} N_K29 ҅'o&ןM/I&Ieĺit '(w|{.0Gά`-;Tr@83#BoT7-C7"-M;$ƾJ .W_Dg;_ VW$7;8J̸ۗg{1v`g8jk:{.g9WG칌n,z Y퓺H8JwWs^,b) 1✦5ޟMw빃4uRAE9U߮x)oU lL:43V"/VQ<sMb^31_dFH6I]c g6}xVCỹKw5Og|{Nymi2Vr˕\3iq-˭TrXV;S^V:[;uo3,^T}I(i@c);(\@(VՊ(^YkWYcMmX :ʝf s8<1Z5|[o|<0'[wjlm_ Aa>*pv8m1og-")v1h\(flxi~d~+bVx(;;`XmBjX_;Ֆ̘O1c(dP 2}|xf$#o=9.ضrU`VJ</Fft\ߌ{x6Od%C2u1^83V9 _)>[ܲ߅~D]? \/ ǩI 2xٛܜQvwhw3q3x9x9nkY?{]9ij^ޏ a|p_ vEmrٯ=iuzAMj;1\{Lֆc^hYmW2ym 2ssjvT~=e\oȌ)wl`LٽO&PvS}idꉌmMwdh^p ڏQ5ߩy c{]}Vݗ ?S)lH>~Lg-|=l7cfA )7'H_f^N*50wBSyؕYi|MW:4&Q3X\A~bw}ܱSŰ\K%`p϶,r.9n_ke'^7MF6|\!ɵuȥ,u=R7gң!C1rXf+^PЯ5ѧ̸w̸/E^?QXtp2nVdn{`\ifu>濻gЋYJX%g߯c 5ܺ\y-Er$C$q7i=X?$ۤWxF+IRf|}+?+xsh޹m $|K1ߓ7Lv#?/iD5;yŦwqw7%vS&6tH+oT4bO ˍNπƒnj,z(~H/)~OW*7SYoRT:Apx.߿0{Ň;HnΌcd(=W읓+W~ownp̘yq҄ #Ks<֌g1wcH?&O1W<]*,yGcf^/ydǼ),lI̗h/abb|I/+$_}P鿠Dvup;+QlQ1>خ(\/Bn-%(qIԤ)ٌzx|5"=s#cH'-$N^^d>Qa$e@d2 Į^b0%3@½@,) Ćm~U(:S %.= EpS E循PzP](a.;[u/ҙ~Kw jKg@ ŖŖu%eg{.,p;˿U]$I]$ dN*vUiUUϮ:Ӕb뙱E~vnA)xⵊ?R_ g*.Q|?+>W W\x⩊SFf+{7VR(*^ ;Q-NS\x?xQ|Nq) S3YEf\3iأb̌:1c[_ܷO+.V1~#2^q ̸OElo˙q!E4  ? s5| g›{Q鿊7p _~}&E>Ea9ɟy(m= l 8sszwfG<(ԇ/{]}iٱ4ȬB{/1?s08N^x+yoe'T&վ. .gRzJk`zzmwS*YwyfN;T暾BۓeLN0ߦ s!*+<@Nk,$Eօ /\+Ay]+J GmT2_+g :OJGetxNy$UN/sKk^ ߫O}]V6xNH230sAsi=B֏qKP42wGгey2e%GU:#o,sݦk<8Csم~=q }xi^V|n2/p ߖɃ ;|y %p$3c5,;x~4prܦy3 ڏM|pH6/1l neZZ#3/pr^<Fǵ*1rY5౉g>ρoI{83/NCHkWg 2N9X瀫PgvNJ5IoRAs Y x3GL4χ-ˋ0Nxf ) }5/*W )#V^7*oFAq|Ua0pe fM o6#=9<{+r!8Q,n7U2Y[ڄ>V͙s-oaޟ|ͣU2^hU_təQv%>tQ~'w)(>?69)Uީl.}@v|m={7JYaGß.曋}]I-ft}%5eTpb%1v*Rr} ^}pq\ |>.OeEy~}mku#~םEy0qrygf_^/n,b[[5$h"(csZqRs8*Ϻ~L P~ 8ʾ9*mN!:2!TX̘}Lq5m}<ǽvSއϽ3`tFH$Ȑ&Me.kH! B&D5BD zD|k=ynwԯofbL\w2#0eEb">@w`qg|C?UߌeiM> lP!f3S< LIG`9e&Q/`?_AH֊S}5F[HgH-ml(Aǃp4!Ꮤ Y8$cm{!>UƆF->عOpPaΓ Mi<0w׀IOTݩYI6 ڎ@ lÐ~RCN[a~%I_ }S&dg+o[(jpE>{C%uih޳!8?=8L`y0)b^#|2l &T/gk8G{xY ]ɪ=$|p[ O3&\ ř>l0Wo7z~MLvp8a4ig N.­Oh#Q`W(j5kOO62IFOjd?f5j*_GNldr8 8k9+Wg9;!ml@Ƣy<Ȯ| e"mSߐ \AWq̫0ᛥ(109vFĽWi!BE=ϧL=E1Ϛx߽ϚgMI[%Mntƭ!#w8e ׳} tLSoɨI'y,YuAs;h]~A V~Gf\>ᬅ}M!,sIG a+iZSFyMA4tŸp,7x.AonZyn_X\ҭS*LklƝy㪽>EϹna>sMXOONHFp"+ae=>1MN_&|ЯF7 :6y/].?:ePnwy廛ضr>AWFKbSn8]_;_GqԨ i|E'GMD[8=&z8_cyDz~L~]%% oO6oGH;sm+5 - zL$P'-{;`ò_p5Mx c?߈AW gSSy)ͣ,ʞ?j]ojl9tHT鐌tۦl63yWcp] 4Yx=w:w)EEl_)I=O4m7Ym~_U=7wUǣ1Fyq}Y }A#0דv&mYF@/QP阣Ș$4FXVOr,JIfʀ΋^*/)mfAH5.l,e'o#|ʝwMH +lw3gS:O[zF7uZsVm my)ߨ<ڥY]GZi=+Is7t}S6rh8^Rc\j>\:☤lם%'!ܖJ9p庐e&Wsn2&Or^++-}\]<^pBȏv-dCČf--vVd_=_r [GS]eܘ3qyE0b%}/3 /"Ԇj$^Ev|^ja2v,}K xҤDYÉSgױy[piǺ% -mjDžRI#Z1RJs`#ۦ}ԣ9M#y31-M:3+$ ms;Os]3# ܛێC9zBX1v̿^w@XG -]\KW4cr~vs]UV6s`+&Sӧj++)6oemx}OcQ9[GI0ҧ ΓΊ WN&\mIL|y1^I߇n+&t*<}B" UT#S .,ֳY^Z6/͎=@a+eϧw.V鐾kq [9==2 5n=Wl8mmˏ^˶N[>Vl[thm} 3v8/μָsѤ GksfF6n2mBQ<}DF8LRg,ݴ nwnmC&1w~?6v#?,{f=H7&mO= }cxIpչ '>/?ϣ˫x^ۗwlϤm}j >{|&KCo:-RMRيx[6@(k':dۏr1:韟B1iZMy?y{_hcQ]QIAk K{i4ۣ5/dy)K#4[Ccbs5=&{"8\6f]y?0d\%4$\<Nq0p|x"vm VNE,Io%?D&G$^EJWv[vmCZ+ O!==7v<֎O[ ߟCf{ ͷ@5w2gB}p=_wn|&Զn7&χqs1^`h gl+L 3& mmq %4vMe}}K|#?ZsʈNܞ^mvW{ҵ3'&;0(LAFn:w.<0w=.Jڅ;7v6ng,s8uL3|gB;S4Ϣ$cyAbo s<]"QH@3NYL| 6 {QZU#RR[3*q{&/7{W Pw_{#< ޴m dLϓ_"M:Hƒ ^!*% d_7w^M8;) KN2}kdt7R'㩔` ?yONwγW.I'<=T`lN$\[nG;-chsSn MB2a6m30<^ vM:' o|.ڹJxǑ?:nM&|GGSvruNю W>CͷY ? |𳄇s|2:mVPMiז8Pv4l@G1M1h:f|+G;ь&&*Fu{ Fsu 05gP3c,Ri sڲEK,Lm$jeNsB>{!nv`7r7ɓ7ͻop{ufOxc:¡ _wuz$\2LQ83;F83$O_2w4E)〝Tf6ujq}۱!?^<1\Ufͮ7ϡ /B6 <1{geա =ĝ pU|`IK:nәgBy^bw*Mݼ8 㢦<Ԗ^_ҳ1#Ecl*Q]oy]wm+0ש~+I90P'_æÓ"xbK])4' I(ըL@$<2~Q-<ٹ=ܱY4v|l[#CzX<Y`΋=݀=,3M^g64F8'ҍu3=܄=>f=qe^G¼Q pf-#5A3ñ{Us ]QJ^]cz ti҄A(URƢf>iMUtVj%_4{}ӆBx5#{&M6@g2e.`>;W%|:響Io n쬽p7s'fꖱ&O ^*z74&@>㓄r?Uw`P)C:'En| ? shWm&әnHgʻ|DAF x/ W;}n:d0 ܴ;f^8_K^&yAH/6^(Cby+ם];ǧ6d`\*)iB?T N#4vzSSߜ7Ҷ8W2'ܱ3 0/鍱 N2临2{WrlM}Mo9 /#"76[l#|D"퍺8 }оNx3Kf,L!65p>&-Q}І8BOJGǙFxחNTV P#<ɄZ]{D‡O {qFsg I6G_L1DM7f2C'Nw8ϛ>%&C+Bx f{7ŸqpЙγx^,s38 ~Aw`{2g|30qnΌz k:ͼ8pFBs@7P܁8;~CsaE 4ahz`IfM91Ьտ?Ьկhho 4BAf" ))4>Ȯ>=Mn?Ȟw=3r v5‡0|wyɄ09Fxn\&kCp lCׂpa9ٸN١?O'nMK!893CζH;> g.K+hg f6soP儳65fj2Jð.IqiNx\¯/$x_B0F8zx9qH G.1 >p}Y8AM]H$<8xYҊ0_'LxdS* k,"W~IոWɆ9xx8NA=ۓ٧#dm}w5IY[Iu퇋p/9B#l_D; SX#d|[^D(E:öE|% /'& gNI0\~~jH >{Ҳ'\OO ҘK '<_4NTW w' Sz ?'=|YPRk'n,|Stii׉~(nx]Iw#E3wb5&|N1>n<"ྯ?o=_46b,y7p4ΕO8}}̃/ěz±f ™p&uװe/ j22:e.YpmQ=,3Qn<*oֶG!}Q&/xԇpx5pxV5[?ZZyZpxd"pyp/1x`^:x?…8Tx:&ɄF!Jm'pp171*O1pY`^[1ƄƄd{hkKƛu =R~ItYkHlY_px|py֟q.;x@AN{Wd~.j ?C2 %\E|BNmGwKxX*|DOx7yA`^8K{~k~"'|8'=IASD]Oq&S'\yiC!t^@^LN駶s?ud s?7A?o7Գa?^㑿 ś~j5aO{*{Ԟ9"s~~)~wë_1 Oxu*ܿiA 0v˥d]GҜ`:ˑ /yE2nx#xfCb?."B3{*a?tRaO Td /3R'^̟Jx L.g#AeTH/ZEW_"`}vL@Hs3%\7cW!|Jnmi,o΃ȿi;>YǛDpx3v^AMi-+p<د&L%*j(^TʮTxtQUE QUyS{t*xj^TDūJhTY,?6dU$86Vzk=dZ<f[&LOkG&uou~kۢ^ʞ.Qho1%NsػsKێǸ'VٶGu񷮗NM'JDlY/krL4';|P6ow|~]9ϟ?w" &DwX{5ކEWMn^L/Hyc;o8 0c't{w"i)8!M'|Ϸ+*~>]OD]^l]0/X[|[Hr[#Gn(Osx'#x=V>Rc֬@$NրIg7 kE" h i˿i8ޝd:۽xЭrljK6/UeLߟ$w߷8r0YNV~X/:zL>I]dPɦ%L{KO6ʓz^ƽz[ӋMv<4A|&eBߋ_6OwuÝ.LE,3fC;QzXf0eNh2y+:Le$.TwLU2UxdTtNSDU^R}cvz7=0j[bN1_Oe\Sdkk@<{tL~w)'woƜG֚;2824 >rGnk:;+>2'^qV}8%·9'W/S\mc2%LҲ]ڳ.L^>JĆ)P[*z8oOlcSY|>>>)çG%L0M!鿾;!WN7-Iٸߗ1 ;0p{y)|y'ڴⱊgjdv+,y@F T{f2{\|`h*\YS޼1ܯ2fuV/_O\GnG#F_Л^r)o:})r=/uX,".1 /.SWY\^ OVs(|Q/[R /W٧[\S Tx}AF =؋DU}_ǿ>w?#wi̯j[D9B7Ab<"cF!puV/`~]  X*ѻu99uitE8gT8EXOJT;NY;QO%(׀E,~]` t@g~oB ~?~gz9m3SǪޑJ|M+'gQGo_rV ^^<0n /WI5b!ŒWa8< JoTz|= ·z_~$#0/OlvK;Txj{al<9.N~ŹۇSFq*pJ(νyq>=U3PD^.=fCwR2>OtxcҽRlob_t_3|8g*#/rWŎŧΡ=~:#I&mS0QUa7 _{M{՜y'npx&cp׌'qSIܵmz>7ߪ}NG^q}OAiLnK ̔nk3"+xK T(?>uÖ̯TxlKyxR6y=v6"xX)O> &}=f="۟w{$',މioWH|//DCy3~WNo |-r >;XIZ~\yFcAOz ^7 ̔$Wn9~E94>% iK]7o*_]H\1{?j[y-*OSwjxr=rOI~ QKr}+I7o9N BO%_en#:]$Eu \ۈ{t'Aۈ{$idx^P7/' ֿ֟\D[5{xa *b]kzWȵ?ӮHEo7j כ{ؓײNxt.vϤX=s^c1xOo ۤ=Hᤛcfs\}e Jr^=G% ոC#-=A*Auo{-7-7Wyɞky^k%.m5B`Sx/)B>zx^}x[ 8ILזx{·Wz >JltoEoCϸ+|C+꘽CHߕ̥ՀG>zy> WgI<Aϫ*tZ$Fk zr^@n]ryWӉc[q+xf͚o&=7x_7m?H.u=+mcqY< Ǟ4{Y5gM8sUQX><½.e2Vxʵef7_UIV^H6o'ͽ<*A@nh9ٻ*VeϪWu>!uO̾ >͢> oO7e GgY,>v>kc ߦN]|!{'߅'eo,=?^ }>rG_ _>&O?ޔz֛ ?}[gx֋/߷e}^|ߖɸ\hR_h~Bs<[ mZ\h/&|`[>z]Q'>om||h҇6_}hׇ6?ol|}d;#?om|ƷpIDopO,~P USV8R|7)|DtY?ϽKi✴(ۿ< ~[+!?RxҿOU/ڇ_~\1ȥȥտ ;}ۿ]d%}:BH޾&‘QI!~SI?d+C;S WGί|Qᗧ~gZ.mgPٻw!KޱϞo.}nE?~Ww?|WㄢiΏ`nM^D8'lx%/Ek>W2fgwg%'^$;)T?|o| ہ Bx0 %<49gLIx, :[JgΙR%ٯgLIL| ,oK]}gfNB:=μ|&;->?z؏ps;pk%=)RdO%=.z.]bYֹ6t~\zn{/qruQz }KLOto[Yixx%6JFw\⒤6[p%nŌi.}~06t@{zocΥFW_3΅x ȯRC܆6KGto#މ!=1 ?OEyIi(wOGy+()_EyFug~%&Mo~:ƴ*Z*%![f/4-32~e/~6aϬo8-r~;𯍲+w2{F|?nF7a;kg ^cmp:=KʡxyʫSQ ;GQ:ϓy _Zfs ~<ImxsxRχS"<谓EI)nύ`<7T#"}ǣ#y$XG'u&pnbOxuD5.WOQVɪMOKG$]/E("_V}wǯEھ7so٩)N-e_nڳDnjeCVTly0t`h)-v/# Fq6r}AgkLRy5)clzz=)/~GϜ&=_*I&7Vuys}něbݯ;ػ_Ij-m"Ϝݲ;e|~jbζD OZagW|ŒGÝ gେ+]Ǡ_}0+|am} sk>@{_sW{;٥67o^' ٿ4ž4vU2ÎN ]xmIUК-eø,`\c |H5~0K3M㈳ cWoMl6vl^VG(l&B{z>B{$1Bxx#j@w,9KS" ҔѢ+MYҔ+=[{}ҷw,le) _9gi:5귲 [i#Z2':c{ /I52aE`yrŬdQ{+v!'PM_,rlrzB.ݟxxYmq8GHx i'c|]cį~&gζc`Ξ;̼$=q]܊q"y~QEk1c1c;%Nv.d"yj)ZK;kP oL;]^0`^sә{7֠Hֽp 8ᦾ\q-bl7Ş(iqyO917${ḳP=`^nv)pc@!Ͽ.9|\8 S7'`Lไ)‹Raay_0^đ`͹b~Î`t<̲5qu͙<,Nϊ s:56_Wk gsmicc֛5M9kYެ<>{ r]l_6[ȷ~ߌ|.sF򳔮[K>qgQrly=)rj97qoߏ6{$Li76Y+Bngl,rzXaJpϼC };F( Q%ܶ~I4ǩ~`'O$`}oIOPqę0v!lkҗ"|Ia;T0Zn}U3:g$n#ڽ~{B"V8_lZh4+K:NBSxh'ef`^~#qFpO'<&S§,UzNPfC࿋@63fzg'Up iWNxFw[ {w'|,À> |W "_ۄt&|WXw15?%vVtn勒@7;L჌)*;i i gY[KRc{hS04zꂴ|f3aL;?3^4uM\%m`]o &ovl9E36{}c'=(on2CכL?gOmk6D;"ZFy|<]E3gF}e OBKlJrB6qyv\|wl\e\k?َl6s۸&y*yGzfYs!2G:fԯh͸k=&3*܆bl6ۇ;{]hf>2<1{kg>cL?0/jxhW8½"r$á37Fg|'X yL)s=crx%|_pR6_Fҳ3ztzy])>^{YS%/'c$h)yo<'~D/V$뵟/M%==SZq$ΧRg=Jҝ#צw}T$inYݤ-;:>R {?'w:-R&\&}ߥa畞(M%5[oV3Ռ^j~o59[Ȼ[Hg '|v=wa)?lswog;@w=-a[\Y97?)> |Ⱦ>]6 Qҽ?2fO7 vnlg*2'{#0l6maCcaǛpm>ǢL~Vfm&?Nm&?_4êPPϏdB Lf/FCVΥVzx7k}>a>׽vi;Uso0mp s!vNtݤkM:no:wyvw~ܱ|w;q0G|f_f0ܜ6by0e ;i| Ô茇6k 6,2¤^ :k@]O{4&?' 1^ wm}/ Xګ29}Q ^gO[x&p7xX3gd;=x/v;t( > ==U9"۔x|O@;SiA|WF >n<"ccq=TVcn@O_Oyq4ڈ\_MeE:ExL?${[rM6Q!>}hZ 䯤S˽=sc! 3“No5HW[,H%_?𺽩c?4 XO9٥ ]/<'?DU'L0$Oy>שOn{c p67MZUoy|fǏoy<>eayfNG3)B.+ˎhkN.gc/ryW2<-I.gG w 97|'-!)'OAs'ȽrC.ߟ6\Μ;w-Mrrː'w+w<wS[Uu~{;<^<`%-YYW/{27~'`^k SOZH>+֠;KoV&o˿C#+d*+\C Rdg*pжu=igWq_+HanudS8A2 WV+\ =^o}+CMPM%@O^7 ~W﷖5mD;v},m!+ky~}uI =P u(Gׁs_ç{WǗeIn پT zL#\VB}cٴCi*c Ix^tRn͋heyqȤ?G; ^]8Agb~4?/ ;4)5acw sٔVO&5)q0%>Gh]Ǔ=˓kwm~mݳ-|>jf?IYsiv nNKx5iExMӖ M~!mdOg\~Jxk~ߺ܌3k݀ـUĹgi縘2ׁÆ'5/(osA{>T3J_2%kHLQ@d|G__! OlǛRW3~xӔ'')c=uLں{'JxO)[*U k[Bd4Rsq[~m}wIOه# ?x8e0{ߗOgCƅPƝ۳;5N9.>e`L+rTM=DSnSn鶡 vڙxw~ʴSN`)N{)nyןN tAGFGӦp~_/ɾ_n:o hc9mgD [=biSNdIzO2{>ZmܞM/S0P j ){e@l{%M'c z~:_w%>>5Ȁwqbd\HK綡gw"_Z+IWЙ&.U0'W0t^b};I /d*pn9ՅYֿ_ /0rh/mb[ƂwjؕuC#("V)G>k+O(?V:ޙTzlۋg|p3w Dݽjλ{ՒwϳO~<?>r9@=yHz!lԾ`=OŻwkdq? 7߂`WCx/Ϝ{y9Jv{בq OVq]3=GvlK!|=wOҲjJp7ytI^vLB$0*Ѷݕ.ڶWg@[ Z`k 7üXt6d[ZvN|c·~~b8;_3wг&+<RX+o&'|=Ou"ޅMb}v wE6EF,%|+p j@;k—z%n+Tr:nj9=|{$r/CɽNa46'Wu73x/[VU.SN ooe.%ϫy >x>}n4#xXEGO|4=_w٦/}֫/\6eM xOzD}oÛ3><d;:%%{mnVy3,diV쿱M> kfq7NɾŠm#R; B4.O'wyׁ?|c37/JT⸬BRoߦ\ǡ9}FvyjgyK28D^Qy61?Û7 +WrH %/9~"7{ևaRa&S6oS}.ctE]3q|5ѣwG^3s;z-:;f:54S_ܭk{Uyn!e5<ԃG| ׬t'oڿZ ~V1#س_S_JWaM|U䉔-Y{/b{ ؙ J*=!/7k%ƪ*cm'6,vk:&='=k2w *ی'ɻ&\u!wB]]MzPaQa-SQqgL_ܮvlqe-&=9X?~]&&opW))k ;=C`wb=(7ʶ+d[vv2󐕻Y+`j"6Gqî[hY#DV,;=kE6O"y=\e݅c[!msڎToص u0\'VM{e#Zzm9M[% u>~mwZO@݃ƱS[ixU({@T7]oڻ|M&$y[7=aqi۠dC+M[ݛ҆&zd9Yw|5M<mXw~_Opp%,w|s0Rzb/^y,<ѹ^zZ"_#zŔ. 3dò6]RKAG/}e O]Q-y~ɂ(N Ǐ_ _Ů>.ꩤ|yc'?'b|;&dOzkyL?쮓J92rΣ0yRT*^ 'tuD8T8GGΊ ֧=>mJI#Bmm6tz@8-T8-S GEGNsڃpڨpRIYiSk^JoTk? tǧ3vVvNJxWZtVwo2grelqd,[ծY!;;F |Q?3cК%G'ҢiKIJ4Ndͧp ;y<$w4uߍ{SmtAS/ضрm4 /dٶLmә3nĶ(5x%_UXxW9s(jMMjW7}Bjݾ *!J`+P;*.u)|l!G 絲H}ϨYVz\$-MblTdX7Nů#76$?Y7Kom7^ٷ}G{“X2_`98  8^BlڏLE7sxܽmwe dNI1si*e@f&gxd3E i>xe}x\WWyG˫4N|tv !,HP,SOi}#͙Hz Gj{lM~~{yYL~}~u/SW&mԙ+߻Ye͒= 9J8~{l$%یő9 _G{“kՌo*xWWkKI9[F|>| {ʛ |#}ȝ\# -͟ q| M]잠r2Y籙j;@9xsVqIns8RAOKO:InL_c,O!{yI|ߣSx툷:[8ItJ^|N]Fxt*ۿיxgG;[~/=TXy^{z|Ӕ_Wz{+9[Y/\O4ݬ/n3y߀J(o'Is`Ox΀7Y/%yn9}w+n>S9]}cO 5Jy>|IW$9-rr(-/kzn[f>mŎ6ogxMq6 [tg>'kmp9{913JyexkyUX[6cm$ykst^k=yͭ 5糸G 9,p''cm3BF>Gv;=za=<6#f7r| w'nl=|k3{CO96s򉟴wx{0|-^/'m䮒\#mW__]*.vg#S XgJ$[}QAOÉ'_\(ryGMrٌzRpLnwW_AgF;]7Y.)^;dcGLaG0MEx[%py/ɘ♱HxzAcg*dPr~s.2)>rWAe%~Eaz)zmyBf>|6{W@>-^4҆׋8^/r{ r#Oޙj9yg_Zɯdz@OA `AFy {ʱU;"!_'W)&YGZq/I9`ﺵʷr!镩tw5n䍶Owoڌz)Y_ZzL>eZ:ʪLG;1e U\F'|x9ې?A]W ՝FL'~ UGuJx'3[3Bc~(NEMz Cim`9Z5'.:-ǁyf.;EbϭF.UA5:|s399eH= Ek uZ*o~%q9Esџ"pf6 Vp1 Z‰q\a1g$D¯f ρм/6{(H#Og&ݽ틅E؜pmlkAJ]C0OVELBt&#hl@9DO#=r[yHXQ7=;x|o_ˍ{u` ʙN:`eh95 owY_r~ۻ|\M`18 |F`.{`ͷ; 8>\&~C\~Ces_ ~S(e$89OEjsg}g1 {H1f_'|J1.|/t :{s ,Tܭ'sN>Y -U웎L-سSDcekwrr|l"m/6X<@,b}%̰בE66ld~S*eͶLldͶLvl$ ef[&l^V.Wɿ]TĞi34u@)- l"- SO?GQnɖ[V~ùM6p$qً2 Dxuy}p^ }~ eYŗtcrmꝶ*cLǑ@iƈ ߝ rDAݝQzA'l}vAN] 콰䦕i+d&Ǥѝ;`p}}oכ5)t')B>@=בp;w\7}os)[αwgT(a)V>0,^u΄,Jڰ$\.sXIs,B3xNSGXv@|ٿ>_l{wn#]J:_FZiuEp[Yǁ97`0_]1tχN-%Qm91%Lÿ́A9M%t 4Yי (aƼ5K1%Bv"c000=mζL&_x>v.Y-6}kluK٥P2xϸM6C[~p@I;O(]UIJ4kXn;x^ؑ';&4sLBӋ8agv y\<2-(le8n\!!4OqyƽgGJX L .]n,:ow)cRƦ^'Yp%UUa$kg`_{YSmLmKzGLMmǗ6wicӥ=KۺX6;HGƅoaM#\ A(sȟ1s>|[Y./3HR qF};d8s Fߝټ3?B'G`t.)LxI0yh?O6yyȄ2Q<,"`["f%w6~H4!\[\?Z7IsI#pC/;Is+cR(?o&< x3~‹ʘx$.+QN]4,kG\&?WڳdOgɈJՇnD zMF?P|x$rFڶ:eFQ٘:*^RnBhPz]jdr tQ8v :3 rT\&϶Fx9ª S4˴(/S&m Bx407S2}i93D*u+^>"*yOuCT9L801-g8ʙ%TN3yY±Z3%{9ʙat9S'3mr;np,玹̻_ʙ5НnFwŻ6mtwxw-8zȧM}Eݵm/7v/,oTMHy]sT0s ܵ)<R|\j6o+67=ڔS\jpq=nYtc{\۝F9|T{Bg܍פ۬{Mk^C*ڱ7x-0={f-r ۔,OO"|E;wAx?0K$ߦep#_)^O)O ZDJQɄp7b>% '|“O86?OtO; ^OLug$[2ׁS/Is^}'{#.;3O"[8rn~>1WC{ Hd:.9vm_= NB.3lJ~ZU-̲%'|*s6g-$0ϧngh;n+|>L(sd׶t|}sVJ>SNn\f#{]RɎ.zdݱcƆ2.IVTWrcw|ד#i؞e #Q&UV9sT2mx&z4Y|qtN`\eoVhaE6O?g$Vl;V<`T乓5iQV<^l7}xW7}azPb nn<d NK> !YZS!;>3o Y ٵKú< [h+g>`7D?0<8a*hDNۺqv|`شZŦUl~(*$J_U"q窘;l^zWy۟U"!eS|7z ;w'N:@ԨjޗRo9,9W60ۡ*֒|QPfT~ըizI١b=UmIأӓېU#1=׆ص탡~Ašv$_-r>R|v#k:rҼZ䱥#XO]s!xi>)3R~efX󫙵m3AFټ۫EʛOV\GV|eNV6 rz5'9رGGYrMޞTԯHiIV#s=iGnNu1ͣ禎WMNMiuy *sPw{6n6v~oJssֈGlSIʽ̗{u:saTþkzreҙt꾀ǖMkD^D'^J/}e=b7^k_Ո8#{BSFtʹ۴j=l#JK :׌:}x#,|5G}S3*bYV3quέm՚Ҫ 6?pOTxΚh 7RTx)n?VxNm W})…Rz Vx.ŏ=n/}9PxQOX\N z==WK>3}TTd.#D>zǿ >x{zW_/o>q^$>|oO9|ȇd})/77M[ As,-4#s ܦ0ތp_TY9<<#a1H1G=iRvvo*;&+;oQi wtK?te agZ2l{^UV4dC>opfBWo3K3[Pp/:FAJ!Yx} wmO™]{*SI BJG?iw PҪ@yX$z" 7}ݙ-f wE*_Šҿ*_ޗY }[J|{k_ xy+{6g`כi:iOfT7McST;J݁0!32ݛJ IyK]y~Ώko:wCJ/ѯU/x*< r~ howy^D8U8'R ۟cеV?n~ z:|< ^y?iu. *S)i{OzD#[]m蟚WPoF::t=pl8QӴ~atCUtiAUԮxg~eΏ^^ۈOZ3Cvϡ^ /6_i: Ϗ+_W`_Zlt6/녗?~<~q;C|n<onӾx~2ʾB6'|4t>/N+(]r_1[I[?nE~VUVKxU _]鯑J>{>C8O?(]b~UkA1 T_8il`{BmgZB_Wwyx#*gRsH &藻*BoK3 Ϗ Wk{[& {@>~?~{;հn+Ծ{coה %o38_}F7.Jw;+^> PM#c'>ZT~}9ZqTUXQ3(^t G+pPª TP*|MJK _T>YR*|ZS T>QP‡>˭M_E>O ~){^= V;w);ޡv)U- oVxuU}Z~R'~\: ? ? ?pmk) T~@pU(|•pEU+(\^.] U¥.pIK(|.pQ(\XB TNP8yΣpq V89Ρpv)U, gV8wXvcrg:&6xd>'8pj*Σ>SLJi}‡]q`j7F)?'fϸ::[)9ӁFr٧z7&k|7R^/ڣSs{õm ׂtpF6y -mYi曇ǦakVnDS/)?{'f6]<64oo埽Yx;굇sFʀ?\捍zC1,o97[HzȈ-|3T.#ٯp$>~./G(57E>MN#~EvݕoOW46YgO=ZW~ + /K}7x‘LuY9HLYr]ian [N(ׄm{SG+Ƨ~4xgKe#=}7h]$*nߴN|{+*\=J @6mhZ٬ս3C[jfOAvژuWu}~UG?qs'=^FD6]R.2s#I2\ǢLe3*ߛqȧ l.r.z0RZ]#9<~WF^CZ2o9gxOcjx^Wo,F^Gyzt|ې ? ҈T|zIK^U,goL!ׄ~V85"+NA9]V:;dmġ+$Cx}cNgskuȷO%_Sos̑BOI^쫕h#z:{$r6r]IeRO"=Im՝)WM-. K|N)tj >cbio>)k讇4waܠ eu䆐\<] p9LQ;r HO.fuz_T'x]6>n|LRx=6xyH l]~3O+K㸏}{#gL^(O9a WI^ʿ^ l0W(3'K%Q [!1V<=ާ asv5E>7/bO_UoOyꂷ\Go>۠]MziڄN§?3e`(+?NaqSNoP*|C,.0kNgt;娟*CF(-OI!SIWs.O{K}]!E.V$.kvejx_AӴUá/+Ӧ`~粱O۫oڏHIjIGɾ i?dsxU0oGѝ H9Q~B<{"i~zeňPa 6ٞ޸ހ| /őx_- ~43R_'=|V9Uer>ሜ>i"z%96t-S\4C~ep,`9X\pa9{a𫁟×,d9Y@󏓠4x햳S'Li9OZΒs?>& ݸi-j?,'gi_4,o9;or9 Gy97}<䟸My WrOݦ1/{!_7 yo$@~y'g'ӰKՐ;5b$ϸ+wx|=פ!"!^i r҈ܫ"wL\&뮲V*c 3w72×;l~&z3}ʿ9$ ]Crϐ1$ }H7 //wM%RɝTrE5g{ޚC_?{>/ ?٧>}frc&weE#j2>Bz{ޤ ?'rϜ1>v^\?r}ʹܣ&xxKr/A.GN@?P=;CP9 _x >eC엽-_}lY_0zfBOGm)Zʝre)XŠ5x|Q 7|—;=OS{ANP=IۍW2Ezfw)/y/9`;+pv~Jfmzmvαamnۇm0m]9֗m}=֗o=֑ ?3֑ 4֗#l} &R6֗ɶ Id[_%r=֗b#m}i2֗1#m-i^dz֗^]^e|֗_([_Q|2֗Gn/wh[_FѶmѶcK1c˛cl}Y=֗i/eX[_^k򱶎SXR{/]2f/y/gs4oKo˒oK =֗l}y8N M~Iޫe&) UxŸ)&[\N PS3 '+;pR“^>/)%n Wx)¿)a(H! Vosl} UP R_㧫 *R65KQE!Whڳ4Ya %dw /ao?+h}OriEpCnO85m?kǠg|zZ>}O_}o1䦦?zz(Ҩ[~koC~:ɯkG!??W1VMyUȿq5HȿF;I#<5Fx"5׌w}ʸiȽF$ku w4O' }g>m񯇞}[rK҈w?h.ʴ̧_ +| _k-_/5[o"7 5> }kHY:9○|1[yxç>+O[_|EZk '5}oq5)? zAOGt5=Y_QkOn.BIғe9Nq`K{0%18 tn9+?`=Gy=;J^t&N`MJӂ}:e$4ONaAl"-iD,}wx]/M{Ws7Y,ɜF'\W'-zs0ľep6 DD Dx poKx!p  oFv/paGKI߆~¹.B*AOsEh \p/DI~~_4$ '>+|7Ã{ N&\8\9 |`"O!H8\5*e  Hp #ML\@x<0iHSd«Ox韟O>sQYQN&<đp3l6D %<8g'Ox '|O q$|w'ء>00 W~ԵODecƄ{F\4.YvgS~ 8g|`>h paW g|'\'\b>g;dp"^Ʉ lqwOx1J&x.)$|؟d„k'qIW=? -) ^!:FO-s F%S?@6>zS. P.P=O_OREHv%t]ì{>SOmK;?ȍ&w,}r?$7ܯD.Cf#"9ȍ~/mr$V!7=uȍ6Xr!,WҔ< ȍ"w!7tb}#7Q^r󒛑Jƒ$wۙ,%7+FKf'w9]NnNrw&*qf]ē[<$7/MGn{rGn~rǐ[$cr[o-Lr{ܢCn1r}'(w[>FnIr[ܮ&ww;rC <ߑ{/ɭHEr;>&$|B5? ہ(rO&7sMO"r3r'7#(J!7wDr4YmCn6r{Irsn'/-$_r1KnYrӑ$'}f!7f" YȭJn6r%7EnrGnrGܩ'ur ܢn'8[ 8 #73Gn %ɍ%},6$7]An2ȝ@n;D.y'?QU}W}-,ZlG뀙')m \+Bdy`N< pyN4DΏ&-WѼ%v~ >%ZnJ5O,-DV]bҖϜN |sg=u]V!),o3'?2u9=-1kN×OD-\e*2[,q|<'NYƊq~:;y̳x.,,<缣Ez;⹭̹xn+s.ʜ2幭R@xn;W^%"B_K >v[hM\I3y͒f;3y;U1yyf2W6빲\ϕlz.?YpY̚b\~&5լ fMg5k.?Yp̚f.?7lf.9n.?#fs5+ìYf0Yg]~N3u9|4Yˬw\f2\ˬ}|>dV־x(sx;乤c̍sRYsKgx~*knN]s+`[R]sKNkns[J]s99e-1]sKʯv '%]#=HOpfY5ςv-ڲZ] M,hBkIZhv-4] F8a-hעk)Zv-`A}]Eg#< tkS\??=0^ŜsM;.DŽ'& DZ܎=M'qeߪf Hޠ3@.C99h}_Yrj'o lˣBX=M+>{нۻb Yp ~ryY#x]̑Xauɴ"*? ;9!Uc0!0Uf`*XaWyNsf( ܽ @ݑ'/ ?Nz^3 [u6Ḝ%2Y G=;q&_ӎ mn*GS$^n!| dRN< \pO`sCx0Y ܔp %͸t-'5ތN=0uxAJy&&"rgMx&pMVuW?.=qa> 5y"is|{vۦɹ3d~:^ 4,K]LnUv^& -ޏ^^co=0%$rZtG \e}ah 6*8IdpZqӊp_/iEx 8V]-p'Lx/ts)e>_0xk^t ysؿZ F=p2eI"zi2a/nWf^82kɄwpZSpCR;~zJW_a-;{~ed‡Ey )5_EmoyipbWG9wuخn9M 0InjS! +DgaOcYU},@j_Ba-8$?[|:`>h0.ܥq'΍8S'w @U>'ϼ聆ءu-ocD:@v;Zr0WdT#β~^ڴomik0y[桫Fj]NMN;,'KF)̖Ծ[c]<^_>n<Xa{f=<Ζu]>Fs3'lbUv>!-.%+Kp rix}9mųx>S/1gbU;Mo rOuN+غϦ%ŖA)NMe[Hp[-sHrڷM6L %d\kҠZך4h֤& i idWCVb3m޵&} |mEQ\8뾭>Mnr_#A·ɥuvG&?iL.wkE9|vr!rkMr'oW79[z{eR1sY?O${'dc6GCsןpY Ths\Ѹ w+e-էtN;7-Wٴ/\޺2VU8\Q7/Ƙe<0xMw}.5]&>bͪp ?Ηp=Sf4b5>-v/\gg"|/ K'd0kxSd3{Gd  ?[!|˔{m2\&>uo?rxdmOH:Ŷ! ɴU2'i7'e9L9浣GEOq |5O)g6eח,/?2zm;d< WO ' Z~y O>/d^ .X|\?GqS>+t@x|m)??>'V;Eio Ӱ-Y殅on&}Ʉで.

߉>_ɜ3G|IK(PvQb@3lwq_8h;ym84vs@jPc/x9A;FN 12]1rP9J9ʎy|-cQv,됸D۱02Kv7_9wݦɃm<iӤvs;Ws_ʌX]hl&m[x{3 #g`pyCl;"l=mΗ5B'};=ut\y}/҇&V"B4 +9{TjNp?ɮC#=k/b4pj:sUq> 3sLp]Hpe wyNSWN4rAk}vH4['Om-Ÿqtޣ8|pGz|eJN~řtvQo͚//)p"(>V~o߷D{g~k$@=eշ*oM'ϻl<vY{J}+= %N[`̩GgQO :M]W_y"wI/9mwGQyЉA}E2'=2Qf^;_C3_;jʿt*@]x9S> v[춺ݦL_O1 l圈K95v{x$?KrCv;knMݭ+lԭ/)O9+~^q}mw7>m0Wz~}s`/p}ס9cMv4gJWxd;Ó;7wZcoӻ6?}m^ 쑑p!.gᮂ$KayM?!8ɥюqhQ]n<Wr ~i0o67SM?Y;FdqON/nGEg_~ .|/8`֚<`x$k O{Iϳ0Ft0Fux-'q\HW{"%Ox9`_O-φφgTw[~(pO{ ,q7-s@hD=n}Je+?^)iQ,\HʖFs/#gl%T:g^X=7{6-%]/Fi4 oWi[z "M|Z?ӽB'<o-+,2[Ho S7p=XT\*_޽Yv?d?[Aw̹B{<.5e^ p%OnJg9XǦ,~ Xwes?3x?߇l>q`a_Ay8xfk|7C ,:LMYUiAu"i(a!zF9̫DǞy3I=7~??NaGtunsvp$}R#~\{nEweSSf׸SuZztʼ:xz|ڜm^[wO2ނ\"9ϟ6ь#7;O滚uw5Nh9"O=q?.^" \e/LC4u?} ]Gz-Sg!ڵ 4=נ@D}@]yl6s¹\jq6= Hg"ץPypYƮ#-ӌ!x’6 ^ C9WдC Ǻ{%~7NHUpeSkLg8c;[Uϟ$'>[d.sk W 9yn+:\Ч=D kDEoضdיW7#Wnd+\NƳ^>_XUȼGޥO C!^[syyI/kuz Ner_v,c gzhv=YZ]}]mv֮vf#Etg{wsN%,jU~㬉#/\k;Lo+{2|rcxkM{B~`޷uqU޴ :#= ޖ2yܞl4o-=g^Gvy`5M /Tx>ec8^R ߯p=;(<@ Sx(ߩ;x)]Wl$\w*\^ye=L S+][tV[r8t5<]a6̒W0l^i3Pq `ދ? m7x~lE]5X\\>78@P޶SiHegb;r)4b,\%+qשܩ]7*ɜuW+ U!Wum9p'5v:0]"`ޗ-yY`#v•oغ ǭn:D φ?m=Z'nXۮŘeM˹ `YS-4e))=t|ި\u9S]_3هC*>q^rMo 1^bFK g 7M-nd3 ^Oq,CD)}e==Xt_e[Iyy}x"^7 筚xޓoi5}o}~{΀=l6D~a.fRNބܫ=e@ں<`5QX>#a><շlKu=FFPWҔyWT?1|\u$ex48Ip^ݓu~Q50djg F;/U6# _B]٩ cL~\ wvl%g&*AxY:gwOe5̀_M¯Q&!:%l~]N/_wBXExzYn8P=w3X•Ty%Z*8(`t_Y¾]Nm Ue;g7HBE:t@HB/Ҥ1ҫ& A:KP~(+ {vT ߽sww0g;˘3*\t189EO"+3'}|'ksp5›#8Vgܞ+>f^&-a? 6?&?'w[7Kr뉣y_m}i1X3cϫ>̣{/oؿrVw{lT߂S䁏N Yr˝&7p  <a3_I>g}23ε8!Nk x#CAT>07N;''Bjn=L]ylGkw="{@z*b;`è 1Wyew?!Oa'c%2оYwcW~܆9;m%6<ҘxIc>iR7gl[ӛG#64Eg}p_e{HYL'dI}R7O\8Ny8eU7kU~Ǟ"C3{MG U8UNUm!2FZ;໡쭸Ps*qn(.n#|¿>O86|…#ܫI^"El.8ƫrGcצ8 WY'˙w&0\?Yx!Rڽy {@V ^w#nwID4>dpiy*oO Ⱦ2mG}w G)u%#|7Y΋?{vawE!.)1;6c`>>R9 qU"%-|?ga ;]M8hs@X8*co34) Ug<鲙ީ\xh{.'֭} KD8ќ\*̜¶ guT:'t~(Za[?+l!?_a)%Q`ݝ%Qi N(; ~4KwY'}%| 2Y\-K.DlLC!\ x> a~#}\mQ~E'w=Hf~eIy&Ji Zz05aVIʐً$_}}9ұe(ikC?#rXOP/yzR*Z労,o/WPa3JD\d*y}%Duϒ%̍%eFKu3Z DK3%Z;%]F geνс`xva23{w*!`*c<<ږ ~ 쁻ҮL- wPx3{Q`* #r\n#s~?ǧt䷩7zz dYCދw TS+*.$ VM0Vx[ֽ"qW8L8ɠw NHzo>e rJ }?#[D#+=SmeWgNMmq͏9vp\I:ǵ/ue ԡ^('^7".-N'[aoW d6TJ'oVPNu-@¸'oClO{ s /fq3* p*'>-y|̧c$7@U _ :.ROA=1i]@ - &' ȟ 1un_y* )/U9?X@ro _ ~Lɂ?uw*  7X[8RL%qw~o5.nB]AwE})>fy/ttϏǀt2ݫQ*T۫w5wprd>{:0??WCA2zRqS4(m*25zf!\/lL{edy'r=x]!x͘Ì7܅wx]{qy}wM8⮑3Q#˞v+o% U3cfL>z" ō|$y3__7tJ6*qۤem-k1Ӗ݃eYwYw;N/g\κ[κe9ndynJN Vx+g.QDoRx ۛ⩓s%J~oa@~eׁ| yor"-!tWE>)ү-o| *ӘpA@| 9(|ƒ^ o dqC{)NJ sҟ[EUx -BWe*[=O_ߐpke:q1pЄO| -S,ɍ' k6\2= G*_x(*{:"}ND/5 Ot}:&s[eNY7g).9{n0c3R aMg;Wu3fWuMUx!]k%|x#0W_Z -˲s56o;#e}іR"ܠɃOLV?k0+XiSQw}wܰzmpt/0)嵢\S˞NLsݽ|H7Qfkz8 3m(O} <2c"|yyۆ'({~Ԗ\MId10Q/ϕ_+ik UtUX1ǩZonMsyuI%.ytVuɣ9eö@uruvuIeåϡX P&4!ys syk gy zu 7a>Io^9ZC/Do|kT̀{qyzZEzޯn09jJ|1 TxP;X+ޚ]c5eoA#WDkڽSMZ3cz>͌yO v--Q}+Yʝ$ˉ\9w Fٗ=f=6z3虵d]S}Y75lx96xڶVKMsm·gx[FF}65˵{:$0+*&og_LQK Jڥݞ8 {Wt O>T;T>#輔ޒ vM@pNuGL=g8$ؽT[% RIKRe[g `%C7| &Iri ky=>f3;Sw88'<_d#iɓ {N~;K]'rG%'vqf>~Ak~.ޞxb{;G]]γ%oyG3ޙq}r g_gKjsV_WoY' z2lfmlogu6-Q^'bnjו̹pRֱpi];~M/g?1wf(a{OӸu类SG>[Α{)P # ޿O)y1;\8\w>/X=3>@׳npO WOJ#{znc~6(qIqi 8fC[s|}L‡# .D$paŸG> M`>wHOI 89U$zѽKdm^wBxl}FҀy&]wÿ$ٹK'2\)6)3m䑒Z`5}dRܕxR~V=8;Efev]a(w0mLBS/F8UTe&~ JdZgDR$BR%IZ6}udl5>PRz>%opR^mG9_WxsLsh 'ԫڟHIn~p*4y'oSlވ9b/ByۺBy0Wj=ٟ@ \7$c`()صX ouoJɶ^X`ndU3 $۾ynXq?sxmO㜐z ܞ}8ˑ䋋\s?l,~~,b ~,f㾄 eqn< e#L5ڗJg>uޫOxI$x;9˧SQ'I'NxuDxw'It&uI2x I2CxSdOx7I2 0;Cx+)¾y?ߗ$Nj?I`>#AI2טJxw5?gt8K!9_Rk[lZdH#~9y|yq8%SߵHuo9vd~3u^HA H#oE Y+6SƒgE%<QH"ig Z·#]G^&nq$M~½ϜODN!<麈4z3 sO@'_1.+,7OOz%KawOپ7pf, ,M[.36Rg9gyZ6koCUz Sɲ˿R> [ψ-Fg~ O-DIʟ~jrS'&(=l^pQ7MW:fD#8ٜ]͠El(qƕ:|=SCۖ& wRv^aAݲsΫو\zu75W_,'W^/P{vQͫk',,fTY3r-|A!a0_]Ab뷫YsƑֳ iu?% lOY ' D~ƃs] ?sr[ g={iC\ 6yxl]ocS"^#˼^Iٷ4$]pd]̏w wN5?ᎯS~`\4r}q㱍pe/UOc$/iNœ%rj'#; P׸<}Ɨ{T=AwmE@x\IǩVX5 N_m\넀c폓8.m k v`}C71|/~׌~ Ipzs;32/q`w#E`Ey[^j`L HC{TC|5Nsq;q:!ܴS09 p<[9ÀO6~#<9 T<|YnMC{; v=һv>|{㐿gqYMrl(eb?Jz_JohK\:%GpH'2w>&>~023|d=)(ʼh؏u9NO-5V2=") _T '+Q! Rx{>W {Ch鉩a*\$\XIytӆr潻g-[yTw? RhnӨTs+zs&،]Oı%~*6nӷ,jϬ U3C&Lޙ}IO|ȏ)LfJ})S=R=)gy2 Zf o!ڴ [xt ɯw +[H;8B+B+|B+SaRT)CK -mZ \DwZڶu=ܮn% ɾr+mʺVg)!)OG_n~n:b']&yQ٧k *޿YuѳԣǴ[RWNkN?;gIwc; 험5Dp\^MozHFӁ=~;x/\nnN!]mçYcov6o@yz;7 3$1~6^u\v{y A.R;ⷩOޏ 93JuSD%%]NT]藻ص]7a xqpdSj NhX2TX۲i5DWwęG.f'oLL/ 7u]m\m95_ӯ^HsMMHw.TKWWוs]]e>M76)n͂9^`SdވΪT!­x2v_%yO;97] tpY{^ !"#40 }BԃO ^>J#!y]0fp7YsJ&pq7lGf=!i ܕ!8&!ܹM'經8)M~\=Y ,"Co`w`[U`>ǃ퀳 >Exp@ぷ3qNwcipXK vlcCcs};ŏWT>XL:}mVUp73rUKx(a:ow.|^Pq1MBGxRgjV#~̾:[nO?TlP 0Ǚܹ1~zҺ\W(#qU6-kX.k{R-\8-Lex["B 6OfTsn{Jh^ rPbyK*9$VRk@)ty pZ,M ii iiy6 oU4,Q}qcXSxڐ[x9We=c&?V*<9΅0[xk„m_{=oM٨=Iҧ7iV8i {=~xIǘ m*,93{xەm ;=q?~WPv~$8N;V =)S2vJ2v9{O{ڳ!w&AnqOp+8jOYkH_L]|u'/r}2?韏{}_}*U[&iϸE80Cd/O^v/#*=#[ ?p:kW%Jw69o>{dv~H/;0W1< =AEfݕ+R1{QqT33 @oc[wx?4-qzĉKOXk*\15RǞ!)?Gag9 r'IGxovYvau@,}T{3$ Cy'yQn#߻p563udPmi^]!ue'tX%Kr}x qpێԓxbΑ9kϑݾٸQic}AC w+"|XrݗiTwW~'"UjΉ"yL{s#EJx4;apwJ0^0+tɋ杩C/8G?𡾶<.n9};+_>9?W KX&8,H\%~>{\ŦƉWO$-J qjʜO*(`'9m fxƸnLs-\B1RXŵ<&1.;\{2T~vHa䖀ʕ^~_wvi{aʫ{modyvoUm{gouMĻforY59]܆ /[9}oE_w7-aְ/ = w}~``?_[@~T& 8͔v.X=$@rR6zk~kܓڀ7g0csw@)wN7%|K`?* ǁo7~:I]kH}԰q̟S'$,WV u o2n584h xmIĥjT%}>~3|^6}MY+{ͽ&Uo>4PÀ1Gn_VKU,Tx ~@ '88V>. c;j*xP~L{2ٱ`ẢeU91- sӿnMz6Y|)ϝ ؎wfD>"wYO^=loz;+G(y \!-?D^Etgc&ȽfNؿJ7+lS.n{7Jỵ`~<"OǼ&H_|Tlu09o@um|'=푱7ҫOOض!m[|C,JwKa\]agEpѶ[_wT8 B] Qߦ^5Z '0چ6^Pwq ~kwMS.͙2O 7cejcp=f i/x(Vq9"uЭ] s;Y?ߦ>sLhRl]ˬ9VJh_Z怱Ҟ+kce[ǡt\rEG힥̘Ef"s/dݮpQULhE㬟ˎۄq8e3N<{\ xFpdSy2g 1>+3Ce_dr!vͰ!pό;?kC>gK/̲gLm/M{}I͹?~|?ߙg,?2oM%͖xj;[)k6`v 9=<q;v=U)'QP ~d 'ɏ+yO;Ckf~̡=3y?l:ލ>3?]sU✼L~Ζdؿy~iXo#o ){7a9?Nwϱ+?+d[HV١a7s}lIc)~8BY>&%¾\#Ɩse- =r\ٗ}fn \9a3JrÙ9m\VmJ}\7Uwo*?Wgxo]pJ _1W_)ٝ;5ޘ El>f-E5wNxWKΑ\0ey̍wxo7]?Kk_U%aDryu2 G{ ߸xtg7mu̙^ޯB.rN3byw_#Cqw};/L"lf!.#:xE{:- Ӓu/F᥂W <N%6r(wm֗Z8Jx7Ac'l>,)qϬ]~o.{U|x3߹ JJ>TJ㇀w=7g`8̝OAg~rd+H<۷smgb ^}$MbT'm_|wK1c KK]u~@1$e9b1=nFsW's|6~|}`^"  (=wPa߫/߼\6 "u=֜p:Ҡ n]`̄vλA`x$+Jx/s#wg$fDp |97q*{?x p>< ^>/s;@A~s~)w>̘F⟋oHwtg F/K;n<\܈4y!fpxݏN,S]\pGn93v3Sx)exGg|tGUٓ m_/ @-Qn b%"M~/ĩ{g2y_ w.Coke2L%\ezx+LZxL+sL}~_.rwaNˤiz y/yeٻʢ9I~(U73[^n}/`@`o 0S;jTg._\~r-C< ΝTg\ؽ;Χq觚=L ?o#G9.W}^$28{g`>Sw |pY`[IT}bM^6tPm"ncVO~\IG7'VVcsw({^6z^wO ox[ۥVHyX {EaVG@}s./qxNӏVVQl Iv_9ӳ9ˬ6+Ůc%qcp7Qzm?WJ`Js/yc\sO?CVwqϹ7=^<=T}6Gm ~GuSjŸr_O#6*f<Tyr> CQYo3e o8w+V8] }^k2?@:zU!qd?ף5SXͼ$d)Wg> _a3SdkJ珀 ;M#ay2l2Z|r۶~N̍:df>dV6N . OU~V*>q:Wg\' 6gZbU<k.m2xgArkj܇q'd']Xm8 ˤ{Px غCS/ΛwK~~: `~Z>~.S^+{,8o~ h $7]pkބߦ?ߦ.xO Y:|gk%Z+y&rd?TUz;uomINt_':vy:;w3{}#`g^'}sdtIm{4m6lho)F܍de+oM)o߉ߦm ,O|͝ Ao썞 !MNbp=ɆפO9Oz_,rcš;38mr X?Ծ=f'~'_|~ezMӏx3]B~𤅉e,x3w0d~*gwyⒿK㯰O_UK͘f-e~ wo=xK<DsמpDl` #\6 '!E[=k8untG9,wo orw]vJcl9iUIrfѸT"AKxDxQ TQ1gnCUoTtEug;#>^{ok߷ȼ|̈yȿraXJEoN' _E._x-tVӅFRőHʻVe+% bV<fƘJ<cm0YYgџW9g 7GLN#zs8{kCAw|VqF1zh6km7:ycѬLdB$ÙK8Vy7 >gt%{2gO}4_ki&{;|vW+ڷ=a5;|hX5|B⥹Y^:;;竼tvvξ%n@'z t@Wz t#F!|uv`~ &~gWwoƕ 4G̎`ޔ  K~{ utweY~Q'mJ&~~}TY#y6+sJc(#zH|O`חg%d'AH},Õ"2t~"(Cd?J&LRv[wR,΃V?SoM/ok>]hfNvgnԛ}f/}?X#Z7L#ljYԾ_& (o^"scn^K:?װ(=hO3yV;YJfǬu}3c/1NR:1yęM![Z>ܷIRtOD>q-o`Sw7S^wk۴o!Mϗn0?%}~AGjCpmўԆ|òpεܙH=&uEgچ'tڃY_}BTyvORaj5pt;mc4Sn{'NC1>ͦ9b~۱)gc]Y4`|-{.ű.zHv){gߤoF<}%o*>hf1 %Koy=Χ2shgM26Ė㹀rU*T?N.u5Jꁟy/t6m5KSO+}dP?2`PyS]κ&pobYo a*d9"WN1ٴf=lzM3m]E4-Ps=!Xߨw\ᯛ(MUiy()+K 8+,9xrp!Mg{Q|oHw~{Q[{rX'{OK<Tq~0DsFw{:[gy ӤNOP <^DJwu*ӝ;;])[Lw>c b{&n[ٷN3JPvO0nffn˺']2MJefclW8W [C"dm';t1/f{s[fnye2D!c4z~><1Y'ņ'5'7/=jl dj!1֫eM j&Cg&g<ܜv~(yW_N8Ɠosc(#˜?d[oܙ"3E#3E3E2EF6m!]f6οIs-Й2};q-BF KFho6Δ9i?)s]vWÅeɝ% ?kcc?7],j08Vi űȬ-3xޙE jAz]oTFwvq _p4'a,m*as_%YRR/9TQn K2c[7`cVvwq8fnnjQ _3 M7gMe3E2o)~:̤L?Hqn$ %> nc21ڴ[䜲[;کn=Ng$ 4g͜?<|['~ϼ_V6Z4DAWf,.Lbpd 4e#)- ?G?V{ 9A|%_L}ӻ6Ə޽5Bg7Ju\j Wu.Vu =$f08=l<}ofoȥa6>(8-9RF4Cwlp,;i<~z+9pK%L{#^TpP_ k>% 4J>Uo<] ;5 ϶03#\H/kQ(iP N@{^ xuȜ zAZ~&KƾsG[ gB!y| ڊ ڊ.E}ss'G:3oxx]f%[vKw> v M?|ǢϹ)6nޫ/ha6ɘ?H$-"/"/":S[;d2y>٣^9&3uHCQ*}>$s7+Ќ5q ۛ>/$s}e߱GxVQn 7un+%GO}WiQ,>,IiŲ?)q]*wdCX怋 %^r\JJ%z]# lY7[ۇc*n+vq߶kVy'cP> 5]l=D_I/`&7jSou ߃l}ZF^ o\>tLLwV 8u>]+V^i[` ZV>bV6h]AmvrT>W nW>SFԫsxF1@?2N9?qt?(:yb1 [^)c~=uTqsvY?<qG>NY[ ; 4;+>b}αNIzUȞ%2gtX-!J;]J) n\y_V:6qi41wo ( G+.?t_ZU oR8IayGɠJ Cyؗua[Rx| Wd/$wky Um{GqdyVwųB,Q< =3ۇҍI\ rs[SY쭗r=hz)̈́{L\Yo{zǵDؗ>4N~3\g9˞EG^枿y3j,d?Kr##93\o ~ jra{|R_*f|+9sctsQv_cY~m1fH#G4{Ov_o6CytvDgPseӬfEaL%{ЧR2FZ+sM &f(MKLD3t2i4!ס L3]V o|&0+>33-G2Ffde^UϽл2QGQ1EшD*x;"% (ME& %` PC !uֿ^w^nkz>3~~77J:lS7J0߁Δ.rLy_i` ):):X):ؖ):8if3NgW$>e,%y*K3Kt,ΦߕhId]!It.1ns%Ÿ̼Nxz"H;R_?%< ut6.$MJxn=; S֧j|N8O-keMpFm@o27}'w`sGldM^4Lss &jL x͌2w0M,Y kboY*T<^0gF#]>yU=+ZyC=!G]Ňu\gr?rh|mo*DEi*7( Sx-[[_?O~x?ˬ N-sqRrvo /p/"z8m!&[d"0pW|#?wJNY`^Gx$pᏀ3I߆Nxp ݄sg8p!p*C3 7wF_Ӏ1N9$I&ION E˺[\{ny⅊TosGoLmk}=ܼAQFnIk}TMnp+Sgl1[ss/WuZݞuqEo;!Œ.vCڀV;DwD|*o^FԨۖU(`~~ 0+a ߬p#gj;E7_])o**7*gs`N| xIIy/vRXwG1fɗ|\øTx]H£m_6-TėJ;b?Njw'-k,}_]Pxw:NVq*!{I0i]V7>pmu3LYOM/\au'?Pu*wW-lr;՟{w˜xODG ?)}ǝ"GG z@etT gyEΐn>Gf '{ۿVVݿ)%~ȧbQ-=6n]عcq˞G }>iB;Go_h^ cB;Zh;wNo?go9ϚCT_698U(yg+<Жoj>p=7U8a3oSqeaQܟmU/ym֫>#~x$%.=?Ӊ\6Ѫn53*[ie|w}NK[s+0xVW\ύ\1ȧ8(›DN6E6:)ܵ>eƒ~] r*!)lzl+򹔎v{nV^PGԽ*w>u9Ӑ/p~Q~O*j9ٳ yw)?S^%fo*̿\RLOfrpکW}Rf\/_k'9m%o+Fb,=Ŷ |8a,.$?BM~\yc?E y{auؾ㟊m78|NwyE ~ |ryrit+ |AU؞#pk&4"⽦6۴]ֻ:,p=oGq3Bq* rNjfoǬq|[f\FVԱ9Aȟioy`JB[]A9W ed/xJe 6+_5;lx%ȿ+Fo'Oy 9Rn>ǐi5wٗSXoA^SY FCSj y=='^{N߀r]Y To)g㼞~H/"ď^Z^? fNz -H3m٧3{K1vѼD+yqwmot=bɑzr}kO%6(4$8e*iɃo 6 /\OLYwtƿE~SEf}:9*GvTA챐lװI`<&y0Mm6ɃilK}SfPozLYq %]?}3{>.:Z[yw'3!es\4SP-8b36q~kpO/{0P}sx>!DpkSu2N^$kuBx(f rw:#m'WL|>ǝ%NXȟt >7NmqBϜ]NkAClT嗀' VR1 9w(:ӥN]m^JkOK[ mÿg˶ fZd '٤R$^w|I2]ocoKHW}o(%^c}gwŁk8/RU2,ѱW;SjǂOJ˧S{ڟ~;ݳoNʹ~C wg7¥y_I/؟"O餿/h@P^vR[tYmv]?<q~_nX xR w-GZ <,`tjˀY5|euiyT£[ɢZNl?z7 ~L)A IW>9KxJD}grr1o_U8i4ifI9UƜNv?G̝]b N5ztiS7V,q6B: N'Vȸ~zVȸ~YVȸ~k+d\B5ȸ32!/7|7MUgB'nτsu9#dxq0ZCQ{!gd 8i;_`ºfg)*VذL5[S5]M'8 ~?(?sNy^#eܣJd 0cpȍc,f;Ucg*O/8}q{<%O#<U/%ܮ" \l)-+n͓9YΡ,zo:cnM}vN& 2 }վۡ$S^x 79sNgs_K^Y6A-pL.n-~07>gs?R,juǛW#H;syyGRaO=].+IkH\)?S'wwMxHC4 x͕lkגx-6nb.;ךxl^h)rwK^=_|5LY@?A~ɳeVzuqI{-S") ټʺG మnsדd`SϿJ8!%Fjguy7xx=I>4Ɩ 1U< ˯_xId[ (l5ݔjY,Mxn넄wJ;mJk{6zc5$ww+bB|5Bw?]y@)ۃ)GqOԣQQH=gWGBEjP"+ IՏ3[ʚ wvbvcd_g]B2R{TY)mjf$I&唃hS4th{o_C|?x(Dy*a`s"sܦ g7ĽTE6zf6s98K9' $KX2s9HP\@T`FP ((ܪSU5 ϝn[}:>}3@y^VZl0^Ce%ƟvWn8Yϲ-i}%+Ց|ũOA ay!"xTf+cC w^:=$mc"(k"o8Wd~p_b:|\jpyzegb%p!J!nޏ4}Ii!w$):)k"ӗyXix1~W3_Da2# 9n?q /^|=ʚ'ȳlWouㄣw0F~(¿-y`3Ǵ<Y7귱 Nq ,4⹀C8Nע( r\15A6Wekqn>o~oFQ&|LZ[ܼ,to%c+vq18͘wyyu1/1k|}+mJTՆNVxSսl=SśbxY3Vx=i?ȹțaGGR7b|Ҭ?uX-MT:d;5w(?i B;ep`Sc6^o|t٭7!gG/I ϾpslMZS3'%܃ Lw mڕcfŽcxpb+66pMk݆nz~foPBfbyYB9q}~L2qkbO6^.'5{\ '1s]I=Wq(D ̸Lvb_½3{Ycy6xFsHͯA6%nYky[?QYf/ּT,~C1tVYj_rAPɂ)➾ob_]DZ>v̽KdJfU K֯yyA}N)' '2Ƴc~$ONdU(c|NroW'(Vwv.%7>sch$HA)/3Q)ud>#4`Lۋ}&M¸:iR?Nmad`Ҁ}</<[J a?4Ü1?@%MI\J>]<˥4y2D Xo dy!OH៿ΣpgMJv0!%ǔr?#q\E*Pj)>[fzm|f]vu.^uN(ENtxS, )\Cck_*3H^{n\+qvT۞S0FKelE*P?XkS9uD27]ps7v\S&;[ZH="@ Jhel\05%NA:kSU-1|֛5,Z*hgc0l>|yyUܚ+N&pf)UM,3a~3˟浀i8P!?tao"44H@1 o.(ߨ* SX/nОPM(C oKCluڊpe$>~Wg3[=z~~f~ֆvpc69K>.1;.Γ{{>Lm޷៷})ٚwl~r3Ut5,z%EZwִ--Ӛq|o>1=1DJ@\^T6Nk?_Nt:xLtf\T:m.}:,O'lufP\1wVk Ng©-Gr|ՇK*I >xHμoΐƇ }w?R2of<:O߲|6pQK`e0}P±m99vx[/犣ݹ(ol30oZQv7Za(-VMmJ0*J5/Ar$;]GΏ0oIIٛɼ=V:mao1XlO_ ׼nwA(S 7̲)Keb[ +tke2i˙LG)le|3c/"温nuBLsf2Q\c.c6iN7Ԇst΄Hګ>}ɴ?2ezF}iq#G̬1\ܛ {^`E^wjUpLZMk|nb&3g }<]gߟx}e*++ͥu,a.) ƺWS&2|=},g6eyWY~Wž칻ͼp|ȉ+_J(<}VBY)4n<sDSFަuF,w*(/׍p/chBUs^Ȗ]Xa]> ]*KNzJOSPП]k)_!}gB6lh󐐍2|Pgxu#%ۓclɯ~g'QىL+?<_nO硕 d8KQgE= ֛grBx\s57T4y{hE26sVEc? zOGZmM/ZﴠQ/nJu83̴boةUR*,/eO?5mf~e,/e&+ܛ3#8OrClNj9|wq֯qIMMaOa^8d/d~Պ8}O\EԕWݔwפ *{.q:<Ot.`^wןy*ƫ˼q ձn-7V6*a&f^Sto3s.fy V 4˹sYZ1ݾc.S^E}T;e>ٝʴ/Yu sBCWoX%){p?,e)a^oe'\;=$mvJU2o0J*\S& W׷֜wo?f3_alm&]-{n]M빩 EWwYo*e yG?iezUF+<_y-2Gqƃ^<7pg`v/bṄ%Jۭx(wWy{1oZ3=i׆6x(cs y+ܭP=IsSo?͢%+O_G;X~;g'CV#ǩ S ~ z]=ֿV&ϰ]۹-ee>/vԍ0?.Ug~J1X>?eclG?wl)h?֏ Pcwj c°KS\Xw5=: g8&}6ȳ'Fu&|dj|L1Yi#U?p>5 87g[Ќ 1G~X-v< ™'e._^$E2YȤWūи"MZxP>g5VQ}Hk}֟fr΢z*0.^Aye-kv8?㬎'^FV5mP}_Ꮌ\U8E1{Ua}~-T8"j}!?9}wl$H'2e_ҲX9k_:x7+b1m= /|ȋ),uKs1wCƾNa#^{x*i*|\XAm`7鐱c[Z{ G48…SHu9H]S _f_ _T¿*XH{Hr_*e]ʧ ojoȼKV:'e_a>EP|ݙWڟ1lSomg:*>w2&˙M?΅Ahǿ' _c*]ȓbNij4[d{_qy?BXqƗ<n[[mȸ_@|Y:2+r& >jRopOe+|?̼zm)o2 K"˗*mƄ1e"GlpjwVxET&P당{L>kQ*{'^,C}^2]t?6}\2v ڨ y8וl'L? <%Ǿ˼s*92sWƳiT6~ ,O?<΢*rw&s? I=up3qýlYsÜNg /-Ogj:{ghiy2=/ҿB`aΕw>6!o@x>L||nU|ש2ToUr;V}+Ҿi\PьW]wRy[{լvO?@5rW4D"<\2ڼWO <u7Pv5_x15qɛ(dy3UX<(=+r3H[C1 +^\[3GCѾ+ˬ'D$;\^Xeg0y.bN*NWa~+m?I|^7e4=z=:cFe]m^]Zy}-<㘸"[*9Se#_OLekH٬74$ͫ7yr>lS+>w%Ox}0*$zi!N*lf6gc>Sz>p6>SFy3j- ws<}ԯy91gQ]_Vk;GX/pLWwY Wy.u[[g|Ug} Jϝk-Xo#13yZx6Q~K499"9(+ۏQ p8~[2#whrx-w'|~ᘁǹe"8G gs!P}fљ(k=ҵ30=7/#3f@: q.:xx/W%^rp'?Gc8~9?u~QdՀT&[qN3NDou/ƺ_\|{Gi @87 mHE5IꢝP P~V*UN?2+y9mU3=[ Q=]lLg(Iy"ԌU͹r^gpC ,=91UKmَ\/Wub);;l#Eá=.d!XуWK֌c}x3yGLzbEu9͙l'p(k~[H7U5- 8Lu0Np7l UYG563l;{;Ow۔Ϫ&{Q[ڳhQ9jU9Gj4_`RƂѹѹUs˿"}.{qO{8W 'cƘ@<<"ڧଜ^jDej'G8Y:nΑijd6'bϖK$ދG+?y>)Uߵx/jTW΀iu3~_ZP=1=_KOnNﵾE3c[$c:wX *5w[?_HJ3[bXLL$ڟApeW ϕkq}s%)cZ=t ACc1xR|ˢ870 L1>_|}c\C$_|p(/I*3$ 7 ,^؏c^fq1/} |Ckնc!Klքn?_dM5qi .Q݇)Ÿ)|]o&ҤNo(W6p6# %o"P)Xaa~L%e  G|~Em~2CN*K5MqS?i?kQ߂}2>wIbS8O '块ÏKQ[b@'_-WZkmNLu{yTRy8oZ+02s}ƀhcZ ?V6r6im*O]ßSóu^*¡o |x0?s |oNKx*s@xދsAt!zDwmb*}w&w;=_Ӿwm7 7{s*\ެ#Fp`nciKO ;1B#xlp<_C~ EA/Gvs VE1sάюu鬅ƬWJg?#U*+z)8fc}]J//+0n~\wd=+zcrV:<%7?__X=Wq ?i[YgꔫgFµA0'=\w1GekTt{̯nL_yg^m7u-]z'a߅u67W ٴg,>NI<c'52; 9#ϙGohg)V!;ka'cH:ǻ7wg9샛 ~֑g=&H<[ >g]Ufg!hL *%ULjLgZee&=rXJdgg6um6"MHHy bj̀gɻSYUyGjLuo+9A/ol; Ҋ+f!!nbﰲP-?߸F-@&s>eS綶fqk+yJ\}X+k?ϞS^#1r߳|i|Z_|#@wyZ#}̼(NVɻK,MEk-vWف/eU_Z3n/zV^]+0֏>y Sm;R~Ə确eX: /mH:%ϲy|c߾wl?Ƚ ㉦OC /I3d]&y^yeuRZ7g=wx#L߸ldEzgq6;8<ˋ&mg:/X~lBf476gF3; o3>wLJ cCG{ÚyIx͊!=GL 94/_CgAx6Ns)US[4g`S0966g`ǚ3O3ߛD ,w zV=kт;='o.mAق0-AZwKt;u궇+%̂+ zW|v+\q2UAV­[:" mE>Պ|V Z[2&kM>ٚ|С5`pk [&nC>(ֆ|P W6mې>oC> QAڶn=p õ0b[E>mʶmے/n%_mKnG֎|Q˹o;v5ڑ/N#_\mGݎ|֞|=Zۓ/^nOٞ|1=bI{䋓7ړ/k 'cwgIJk.py?p ױppy[p-3pmS5;fӣf !QGߔq- #H9dʑs*WR_]χbΆ{.~+Sgܝ=®[Jg[W/t6`Φ>lfV\ {%/z"6;w6oK\˝='lΣ[o;a΁~N@+oo:Jv6AŶu<u d|J &,z;I>Ïho'}W'{_ _VBa]7UnpܟnQouv ?;㚈1p‰=<x/'f;.SBx2e\p:eg\pMgdp&e<^&t15gn]L=%|;Q.[>Jiv_@?Nt;I K7/cw>~ö+[|ׂe KayҮ&yoym.],A skCq\,\\z}ñ<磾|ԗ\GNHٍ±,>Uاq7ܩxg Z>qlGǬ`mhp`%W#/¤$NSN 5ڇN47"hpu ȇ ;~|(#I*W|}E>TD>>{Ἂ|{~FySۣe U3+j=TsifǿH #?#ɞ?Ƭ~=h>s^rXn~! ñ~5a;8,64_rzi\2cpn؟uma7:TB8b_wd֋O`3Ŧ4'eo>>SRLJ*\kOtZM -K;YURࣸSV[㌽gIVWi OI[0?Tg)~ݧlSgxɆ*ґ8L{?ւ+R1 G)yix"׽={@0Jb=aD=M[#VT=M[gshgiw40vMOj/0oQ[3'8.ڎIwZQ*-hOjMqm_2nuƭEXƽd<pqX6UEb߇vbj֋kxگwx!?czp_>j?؋~8_p{Oom@k{|}ꀏq\ ~ӐܴM _g?!iSw9v>|^c 8eK2A.P| ~ Meyu?H` <~HsMwx91dS1dS1$Sqj܅1 <C8= 3^yq& <gcy;Nf]<!Uԭwr0wy_gqM4.ά`K m2@~8XM1n''FJզ >_9JWۏe8hP>Cc@tuYY&{'+;FXWG 18Za&O/7QvQE{+/z3,d&vo៭Gy#ɾx3G͑|F?/N̅Uxp=נDw=OvKmhG<7gO{)sLU[彸x'>́q.:8oyy-ׁ&mq׻}9OA`>xl{MP%w˼3|~Of=SPTx}1z8:qjzjǼۡ&ww+|߽v-1XW?sBO(/Pq5p*l"n^^^w{d/MwNSXù"[>u-G9ygY&g-b`?cY> xikeCwߊ2z}1Fπa8D~:d>nD]vRN׼{/_T%~r@NRvV[1FZϾ=_[HF~R:3`}ͼbE_>/HלauO7]D]ψ7 B [ v;*~V+oq>[Qm?AjuA_޹nɾI~/{xtt״3kڙ\\gR]zϤq8֫2Z;ޢ@t'~ cLϬ)Sa&3tDw|#+>,kL Y>ҸwRٝIVGcMh^>Zk߷qL ~m9´EҷfmKxG|81~6و7^:~`oyO3Ƒo붅(֟_Gpry'c~ƥy?7DS*?xMi<^M}?¿@0΄YpxO%=u'1DQ: {48FLr%!lsf^q*~r2@nJ]1_@t> ?c,T|^f sg|uY; t)8>e^>=)ݵpf}}/sK_1PuXt<ǙPٯU³QI`0gǯa~d^dt OyrNOl')Oϕ/.N퇜}y7 e=Q|,:֙J0uKuڳ>dx19^6Jqv{߾ Bt40?{S]].?2U{*Pz=VXC_>f>w:=QP ̭x=qgѼ0ųiVoSx'.8Y前wLP)ps<&NσmD~IT,㎃^2wx5^;ד'jRG)?R8ˢQudP8Wn2))kNxz\âUZy' 'd݈ t ʗ"׾uL?)iRh?XzR_,OxdiXqg;/ k~r78Wkg_b p/e>z<[8ld\2g xF]]G>rPޝs!/ 3{6&0s 9x&ߛ61;N4$a笝w8[$1)_,}q1A-wCGcR!!8(n8?`8}/#5fy=ǩϘ5xu٫y?vesVEnj/yϵGnEa:LCpyҍZ cw1.}4~_c1Kl%_/S}ߡY¶q='>'Mګqzs^|F!.`L_*| Lg(g'O[[?B8 /-oxIې_sc4}ۣ͙40Ʈ=PjT1w!%CLch,Th Q~ vCcbݹac}v;Gtߚ.§|j1/p5X35Kᱦݭ֞q:L(I~^۱c= LH$ cE e,gn˽5L-`%[o1`밢s1q06%qI4}üXYUzcq^#y F35[}w+[t\%~Bqʌq8zFvWqlǭǙoNpsJkK&odyҺIcpckh]ݿnwuq50o3%[Ox<1iA?"Ưٝ={)-Q sJǛ":XRn| )u.3)//"zBx/!zi.} XLHnb侖'gU*o9{h& x.0: l-,^{xwnn^#zziN<'~sz0MoW-99mRo6Hxz6>wRTmW/xs|fS8璥s eԌ1|/O!6>Q_F>3"2WxOgi}K-|_O%xv+gBdpEŬp=&^ Ƃpg:' \!i_|ן?~_@[mB;O|ٛDM4dH;zi u77ݑ|j"X/~o6^Z7< 2⺎c{&ϙƘ}U'w_ZM4~cx4 e_!Lh=OzDӳBDM2$d*+<@a[Y V''?m} ;sr52;TTT2jMqܟ7޲#c,hΙ$7xL oyy˧))UH2o:~8__Rcn eOl3frM ȕycWbWcglؐ2VK:%OӺD/QҾ!0o޻ 벬y +M mbj,p.+8io{`{SqeWm5Sg+f֌fqk>ScIsYôIY,It{rvs ozqmyXqFg{@~[CX 8.W,?bk">ڃ; c:= cR2al%Mxn:QN[>M1f%/Y6b8权@Ca]7D?Vk-\^cEK,ü+x>)>Ì mRV~R8l/<[6) WH)Za]|5Ռ2/UX|uB&T3iù1~j\3K$O7׌*\p_߅MQ9'Ť{N\1kPY[;ÿs͹Ul^6e޶)Ϲ:koi G3Xf0q2WsYGh:|<=۬eW`?ėu6ysY_ /TnGN_^|x^la%4TzɼKDybՌo[lYuԶ&@Ŧ1~'^Ƈi.! rQ H_SxSw# $0V uoz3/i 261ʘ0 믰RWEǩgK U՞`v-k@JewT[fC/*Ra+jWMg;;==|w[}szۨX bńA`qAbz^F>/p7)2e'i86r&| )cLL[xUmCE}8w \?-3_Z8m9SҐj~ ~9yv;;,xyx/]V#?|7S̾ ljϠt!= m,?fZ:,ƧT4<ؕ~iWN@KҬTWeQyGV';]j>|mij/P/5nN}/W{Tt83⍮w>d}٢U, g> VYR=߯8 PY[iC ƟM_I]߱"E"ONʌr3 u\T3Ubö_fg`=e|IAjUG0> \}s|žwT_11/ K᳾sE͞C{ 2C]y H>'?I?rxȼ+9JB<)Y5|wIf#edMXCҞ dz0/uү^ 97RoMk$p_-fyZ]m' 3Ziٗ'ܭ߹&@ȼp5>̕<(d^vsJR=ܖ\WyU:Ux}~qgS㳦*|S61w0kLw'bfuWlmx{kg4c\W>9;[:1okM?id^!e1?&jVS3%יVkotx%_o^OBx_,"X~|_ cf+G %:Ә2zҭe>Mz:ǵzڧ\g=-5[O;+⚳q>nq\8((("{y ۆ7<-J N;l7sE{>th=@ Gorls$N67Bu9GU1ek;/+AV1ooDVq ^3R:)<¤-x쏅dCgJDgIdְo%{YcJaV `c ́Sx^_cݲ;On5sdf,iE6s&yrs8t?~/ l4/cKeϊ2dUC>D]v B迍〇v2Eȕ:4j9,|$om;7=p;Ip1xb1\l7+̧c9 XǎYu S3|x;=ee|1' >8S2r8p^s უ+ tbƁ;Lyg`s K|J wPA¸ ,/0w?g~C}8vjx28Kv'';G8~rvxzټAtYLƙfe|qMw|S>GAU7yU*T_9?$&ί;̙ek; c=ey;=e)Ŭ[b'\ﯪz6#?2^PG =bOe>1>zPw{޻=T]}/{>>i/WE\ֱWw1Ķ̻|6,6=̷o3ƶ1l3v̷K\jݸ⬗US6Z+W oRWK=O&tkV45+4P6*VA OUx>cK#wKCS{I^5#7kg ߲ɴD5jS re^-}lcӽnpןL^Q vM>Z*C OUx[~S.HZ0W@^q6;\a[@/S],{aloc:nrz g(ݵ Wl_B8[NU~J7 ӼaS~#0.o0o`z0/Y>}ͱ yÔJ7DldHM*~Tܯ~y?ǁ>q-? 7}^qb ?b2xi <IX?[ZG=f{8 <~9\Jl?m;;| W~?gyvj dl&o}&>961|7!48<4q1UO0U=@{/k̳P@@>̙bws%yw:ΥV8k|ےC`D*0Psii[:)KU=:jMSl36rqՓP=IuIbﳌkYDzsc<}u=;u?yEIgXc/tﮂW6ĉڨ&gds 7qn`9 T4l3wx2}ICB\3@<{|1Lz]ј=*O+^2=❭nE /V5v۫p4řd+c+-k[j|m 7_YœR`ϒMLi^|?^I!^u];ND^V֫G;oC*0U_/`sյi/kXRg?f|U u~d<:s|O!P/q|BeWCiSU$i伸jTC+,hGRpҸqk{ìZ 'gNgV`G?es{F9AyϨOVgKs[dc[b/'bWSfLi/CYo;Oi{[ _'NEqp3~HXIգ95L~$MINFҞ~I5˹2wڱ|oq|2y;7ϲlN5{x4~^'Rcc~]_F\=?D>CO#k~Zv^R6G*-~1XaGs#);4PvNffogVw0. 8Qe -{qBLA8JwT2r4 _-\|=;k:i8Mkc^Yc-XlĘi 嚴Ns:ǡdz5y/Qsyq| }*pǬ;mix}:|dBN(}j򢭂~03`yl} ]O2<Öͬ`7p52;œ4)!`/8żpo.q&b;Ztiy);~'zXr;9wOŇr(ߣ=Yї|e޿oM:gS6vi8[{[<.o{tNtyjڵ,ZZvmE<.f~"t=V}<ޓuQ>IU:t/Jg]'hOSX31gH nNQ7|t1lQ>/yU5ihwk}uMYDͽN&S3ٯ'{ccvy3θg ag{{Qw\x9 Oq r̉Wr{4xzSq }i򐱡ԍ/OFGy ߁k+G ~ʳ}޿6 e,geHާ?Pq#obHJ*ۀfw]E#-9gpy6CӘ1m0p;px}(;3>w&d;Ȏ9`xFH#"lCwxw%1x܃DyqϡJ F'OOeV{T>m#}#/W|`{d.#ٗzU\zFqjļɊיyS-vyz&2yoyӮbS6.Ԯ53̟`+|ykwoT>q~HS _Lf=W+ㇴG&➽2#|x83~?iE-kWCM3q 0Ë&kW)/>4 x~|rs|}|.z$|1+UT% /꾛>*g):ec +"3FoQ81C_3cm8c\! g]kϘS(s|uO~DMG T*fҀ|0qFE@%?z]oXm5koS7{k> $IZ0ml mx6sOq"?68džOStU?6qMT#~p}(ߒP2zk*g Ozg` * o{*| _·C*@') nN?Q]lƭu?³Q.x{xZll1,1QA7Qh2H6n΢pa} 7Se*H P3(v W­*K _VNy WQ}b)|L C]18¥p*58XW)|DOMğGamw(|Z; u'(P RŸ+|K ' SmDW+OS - Π Vz(Ÿ*ž/T9*_Z wWxk>WO}|p9[*W RM/)‰RuC 7R#V¿*}L V­$*C QapfK*\Ov Vx;~O[ G~mp+*X OUx S~cpA)N1 Q—CcUP Pxޫ)Rɾ38>[+#^^)URq 7W£&TTN~Q Qc.O)t~T­~UU TWMțMV•np/_Ux[>9T±?SR RCZ*|I ?V8-n+\U Tx^.*|Nopm3*\H 7SCvV*1?I WTCZ*7~p_ έpik)I OTx»~G/KDw NpQ)B OQx‡MU8WQC^ U).pm;(US8p'(Sw>g^-c^d|nf?g8!=MeגwNσ=d=_$5Sf* =g!^^-Nz[  NZ2ox'cM4w^wHA NĻ :ɠ6L\'te4k&1IRq&s&"ֽ~FX>Ӻ'eMu7[ŏ6OXozsAo`q{e.oMte=HӁGp3fW3W 7m[{W 75W}zzq|zՊ,"Zz2meeFaoU囔O٫Ƽ-No RN"廬sTlSA Nc3떂|)_Wr 4nd,jtGQۇg>U'lumz_6f)tf0;飩(M.} ՑIta;>m \ۤ[~..F6߮Kۭy9P'^wǰ_Zg8; S8[[M05/E9rIm XztC|M}36c eTxm3?Ux_ɸ:m=)17 ;?>1}T<b̜?,Һs !׭{QY5M%%s-юnݒzՅAXt?mk-a_N'<,O VXg+x{2 `ĔC/@}›nqmZ2O $5?OGe]x;V8?V"NVH^qs~[y#OJ8ya<0qh?70MupL'gJ~%ibIEρ>me9!֔B% `SA"*\/y[ 8]_/=ޒ;G> |>~\ܔ["WHgX/ eY^ ݫla?;ɉWx1Yc̓?L>[W?ێ߯XXˉL?HKezR62mt4v)IYC5;\[cבpM}*|@ѳ YV>4'^ׁ6Ff y=*7!z`|8X"'.>'g7d7ofp =pZ0.Yғ/:NaΏ 2|_ܿMS_zo)/I/蟡y'9-==p#gΌ_L>bKf!Z9WC(8/X bDx#9 Jygپ7f<Յ~J>Kϳ-n6#lքla)8w?`'2Իz/[G|Q!ןG~º跱svpԧ%=1ַw'?/Ko0:yKC1ֽu/Trddt.dng+o`/&*L#MYôe!x]7.|gse¯գ]vۣ>}HތTevˌnpR\?X'S&ҩ=2Q6-YG2Qu3=O;T쮭ʙVdɆ ~-`T_&l㛛9ҿ^D<{7uY KjG12?:9^|,lI`?Ut40cph? j ::EpI~gZ>m vNq{L^;w9>O֑uJ4QМ5_tXtY Pޟ,ts*rbirbU9W|_DDlq;AocDsYxGڀO9+wƐS<o@f%И,,H2f 4s+|)g^c^fT)KPyGȘ S/Yk6vpt<^/1 731F^oLs koAL`wx{Kk0"i|>sF:/C}S#'p4nvZx+'7IƴRƁ9x-%od|NG/|JHwM㎃Iᆬt sX' ڷ\8/:NN?0X62\ÝO7e5x]s|ϼ嶥Xi^E}Y'{$gd.L-p8 fnef9=)I'L cc\e#PϤGX<_\5^ 8"e=~TnV)7%z]>yΚ9uӇ98sӸBn<='SC>l-wwL1abNW,a8n5kR=o~|fT|vh>3l>sGuj2Ľz~? wDI^OQd;Ӱ av>{;{ߤ,ק\O\ #YxCclp7?v( ,ǝ: 7:#tuoNn$2?u'2w|Η| c;~gni~Gc~lWwT@!'ٻp4?8,! $Y?*Sw[=\I LڽSc ^#vMKxJC(TrxQ[9j4GR樓 i>g}}I[F4*/ڑA`Xn8([mxߞ|P6Q XhC7’~L/gs.1j/eEy%۔y뛼b;.yχx򰼼T,hX#WP78[]3/tY:_*Hk,ܿ9 I >)_ OJhLT +l L`-<G.cyC|v }y-Maھb^sm)L (+z3#d^[^.L>V}y7<A>qղAxF/eyg; ]ݵk,^?\e,mcNNf(B-Z$x:~YyyG幎a`*<3C-?\aYQnH0odfy}4_ awJ&s]ڎk'1E TY>0U?Mxd,n7pߏY|}fnG1Un wrBlϲ b+o"yI:5_dVBEV<ޏN9{?`%*Ap?G#2(+u o36J0oՎ/c8/ [ F tٶeC{毳"<)۠| dw(Jow|[aގ OC\a O}⤷r*~Y.7g{Z֋xw%?\W_>v7u{>[ Kڼ_:N&Y'lwYOcOpLpO /KUe5˧'E8+P[d%#m7oop ot'{Wxo(,~:[Vtv5 R(Y؋/?y ž'y>SoU8­c,XRFY!ȢKqI:ҖVV>yɒfl^C6%͸r a~q^[ ^W&^Drt-IkKA`H-©A喙 J>9Wk*S,ܼczyITAx׿` Ǫkn3/92S90Xsj)ROS]IYg 7Zq<+*|wL+ yٕl?f^N},0VejḇjZ v*\La]jCxN#͓0eyQes볲*k_\Kh>K|7._*Ed؇/eM4DT/cLw~# )Ix/RWM̫J Pxla^bpnǼ*ȓBj"̯a^MUw9Rh15,73 ^iOk+]1 fK)$|rrhuw!~S-ƨ+Vvy^͹0C8ɀ5g3Ҁ/p{a_|lRv7\#&GͳL"! 'EqiM8gTқY nYOr25+tGpՀ֣4qh[ ij7c׭< dwz[SCnlzgT?qQvU0.NUg^*_*nfq,>1/޸&]1csiǜH~ϵQE#{T-\+37#vgAt 5?>eT =U! =,ɼ% XP c|&8Aa)[.Ze%~~p*^K^ɻGw-=W?dޗJ៫V֗j;ZWt0/hc_ 2;W]F)S9_Bm{W3e#V3cQty{V1՞lh> iT7PfZ1h8m_L#3IkhŴҌf<թ>>0UKS9RX[k7md {C\xJWn"9oxs9¿{['4c5X(%㾝Z5O^i|i{+,]Yiۡ}1>8l^oʾlSK+44>A [@V`YeOW;L+2ikpZf٧V`i祝d0}Z_μ^e#:;?ē6᱐|ҵ b<&gTmMܢN>OaXR "O asvn/O0uIޤ?̼)k{BG5yw;EƟ1K4.YIYfu/j!ڍuh6y-< rLEs _7G%@eRX49qrg%E ),i]״aްq®2o{?6|V|ZG9^PX# %NӃ^SBO'NƼi 'eye aGOֈ!_4RqW8雙x43`ӲpT{҇oU+ekh;Jp>G 5gyLdmQ%k]^s&*>\_4gTmK%Uw o.3/yzs$?j׮53aB>r›-N> Zţ^(enN-aJ Ϗ+igkS_:_xR>#_d,~俏WIAx=/6a8_ė)ZՖ ˫/LO b /Q SxWx=ᘞwX0\.2œ| (g)_73?ZJ2)eӒқ[~ɖTZA|Y($#x+O.9hocх uaF S)Z3pN?L6U5EZߪ}(oZz& mM~YJSljq/ =sLO{~WG1ۘ܌/›+~*!xϘ^J秽3o0C Ǵ58enp7*D E5E“3yڙR* o¹{բ3˧YH#7x7Xv)|1.)ڙ[0#G>1&kolh1NSKqx^ vmopgR{拧 sE&8mJòq7[9Uqu,uփΧ,_kL?3/J%-þv\Ho՟WY2J y-żV!:,:o{ CXoWz zYoaiF_,9a"ͼ!x˘;d/o1~DAF);Q%;jՑ=5omeJL71ܭю/u ,ou3vDms8ޗ\{x };kd+E뀓t2mNzhRo9=U!<)}7=OwӲӓu8A?ucQ,_8~)7Y'SR0~[_=T;:u u?\ƞgXE'Dadg{fLc"iY3=mױ)ہ OPxާi/u6eqٌwu1默* o۫^*>K`}y wg1/ *zS][h.mxGa%jy]mȳg.v޴Y3n xζ Eg].9{wk%0tlVpYluNO$|0Eeo 1 o^c_+nDOJvjsx'GךlYV:؎Wkc;[͈Ǧ}~ DCّgh'u[yV*ۃ뼧yۦA=r2~QˬɻKo^-݋we|xG2ޙuiAm>^rw™^63~W gmp *NFCY9X^9XޞSU~=_ckxG(t/d,ϑh*lk73O3)sY+P,s=T~?n{)fcO+凲uKZvCײs׳s7J=eϭOggo3~tlz]*dKNUOBJh%˓OIԗgGF Xf 3qUhkN}HcHsҁti`|/t x|1G(>j5w]k@ZGyt6s*G R89ήp6*O* WVpy)\V2 V%~Q `Y/].~OVc g!>q0-Qq/μ_?.W[kQW;30STvXvvD{0W׿ Uh%@30-3+!i&0c bF;4 s0_U |Wo采Пא G=x6KYɼA̋xGFˊ{"3Xqbh[OQyGDLyX^K e觔)bWiǮx9/gd wi;+\ TKo+G*7I^%L?sұ|%{3yY>e&U~koo"~9<5tf7(ҫ/o W>Fi^|;WG68VJqTA[ymobVumMrtagn{1 ?}½3E$8_␺5P` 7PA/>I2ƹNQ3G)yyoc[Ϭ2&x/C%h'/c ལl$S9%l,N7OnUX6K_kl~~qk3|I0#Mdg,Su8^1w1Xs/p V\`Gbƙ g|ڞXN2e՘O+|&^E.c6\qN:δT*Y1vh_Fy?]ٗ~ƹ8sfams^xiV2uY)|˼? ,"Os{=P^qOB\Kg aCˮ嬑/O0yyC/c[o_14]I3xa9ޤ op?')b|`}y9]xC~ooCr~/舾%@zɚ0S<#ePKx^4!ANqsNct!n=gQpρO3/S{e ?>#j"(wY {9@7q^F#qD'b^vq?&aaW~Ǽ!xDWxy'$SM2x$SGN2$SdIdSrL6\egɦM6>_8oN6fY2SLL1>kϦ|=bgWé&_#|jTSMO334Y:4Lʾf5ٌàQ{#_ Rxo)3 .p-+Z~p? c~7U5zI_y0wOxTZ2p; Luә7Dyn_e+TwF%MWf1ψGj. +J`i 7zB?c)}}M C+p32v>sA}Jᯞ&/E!| s$]/me!J_:y+B/]ɼU;B8:'QKW5vr(?d՜Ǽ_\:Hwe΂OuHF\w"3u{nເg8tuh>pD|N«ehZ :Yztig[9{zSwAK=zNLc%Zo-4ms goqv(7pυ:vݹ[9>wc ߨ.w|vaQz_w¶m{=n"J:oG9"󧪃)Ÿ**A; RKR-2m—q}B&<=Ћ]Lf$6VSݖK (/OrQ֏|砽Aj,0}zݶ%cwby%2:ކڤ,t]qZWA8~ xbߙ²1,Oϴ3/ 1w-1eY,imI]_0/3ENO0?+-1dْ';qlfyNނp'<KyyywI,%y&SX;co$2W4gYft )\qEeYfds08gU0eӕ)g(8{$SCUSӄe)_2&oIm }KͬWGţ'X>nzVݵǙ@uhL_쎛XJdj3?_'_^妮<=uקX Z.WcU#~zݟ#J*?94XUߤ^K=YA,ȸ D3ƽ@V9,w#L2xw)WC63>_wW|3k]kj} A;Ҭ}_Ҍ`;37 \7^IcWXٟ&'{ +wUS2j v`x6a9YD.*ż}y+Btȡ5w(D\=yoYc.I{"mzQv{53kmwT Z`;OE~ǭx|ͼm)k .0bak-Rmik鬮kimy%\ m ^x о_kvm_㬋t3@Hl+J#[ 6_G6{"LpQ9^;gq6s) >R㒯#We>jễNҙ/b=.NqƟ +k0x= *i;и4.~Qw }:ԼJ+{%h&#H;(?e;_Z:[K}&CnDc} S7Ɇ@_K!>@SyW.E6"_U~eﭔ=߆8zn0H%m^chi72s_W)|Qso'(|G3F;%:xt'y7 þVXE>x}z/ p47L.넇ΨjeA3Y!l }%=L}Hr_|/|,Թۀ'fe1R[,w)7QmE7Ѹ&f61KCMlp݂*<$\1/6_Qu+S{8Oo3/Msv<lWsUDe`N2쳿gfYLgy6Y6fBԿvLG8a79+~}3ѼWI{) 6Snp~4q'BqBqBqBqBqvm5'д9Ƕgi8qJqdJqJqJ⪽m첕Apbp,] Wlw\k~W<3[ }A|:gmYp^8G~80?vy&9(8dA;ޚ{\W<g 6x.ed=pumno=0){NL N9ƒ" ˀ8b'Hg#EËDn]|ϛKk.p~ +̸mc@/'iOJ~Wlw 2k%Sݬ k߉ƒW%DWF94 [u@w#+~Ӊc5l{$י_JoxzFuQIxY͌ w4&cWUd(ymR s;)V}:&sÝ>keI?hqnԓi-v3íxq;i̷dۋuғY? wc)FYsችm #2oLk_]Xvz[M^CiA9o3X?2ހte9Ry RFS7'O9̛U}_^L73@>/]\evl \w|57琩 x9.ox܏~MaCeuoDMhW~mqIYf<< 0o;~m?o1Y{ /p='Z4`ipyc˂\:~3yoΩ椟2S7WZ/ۼXH_-JgcQ]l`:&&:$h7f 'kpwk{(ő9>Ky_ʼ[(O'p^[5zI]wT=|é}7՚?rӯRXVx/ľAC#boW)zw,=סo s/Wx;>  'op,«(\Wa:O}Ҍy!=^mOWx;FKYڃmoX8h=nb;S#'i ņ8!-0/yVDmD؁~~OCڂK'F^qdC0s*Ϫ;7S 32NG3ެ`o)}UO˶'f~NKdҷN^e{y]!Oރ&߁#APc<2,/d\1\6pڰ8p{),JϕGl^d^8k(z tRWG7voV&|6KQxxk'8D ˠpCF? a,)ٕIEcXfs:] [~vA?X_w^=7c܌DaEZ vXjHߢZKӿ/Yo_NWX;=Oo_eˏg7-`^/?ǧ_*{8o'>p <%{ob=A߿aIg%wWX]Lc85LIn(ȽKh8+:z^ojLf=#m3=5q<D m?F;G}3@c-c*Hͷn, v->VqMkO±۪4cIi[tyO?qH9U~{UgnH]N]g_Q /TXe =kRZ@zzS1q 7 zMA |Cxo~Nƿxq\yAaP˽#ݘ Ǧf1q49w wg9,{0/^"xy2"4f_Kt%j"_DģX j m,ifA1ˇ4 \~'@ra+p8H?6eKU#^Ƚ➿Q/KHOC+|f%Q?l@} ԇ@u딅v*L :)`sY^AZAkBJ?yi_~8V(8]F6?sxP;=|`^VK͛lAx?3Eu.R6qK f7gGQyz 2 S+;SO~@kvkv‹!z etsCԦ'^c55㏲ :FhC/n3C_i Kܾ֝T8a*5)D 9fp%)?AKqĽcA_ 8{ 僸XM"i<[jɚ/0v`t`]8*n#_"y х:ыqK#X,Fq{aSŷn(/kYWkg gP$` d]9*tt{Q9xQ|p9>q*>#9ˤE9uFޗ2^*Dz[yX7%ʍ9%`c]O9I*ϱ[f}:ҟ Si/a7Ig ,}%fSp>e]8*Y/Q$]`}Yl/jb]S9*C45c]9E*//gw[w/uppxplTtpV.o wpˌRN%8 ѡݖ{hOyJUƳI3hy#:4k8}eI p8XlmpѶ;ٽdτ[+q`t}g'jp mYp_ݩ8MQ1Fֻҏ%cꜸl7mMorp+\N Q¥.p +\L Q….p+O 7Vu)P Wupmk|Q5k(\]j WUU(+ Woz7XI (*:k5}ʼV;4եtq0.TwlꞦ='@'G#.21/%P<9y Z[ⲑ3/18&;X34/'WOm nw`T} cKt37h?NB 7P1cɯhsQjK5#^F,6de ؿ`R܇y7RXsW4K|C[d}/7"z{d,կ&/򯋦}ѴU.%Ӧdڋ!L0/%S.\2u%SO2]6eeS>^6e˦Ml&\6ysg+F;4bcϯ;bp늱C;}aUc W]5vfPCkï;,fp׍ \7vh~^7v^W8 * ^)n'*ep!+ޤ0­=/;8z~nXˎ? dMVv%>{Q~{ݿDz*_>OyA8kWOl`Ai?0KCyrEtSEv 6;y#;Y㏥,7H|Nֳ+,weCj<}/;(zd\c;f7wxNoe}uoS$.̺q.aܷ1X{\W"į]ړ{5^5^5gKΐGL||,F]ifC|ȄU*讹B>F Ea~K9k9y8[vwUNӊi {QЪ3?? 4{3sZ/ؽ9'>,|! =3e]Vʷʼ;eH“KN\KN{r Y%^▦U0ܶ/WwsCyXG2]W4%H}v>x8{݃ɽzaMm[C167GoV58ʻ 5+Z~_A!©d{:_Ex g<x$-epJ(?xAx+,屝H 9Y_7ۊ$=1K8˙?>w=E8n5,VkZA<4wc^mpdɠJf,׳sTGtjp\ tǻtGQ=(-szu#i8J[/ՔuԴTm$^Wipn)2N4Rx7[Pڞrxy!'J&c^Jt9a0K^=ߧRV2/w+rOĸ~bڠ zb$ln|9XuUx;?[A0=U]xx=@?dE>52isx\΢['jðxyjYRG^l?S})U:6@;<+3p;{4zjdJO>_;88al);3>; 0TKm t>=rסLO1Sskf}ffƧfO>b#"6hOr|B̜ɼv 3!f}`+ø2vh?ܓV.q ຀7}V8nϪb/6ƽ c;L`P_c0vn9П1,ȫ8(8-;Qc2zQqX2`϶_.n1 C(_ruqD8PCyL9qT9竖Tf{(Γ/8c̃rָGX$=2 p{ҋGayn2SXƘxY<2x-1z&W|  Аxv~y)(-Tᥲ}G)Y~{>sbe^bYxDZw$nCC=pCqL8aNlOӷ'r8q+ZA~<2!STYjy- a;n8)$]Y6vPBͻ0n5 G|%/iI"R_+}f] l p0.Prv#>R\>#'16Yd ntM GZ_WU ϲⰂ'7 gҹP?%+ s3?+%H͝KD_9/cwhq:[a`zK),q4ϭ Vq;+< rw߄3JWƸoZyp_ rW5ICy=;Yw O쓗_1[2>?^#mi{A[8lw ~ ;v/1c=e$aܓ#Ҕ⑆SCᖑ&-b,{EyA7oսSQx:cҿC+GgI8nK;n{xU">:eV#OlSgTxc&4)*In0`Ya{{(,YlaGֺWdߜ1NR( dұ[aI"=˼0afNv'#<$pnq$4j'4*ɟ&s/Qw$ C{쟺O?-'2qܴ=f -eqeW(i^KD(mP :2 Ȥcz4)6̍J޵K?;o:Ȕy)/)Tn|ʽ}ꭘ_/JHr$Qm䟴OIJJZʞFgQ'6)cR^2_1q+/~!$2I,)qyn`go8[ 0eJ;Ht$!'%yidw4':WKF󸏓]ekuúc] I) %~r{X%貀3̝?BonG¥e_yqjʟ*'9Ἶ^0\}Eut8:N{M;pfxonDG%FȻ%tKhՃ&ිGe51NbꭼNV(9e/SV6(dmxSr,lOr*-'Xy*n]鿗̝5kc#[`~ #[`zpm/@Aヸ?} u/iJ7#Rmş}~,ߟ+0VH4%d -Cؿ o3᧼cyQc]RRGΕn B.u'1%-'j&J{cSF5&NpzWlITa8 2Wճk-TݬpG>ˏ`qF9Rko/S wWo(GŔώe;?#&)U㘾dwTěKvj0+mdzq? ,~TT^OZQzVc/Vu ݻCII1mrXtI@ΚH<:59i߱]/%kM9%Wtwに߾r7D /"5~UDD&{a$c|OԔ7_ _+6|Lo˭S '2o# zB'Fƥ*ݷ]"' 5Z+@V:8Oo~O)qO Ub+(e mN|&N"0Eŷ )"@.5HzhyQy[2SIۛr*l;y7Q`28Κɼ|}ydڌfl߳)JQ [aۼ+Bh^ގ(8k (/]Yw}gOeڿ]5`>7po \k;%Ds!\x+ϖt|k-/ 0oix] ,w/rg2_qqu?œ"XF5,W)H~/*Ea7e+8h^OT1j4>HNg=_֒g.^xyv\t/:nWfz\pD.Afk(F*Z_>mO5^#'G/46bf*sce:b{|Qz6y֭6MqG73~f}q`,Kϸ5en1lU+gR,u-;^ WxcuV8Ks1 |q0>^ˤ FTVfJo|Y 7KǗGy|mBƘGu$<;&8 QTpǬ5ws"8׏1F:2ǻ(/|a[Ko>YM^Q {)nw&]`v(a,լ" ssB<yq1Nld4✂ m-dYgq;Q+ &kE}א1iMkαiƢ8oH :zE qnt>6{t1l(X҉qð? rxLgWd{}qI`IЃԘZ;KDލi}E tQx~Ow~_ e}l6g,dD/c,ݾ kSo}q?Gs}:n0T&t&fp~f3m$<9 bـf3[ ʅ!L_;NqxD3Mv-]Tiv7iw8bc:gqgӁr';rtg Dx&Ar+1}ؾAm֓o\$Wr)kz*p a촊q6?9-z(?^GfT9 PNVy%^?Aưgv!YGύ pn4e~p\[Av(@)ˏMs^QЛ=iNgb."/zgrؚ'Bmo j>Uu5c0U>0˝ڻh+'`^妲(aA\יQB-" H_+7yW-v۠q̻nEg;[̿i/̻ }AO6+\0cLs7ǵI{s*rX=y&{D?+n,'uu7aEirU8φI<˼Wۛ# 'e=;r"JẌqoJ|>Gx%u$8PYl:K6~_cqC@}̲8Ccvh8ޡS>LjN\:=4suzhc_G{iK7Wo~3*dlͯp)\Pa_< k4 )UpUXyW~gQx2[)s^x: _WW%J1Z<Iw$Oxř̲1[ ϒ̫}k;NUQ<;w-̫=NQ?we?,<rG8?>gS7eqE.>+忿mv?pǽ>mZxp"Z+xq;Zgys=ی>Qyxi?ݘ]ú%FO9{Y)>1<|.`=U8!h _pq '*ʼ-_#[%.H * U)`|I94rиÆ}N[ C `EO U(§_BIщYoN3$Z  ;&_xF?G?}B,8O ΞMNKb~w`[0p;1t8N  Mc9΋wn<ܧgEtzv˿xNE .p[G+‡~poq ]=Sy' S@-[\5Ngc:f)9,ێ.TzlJe9X8 X;7Vp_:&[ {IRUz2r?2 uY9vp^9S L#E1ҿF_rt5cZߨ9%763GE^00D5=a g+$|N |~R)c=>O^P,7lny1L hCJ^6>#\cۘwY\uwó Fw|_x?Vx)c@/\T OSxSDV]1>, z5;T]0i9Iq埃ftqF]Cᖌw|“^60X?.k?<-nx0rs%-As{l"kԲ-O_`<1cv5(~o! OVXÑ3k"Oe#o떅OC <+Wڛ0+x6?B_d'ʴ,奍pDϊ[$$_IKkS-m)1]2?{2FJs%1/ P;%QὊsYG%MHRʬ{G3_`ҋO+xf~>ˮ—yC,x`?-wYU/DR8Θ¼"*0>8^9nU.\ H9SecC),W.D,*ai]JV|zicץ(mqJc,eLJ1-c]Ƅ; 2&ܿʘpeM˚pk5Uք; wc'G ')gp!+“Y= Kv*5?X -Orm_ g׫,,'\ydױ|+S5HZձX{H̼;p1[zLu'coB fӃodf`M$ƽ:} 5q?0;+PV{"*,̨p&ܺͻ1S -q5>^67p\tn`\&G<'ƴc"*;@|ĕ?j3#׮dl!Nhҋǫ1 1qFw|@/e e%wN.ITOUq%MEU&^lkSۅuL/W fV+f"6uئMzW+ cSa+⮯F𷣁Y82Ӏe=j*g!>ֈN5pp<FzO,xWng} 8=sߴWy̸a pNƝ"cܧ\#8ю1> |)O r`7xg*xg粒=Њ~WcnF7$>UU)zwᗽ 8ԯgL~y(-ULθNeS0O /粰\ fvc?i f2]ɿ%Ia-m﮹ۘ;W1gcwuC3yqifuQU{#{(U}ݫ;" xQz^ߠz^̿uœvux_%;l⏼MZv{WI_)oR4(I{3%a|Lн&bF~UoxWoW[!ڲ qlW޽isr}>(;%5𑶴7n['һOL [.X>;44iƈ_jdv*|92E[(>5x?1Vح8{aCwȾډ|ᵙ ǵ!sX1xks*X_u ].79:E \ 3-[9ˡĥ3{;a,1=pg 'ωY5¹a@KU lxqo qGUc Y6lש*eWUم<*܎>\x"q 8swOЮ!t~[;fDRT䨇=`X05n|?ySi *=Қ$kXUo`+Sxy}a}FepSիpY]_{yڑ92meEy̸3sUGt[ScFזS~iĶeJu 13ug{i_c{=cvG}buK/ҎƱewܻz pzN׎ȧ쎜5kG}dvQz -8[;Jo (il8W;Jc;yQ{׎5pvLQl*aL9 k Cr(yWxwMrur"'tq4Mg˱vIa->>wӚÃk] r5 ewFI)GUn~7Yq5U<875V7d6 >1܏A{Aj LkMU6ަ[}2;i» 1w -ƣ AyRxKS&6KƻbXYc8oI8>'!/X[ffޥ焏e^C+N̼늇k?aM+GÆ n[i1!w~2pns, 6侱wyCGV57}L"f<BakwK8F,Z#s+<و1B/ӷ<̤qLXI&OݽW?/`⏼h?ӞҢue ݳl m^Zg< =JwC9*n+sM9W)@˔ XO6w=g*?+|%R~ MToQ-#OLJݘ؂,rۛtm wőxSc3wklST|*]3Qczڄ☨ 8:*myEW7j$IcOMLܦ+}8C\NJMA|ɳ}>6tO4rq; 7o s ܚ{:wt]ؿp0[aXa~,Iv=gZi`% F+_)iOa;D73z) ?4i<:Ny yo+#l`yك[yq*~WGZm.ms[p7(mW?W2y!ytnv'BZfi}o+ey]ބ׀6 Z;_  [ka'6e}AkSvVxoSX!Oyy3!M'oc2_| MjSgs}UJz)cf$\3SmLY{pCۋVUKѷ<3u՘jWZyI;/~p.29\gų zS2Vg ~]pw+<1{Q~pv}]vUxP{IkƸ'fܵ^q7i:)2֔#WWOzW P? }c(wscMxޮYux:2h0*+/wo'#r3UEϜ|Ty7@Ό;;4O`'PrB]+U\vv0S}|d,~sܬ{F2s| _~q{b}ثpUjWwQ =1{Os=Nȏc^B+% qP疎4odo{\{5Y.GY.KMⒼ;NKk,>טRp^f dڻq7p읜ϼhY]u}-|Gwkaߡwkx.uxGx|zhN"}8>q|"X_"D7^Mt'}íra+9Fችu'Xn˅XrR3|)_ѝu`Cv+8kdՠ}}ǧQ?N|ʸd& 5w)yw0=W])O ,/rGaSb8jSFp/KWPc-J~ř?/YΫ겞Y'XyO+ʫ}0Ƙ^ķ -q԰_ؼXX}l[߃ږ%[/ļ[K84zoOE5ː=~qgޔ"8LZqs%>ݙao~Cp /sfл4x#~5(aIqƚ'gЗ1!̧+ܓq!sDob)I#߾sV7f=eƟԑ@1K6֤Qa*[럖`rzRg */&'ş/i/m /, ^."uK/? `묰Kx:<0O]ew>Q8Io6aW '2ޜ}Ѓ--Yq`4<ҬMclo&F9l` ~hMT=GciJm8_/xp> Ѓx6ڎ2-c  8`^; mpov}e2g\H;8o;e. } ܥG_|:[Vkfoi>D9O/%xcbyT{=&8|=&8LߙXs}';?a$"];*[? 2bLn2ZfЌ]Fkh?_K̘M9eRߜ>\(ČaSxa%F[p3 egI|G;{v l][{'_ePoStI]Aó~X.t|zuTI>wϳ{oI(7#ƴ\c!xFCz[RGIm~=d0ֻ;d#|&oc?\)V7%k[yƲP{?jZOFmފ׋mߝ2Z'Yo̼|W)O0΢Dh/si/߲O&|&OzS~s :zܕym4 o<9QPo*R |Y O sp-<p0=i=16[/-XZdXЁ[vf=ǫ&SI8p|weS-{?Ku@cnf6s~b ".}I-xb Zс~wo *5F1rBW+|5H2{@>)y?@pq?CJ1ɦ8Y"zɨ0ɸ㶃mc3sT]C._rw᮹G_t0b==֋qߩ,fwl&;Nh]0s}?} {8>p[a,IE8_b@( 50gR_s2Œ1neYRAfR w*qh߁tFX~0g !QB>y-< aŃ:e{)L &νA.4t0f0C%[5Θ QxsHaGуBMRT+QaqFCa0~ϓg*}-wϹ}RƸciwK*xlk[w ;4Ճ)#Ƶ`.7_a| ϳ<]t{:q9 uZd[:K.40|:Ƒ6 )+bw;i/> Ϧ pw};!ߡNܣ%;fٷϼjb?O;_kJywVJ}UMmCn4ܝCMy(IRg(I 6Pjߣ6){4f^_[tuն\@H3 աGqkK{{4\ټWVc_x(w7c]+r^;7whƸ_wZ~8 ߅9;P2Tq6n? x/m0EAƞOAa?_vr1̴M.nҀO)wҗwd^xGڼ 2~x>k.,;`+xAǺa93g{e,{}:_}Kє){=Sw4ħZ/:u[m8եVé.Nuhp+SSjx^O|.Fanu H3v4mVNgp83yX>ΔL7Sn aru /Gw̅u x n\t;W("FP>dg!yFИ]x3.N3nl6A~a}\Ҭ}>.u4ʥ8Oj8;R8s*N8Y1>~]ˆuawXg̊eaG4JPʕAζr gg濪e3C?dڹy51nEUCt;G |qwp.7A*,G#2@P߰1F;vS#(u1PP];xR&./s%j1b !7dG5z~3FL忏JRFQTV:t:GQ{y[s^>xlj'dTmpcg38c](N}gcD)~bUNx2e<:b{7ƌ(,ᢾZ~l̼ 3Haw&;y=UoGſ|N7Do+27nOWxGA u{c?;J{8C!?0cf,qK1ƹsfLluk,ov㵨Ls8a+揅xcsǚzkƲ:%2~ £CM,BF ?ŇOqT6=PyC*23MпI+uJP;̟hʎ}^<3K+~6 f[{}QQ\y?2Icc(>wl&y|.WNZʼ%*ߴֲK߮#^}Ng ܼLO7Τ 'ʯ83zq#ƙ׸q/=Or>!ȼ 05gqJ [Zxu眖4 ؄r܇~lڮqjűR}r~(J*gǦ;MǛ-xmjӸ5wvNGB3N(!Pp{0~9ƛZhCXߣyiuьf-Yc<΍1ٝ4& `]e>; 0wh~#ũJ'Pc?|3 WQv/{m?Jv“'~+x?Ou Q3.|8:1w>ݱi?Diy& Ӗ/rA{ymR)'>}DwWuC 7HJ~IT~sA#Jr)O&Y8ѴْYjG} c\3ѴG'6’N a[}5VƲm@}g~K7_ó4'Z#1N-顟{!MzL~ȋ X7tZ1ssNk7)u3:^uD{ImEqkbN~9נRkk|ǹ:qj,t>?qLe)|lG`!D_`.&6Xp z^~g x7ÀQyuq4~L3>:\G12Lfgɭc$r<|ȩϡo9_T_0 MlE mL<,?֢ZL~Sh-jZZ2)ut EݛB"TZ0֢MSiĩV.Jp)}Jvjyf2և)&淴sݾ70mEz7aA_7f:Ia|zd~:V  {Qce' `/^[vӼ"R{[Vv1钳y1u{DJJ+|gYn5ctm1*6./Ls j|A]} e9fDҒ:}&A ^#ƒ޲\J1f6ÜGPĹ sqvMe|FO.R'?1c ˸!>ro`cf7z(]s)Gڷ Sr T'a#wG3'93h\z0=3?76tp|٧"Eq%(ø\'sͽ;؆b˷.YgAiYr{ECLhG8 WGa\p%8=t6UWԌxXh[}4]kx /;161ӼvP{Q:3g[i7f>/rX'Ho@g3#|wء}nE3͜k#/d9]+zmW8Z, 7UF@WU2 NpAs*ܭ Dhwͤ~gk1mٿPO$oAucF^cV)̢TY lÖ R!1We~VX&y҈yo[q^WD>c@pg9Ēq)Gp. FA_?1oh]f?lEŽ@oė?$#q~џXc^>yY-?*>יp:2A_Ƹ7~iSڑU{_=ⵚySzf3oe{>YS?yYuHھgCO¯7Jgr'3i 10Zb8S*VqF$1c@Nm2}j"dαֳg3ϊch='W9W6ߓ- pV}Js=lGXbW=`o ;1^G+\LiuWk5Q5l6O.JMg|l8j>b fv+ĸ\}g䳌WfM_-sh b3~Qݡ9ZHh=Tshzp1q^݉t8 +FS󢁰"Ĺ_ĥsFspa˽S7tGrq,绅Afύp`cY xSbu^Csshv^sn3֣ޛCQcz9w=)t(Y0'·qv^=Fwm{?~럘?;Ҭ~x1%b-k}VCm<(м;Kh9A6q?1Ņ>&~Mu$ "?)n˺3Z#`yԞiBj~0Z2ȩÜ5BÝ~2X9(&w|s6gһi*9I 嬊r^sH8ʽZ8VߗtZ@f|H'ҟUQpS#c}zua1S\;;sko)UoS+|H +\J Qo7*A2j275ZGS-MlKֿ%yV.7L-2ߔ=CG ]HBwx!߳Lm Xx #˿W[>ԎZք9m{:sMɺG x8» u6O㬉|#:yFO'O6Y 9Jüd*JS>Orf@3c)c,[B4d4V:dBxyyxb^+“5QYŵP|g42<ŧJzp-㳘Y2 sT[|G{#ͯ̏O4>hέݲїNNK?} s[vAo/߶,׊pW>ٿ7 O~ [۰9}"WyVɾ%C򘛟9YxpMpz0nl-c+~"3OrR.eVI[kI1^V|"mg^eEpy՟ZDs{HO|v6"}O_cZIgU 0|y+1|/V,WӒtr-{?±ޛ1V "/ GYw|1Ә.9)Q3.=S26̷\0hwkql潍;sDG8wų9ٻ$75:EV[?)c"|~a?<nkgM`_%=ٿxa1:rߛut_wKbc9gO.S`:XcHk,@pXW+`&srv3έn_ljomw|/w 0.GD}lȬy'[ͬ;OT+ו6 V-Un }otq!ܠŽ6ٍ0; tIXn5-?\NZ O? ۉ U8X>e;Yޣj1d];yZ<;HxK=61rOpR3֖0y"ڀœU')C L2%k ܄1{87x/\jr) 6>Bm>mƳr?9#a>m2܌9F[Qɼ,wlu!!gP% rPC UPqTxl1ccirp3gkg!kq ;C}\2?H.Bz?z3cĽmEuio?W(UF%Od=@0Zʽ|)Mo~[.oäݗ|s٘ VnIO妍KS+W+&7<|+ş'XdYA u1e6߃gQ|Lɛ~b d(/1D#)_X%_2JmarVx8Wl+ D8T2*/`A*1 ]rfrUAo3{/%w~s]DNˌg: ŕK2yy5 ?JSr41{71:ŶʄuJɝ=߬89Wʏ,J~f潥Ǿ\V gد))#8'% KXT{?aLa# cWTl띡JQeMt˽aYrҞ䙴YL w-?TR2o.8z7e*W+x_;Q ή7K‰UXlu֣Y~&/TXlbW[G ^mpQk),yÒ<ȼ{OY Nw?9'ݴ;Ywa>C/r F~zXQ GW~W5Xϼf*7+8TXktx#р[du1[s@O!5Gó54 GKx QisC}я](ӾYhf#\8˭Y~>Ɯ+9 x[Wv{F8ǽ~J}Io {!OB wk[x=X֘ck)˞C'F~ 0Yך\NBdSq8s:}:v|c7ԉp>k8%>Z&V6Mt% IYW6'8 rU60"ʼ4H1!dmZj6Xi &W8mxsl6y[8s_0%,N;G_o~{IgoZ,vA&,_^N~f))\ic,n3T[jKD/Xghחf^sdmvM1iưc6cTu9{meCϛ{ai}JYg▒1^"pi9ߎ>P_'wvı #Rc٫o٫Pd9?"ۜZq.7G]H`O _&}W~vnyy7Utuou =8϶< Kי˶ؿP{SQq #鰙f#GX69Lś>- b}s%$wcm䥾!0[˼^) <^|2}9H؆*?X!.^yf}lj?ڡsU׹ Xo;qkˋ゙c^ϲx^9#2r?@'iռxXh` qt8g֛ċGάd[- 1q0'd*?W!ـ=z$?;6{"w~/sy?n7;Ǘp^Sk(Lnv.?^Dz!3>Nbp8cYIJ*+CϟrtI?;y[qwy)&kJ^KH6OS|/Ydc]} G-*1_3Z(;̃ :#Oq4Ʃ2Y&nF_m4yF|{#e:]߯]v- ?&/hVYٿ7n8{vn/cjS6/Vׇm}뻉d3 ϗɾHSR?\ CC8t8UwDXOuyBies(-MT~d^+֓Xn+AwfW,}[h>*\Tį?3-[R^p~K25]W>R{3\-X/@yuT.ŷWO鹠҃)5vBct[~e^ex?k02ͬc\3[8`m!^kؽX QT(|UርKZp9ؿeChp\ 0_+X@,μich/-`4o>AcK͇gmwSXgan}ȝK8]I\Św`>+#Wҏ51y'=w M߹&ڬEྻ)Hc{W*8#&EZ{|~7ax|B,[qy~{dK}J|[z^8c}ߥ¯=^/ٿcsշET}}ɼ79,o,Z4Grov1N#c %=α4:G΍4x̿$n} q4}yؼsR/f f`@ 8C=+UkT~{z}yUmQQ8R*wNM>a6y[DZG82%&*Mi•v~/6ϕŻ-}r"Dm( {jM2vE28!c낄W}t s2Q~vYv{v<~(w_8ź&]9 IWä%G_2g9s1a >L$6pws*Ni.?ɍu̽q4·Xİd,!sNdh/ҽdz1q^ʸ(1. x4 ik7ۍup~5$opWc<x`U576k1[G}E>qc|X3Հb;G҂6[aazcq qE*nNs왋>f`3YOT9FY?I(}dy ŻPSg7?F,{ 1bgcv}>w)cT1s/^ /0ح$| ,Gx@A.8˵^WSݽ;NFV(U/61u`7n=*^niM?8}˽EYi]c,+m8ٛ"Vo{D4'oliVDB~﷏SKz>],#Lh6X}?=,"Idկo1;p2^֗~pW5Lᾱj !f7svY wԘTi!%N9w.2z7y}z06nJqs<H {$,f,OP9Il-=El$[$?IN`Dz3N&pD. p)_{~[/teg6F6Y Oc1߱O<{lwcy)?@=`Œa,^9q8ΟX)';c #qg>kΆAÎ)^9Ox"^^<8A|Κȋ߳|ӂEݣ7)xɬN7<peMa8puqܑ)2ǹ c 0Cـ43ǬόxSw6b9q/vrzoO.pw8rr=cZ[Mw*WlTw*K'Qw*?'A>=u3 GNg;ڱQ;mwdS7/9MxY(Յ0~:pm kݻ";[<<{sj ]L w=E?;-l|fp\|$̧! L%n1>UVh$|J5KKϥnFO|(|oNT;x(-ÊΨ`7Ƨ{U;4}µ!ԱYLu|s!kI/>mީr|o3c>Xn{y`Ec=+|l x7BԷ4CNMw8 p3֩7!9Ϙ5ubs (<ǔ1x1PQN{[S[gL}0[eFLSEm5Xָs)*KP7Jf1c^)ۭQ&gco.{'H&?f왋aO͗"X>9ףyg3&TK;ґCI.O>Mr?1()s88WGey|?ܲҁ3qNݔ]ŋW1NNGuQ'ԟm}nfB}CgSqSy属 &_|anpzU$~"#:D蘪ڧL*E(%@y&@qF\CG, ?fs?f/ƿ94Orǁ[nY(G:?<Ɠ_R8hh?4>v-kq \E;^ <1cysC>$L!ܬ7sd/>$lj*azޔ% p qZp񽭼Oϯ*B}}S^E=λzp/&pGl+0>fh/x}y} poolp|" ƴ`#x/,ꎛ3W?jKNB_q''5Xp|$'ӭf Ϙ̩tۘ_o&n //1̗q @ 4w=oOc!_qb>N̗ftǐ}/~G+.b^0wO A_Ki=rJ*GU~V.pQCCVe9S+R T8%.jlx.8+1 pGmvꋤI{(*5K7'J=M/xx=SgPy-A>Pneiw&˵ ~[# _b{f0=Kq_mBE[;NKtz!GE qI>˙.Ƥ01/瘐m~)˿xcI .koN  |beP28˦ξlr)ie1Wghml7:m3C'.aօ\!1VZ3{%px;Cs1o,c|_W X^韥˼ FҎk)oߌϙˮ6ecg(+&J3fVX0q}Agm,v.N,7mث - YXWZm׻g[n-UBIϼ8S85u~H|-7~Ni5_bDUyڲܷVz$`}x^OS-ؒjw3 /E+sw mrV*XNr+A.’ԍЪc_poO9Q1]&𥟘rT)c *s{gvxf& U<)J͓AmJ/bakѿ.F0}dME$uj >emGlֳ'qU9_f}AڐM_E,wCIYp__ Bo3&$Vߍ~c-gQŋQٲ~#1Am|-_}~e~5\jo]Zs=xLpӃ׀V~+2yi-r=h-!~v_t5,\ywZuU`A.sF;{;z.CS.(V'0 ܽi#*3=VĿ07űƱPU^{fӓ1~I){ Q*4OΏa~;OeߔH:잤4޿8-31e=ئg^23 Sv)x*e^V&mnWe~ׄ}IW> _0ϘfCX刎ifdfbt?Za_wʡ='x4 lyd ga~s"8F5E&EV{xo>0Fo7(>b]auE0Ɯԃ2sCս"|pm6!^ws;}T8?;s*\zgrzpϞ}Vg!=}D`ÿO>$4߫0n/?{1?s=f#cgf< pGB8\ﮬ2w+?2{Z`_GTV0EK',yDִ?{d-ʿaGd,+?Ͳ7ѻGev . Coca4ϔI1Ҳ~X>_mg5L亃Ky%%v<l{P6?.;G1wccϾyU(_ә{<9Ѡkq[a`]NݍyySL|DŽc&q<}{Y!T 4>IV/wc;cjbKz1Y))}sϱ"/|Yoyָ/ey;S5o_l` 3PZr0{KY 1~ǭV;?>l N[e;yX<1dp;Ȳ].+Hx)ĿFV̻ͭľy+_>7`-^78d;M_w>(??{'.{N>SIBx4Hx}5=ӎ|g~h23/xmoVq6>O䗮?!c;c/[}#w%jI;'c^ {FٓK2kICZuXAO}9,_+߁3.E}ru `,g=Dd=$ #N0T s9NCx|<9 18x~^ Vͬ07NQ}8/ Owkkx9I8h==ȭ <1 NqO,_S] O܃ ywx\wkSY0}7 x B~?Ʊw;qC/r[8W݀Jqkh%+0P_W=Үp~{$ͭpXɞ#Y Q&nGxqAc'("7WeiYqq>9[K൘ J e@Ҏ=LX0_(FDF~|gz^2yH"ImHj;c)*b_7;4N148I [pyΣpn)\H W89Ρpv(\vd^s_ova_g_(X_ޡ1+T Ωp9(^ P3(J Rs.T/2*Jd8B C۽R"SUx͕Lg)?fϑ}*:Ih5 ɍRr%PJ2 v^ H~ΎYn*}ʥKoVœn_$ I#ի_/ 9 Sx*,ħ}BkA/($78.k InYOƸh+UcMpx S3/^,d*J)L4[>0<spSp;W\JA\%cF =pF}a-ן7C<`y~ Tf^ p2q¹4wq4^ׯIӘ/AԇPϩpnɝȓuAӺ .aM*+},{V})~F MatU+튿 T6ma"3o#Sv4s/r ,ORY{N8<ۈy&1Jjc?kJ7P8{/9ݵi~4c^+c<#.)Cp242#M;jkr? (:1SOTi 6>u#ͺe.~m?3Qo왨,ttV:Qb[GEż,LEۋ{|dxSc'Eߑ>G&VR qL?"ҳ8W ˼=9NUUȻ&l/? ʼ `l#IrVDzԂj fWxpP?(1S=f0vJѸ_g¬ǰ|3]6YܫӒy-ho 3(&tWlum,:LL}HCdw5H-|4e~w+ȽżɤiZ&_Lf2$22zZ,3vl_%d?Q28Ϸ}[!nƼAwb{,fL2?W6vrfdgYLHDo&V U"?Wk_ [X̟­<Zyr.aKxLc;H|ܒ r`ozdO G`~[rOzLGH6IӰr˟ĿcW5R㖅̷xntt(>Gy _+>#oVQ6ݷXn%gG0E,C9Mj|/d'-5( We,36~a\8i;q|x&sGl[)X8XnJA.Ysp]ܹ,9'Va]fKY#VC›ɼc֘ g?a6w x)|UwOs&ipz~II}<ey[#x~3_҅a'|6==!_u i_[w[ԣK׿E:'\L\gel\0W%I{+25g{,i.Z?q&C Fgˁ ċ̹kW27,̼iy'TMrtm; eH 7z>[&\ gc}WɹeKҢyw㙟Vŷ&?J/+[CDwY'C̽m˅ѽ rVCX.+U_Z\{ܾ[mppu1 _)˞A/Yl#{w=|yLSS5p'(<1E8zBoy_3[a_Wn7dKk=!Ae ħgS|&me󙾿ctR81αf1v߃O|{/̹W5/8|t| z}o}j%tE;T} ~Oj)-P&IO [ׁT eet&xmon;ʤ߰l A"<y^ w_jlneC =z{Ae] `-@ސsタ=+z,`V*hNq,۳Y홉~X6ἁi|Y ~XT-{u'cg=xֳc+Ƨ?Oֻr:Nm(| / ڷ7 ߛۿ9K=7azABvםCa3{ d Q|d C E:{sEPd(gH;pz+3xo)D Wס|r Ϯ"}Gp"sZ1RmG21Vᅶь)y: ?<4M#9(M<t W#)~p{qS#\wý a%ec̅͞t[ӗ`ǿLGVq4\G%X潦>Wg*K5R”xj_^ָ =yک7;d s8ݙXlUaO >i9sP00C&(7P,zO1}i!߷banklOc/w q8حl S1pp^+Cn GȇqeٌC'u !zaf'VΰqlC߇zn /||"?WpAs{}ܰxmb6384/upO_D(3E ³XF\QE4 qKEQp%!h=R_"4o.K@8 qo2Ϥ&BzaPE"}_Ef]UЮF񋫯$fkd] _u25: ձ|)𣆫*H5֍Sx ?:_yŕ~CFaH՜;9mDgpoMV<#6Hl MHQYּ"`LT>: `*sG"vELQwjE6r-J]Ϣ. 袴wsrQڻ9(G(Z]&[ 'w>>AADH2'(BL*FqkS(ËIbtFtY1bԦYΈ)FgD3 tũ[Έ)NiQΈ(Ni^<ҷ}Pqj>,NqYsWwrѬ'ؕ3rцx Xa?X-mQCo nl8 ~ӔP7Ym0Ήc#)߮ "JPӔ)ASvoJ*#Wpg- ſ%8uXv ?^/Mx'Ru+Hg#}չ'/cPMtnF:Q2Շ4؍/nid :FW[:йVaKYon}GiV[#W9r۞V8@9H.|Gz^ayNE{^ݟx .\CgPvEۋz۶gRiC|ʼ,FaIsR.%l sgC _P6Cy<>h/{qOO] =k"ܽ^)rl/oHrE?*?r-K1?[; ۳\%A;W2 u3/ *U+(8uCI2;>wsᔲꝄSC;reX.ܒo9Q|#=@OB‘r ͓yo^-#M{T=%-oi.eql_y1>=[S9o؃Zy71C(~޷hYg^ Li⿬͙֖6k%kAdn0TOʟ`nO%Eᖦ|[Sl"V=f}Hچی|7CVX~"LFoAo$NSɬ?WRޱcURikOw e5XwJ4hUڳA8e5kICb"X Se{V?$.k+Qۼ,?-GXc#-WIM=eQVf#}o/^9oH&L[ixc)c+0oœ|^ 捷'bGߋٻ&+ML).~O {~Xm|l~Ƌ"O_ަ1)/}%SdMb p+)1X}#_UsoHP0XG9c8O]j^ 8xWB)݇-hPzlJoPzF}hg;QҞO6ɂz- 듻 d,?dZXj'.5Dqvt8(9c䟶 NNs_~_T5$JulkUdUǺüV>(MRFRT#ufDZgM+n Ͼa5XeC g(߱ʊtS};}`ՌSSW7 Wnͫ V zI^xCSڹKƆij̬1b*hۥȯ7}:~qW򘹃Hʍԑ{x 05m7 gVg#'q=ӮfR@=#[mpzFϛvsx=aɌW(?+-J鿨MGuj=J[fXc-/wb,!Q*'@G A바OYם-Yobc 73 ip]pw}1x~#m#B(ڀ% sC$ VJjN '> ߡ5WX}JiVxmC:=.旌= p?[mqw}YZ&;;Ǽuu*Lو7{FW5YX{o#_)}  <;&H`> obv߻I6w}ӻ!n-hLnL76wӻӻ;zdz [/؄77ofn~p7l~Dӟ1e,{!mp;}GGHߨ&|)ޠ;uH=q8-7r'9.}ߞ+ф71LiBS>o%oخ1l+9&ߘSN# Kaa}*O̝`\w71+c`X{X+nA_n .TCzxM) 鋏*[ך y:}TD_C;En1nؿE7X as7aST\Ϛ̩ʜy9#R]єM.hJeLSwR]L،윭Rͨ.mFuC3Q]ߌfTW6Q]ߌbTSi1Ķ9x< 1< 39 F cق')OY W 'N&P_ZRYu-#}uynj}\\ as=:Ҵz]:'.')op)|QG,ZR?l-Sv>'y+V\Uϗpoo:i?-d~Gof=oVcʹi.ݭWkrxogOKЏylj; M`\2^uHbg^$\F/: }o![_e؞VflLʌ76Ex y8NVԿnE}V/oEcWh,:ڊZѼiM̭i\5͛kys4oԚ3ZӼy}k7nMiֆҝ ͛ys64o~ ͛y64o ͛y64o߆͑miޜ-͛+ys4oՖ͓Ҽyy[7ݖͿyӶ4oNێͅѼJ;7hGh<͛Wyh|͛iGiJ{7hOӼu{75Gp z(e \|U)xh0~at^\SGp 8C:72p+ U=nUGp 1©nNp"m[~n ⧿p]pO[[7S=K1};:GO] ~z]/jl?="/p_{uvw=crp7 MXz O;C{F8 vpj7ܝn8pkO~7Yg/ܪ~n[pa}{pܭGp NJ{]Ml:Yi<۬ yx>AhҢ7wОC^m'w%8qh q -ٖil7zw;r+#%pw>u(6Fz7y"۱19Av s(MÝ7߫x0ta_oj9QG1Fq1CƱlD;~_Jt1ޜ1_9{TCuQO{ƨ=2S2qaΌiDw\O>׌\D5c΂/.L*nn&>. nq7:gϿּ½!>}C qw\vǽK3|{v۽g^7Q0kue+^S Jn]5_?c<4KZY9%J~‹պKdkEYk_͎FS&ߌ3-jM#c,׈`i}Uz'r~C3~s7N|/~8Ke]:Q_Es=u_q8r|w^:nU% sY/ORu}GWU_$uZLb O*|Wa|R{*x+Yg%{Fs"mٜ$ۀuUΩ=`=Yb{b]+Eti), -"{ؓaY DŽ_*f[Jv X(;7Q:s*_*R)=O 7wց܇C?WFᆌ*čk}Os̯iyѳ;Y Fݷnp,܍yPveº2'wh"ˆ+@R5 &V$X7 * d8җ**|aqq:I l%%dً3d3+,dQ&`cwnٕ|e(*+ҕ;th]MY6N6P>vcr}-d?{.3[YwqD<.|q~}4 @za2Jl'/*z!GLx~\\倻L679"*=h*6:6|#A҄o|dk[, R GlS:*`r#~ 7#s^*̜5ᣬ>F>BQmrw}(nC?^9p ww|,4OTZ\{Oqm=z,?;+m8M}-OJvG{b K:0y8$oFe~ "ϝfU%q/M1nXT;ri _RN _򫇙6]YEv]deA_8t8򙆛tnU{xyoa8ۛs)~=X~]$,ެ;_HW/c_񒺂5۳o|Q#5K%'l,)OK ?P8hnp9_i95ϕo36g_pg1Keȼ sp3?0?|=$oSvck#~O?G81"c;+e 8rLOv3zѳ1Җc3нJ~c"it<8=c:oܼ΅ nst 1y8F叝cݍ)/gK OCֵȊ'#Pv|z4osuɊ'?&ۥ%VHpBT8Bp(p! {(NpZ(ZT T8NpRX,@]<9\&%e)s,kS9kk {]8? b @a@:揎'Mci2|xsUy=0ɻh/̼GxVuQe}*kSYʚT֣L Y[<!kN,7=H~=W̟ YSL8ԥCSL}c)jʷTSfL5TӦ|\?դkSm۲L3Uflpgk~fl{8ؖ{tc[tcےƶ~n{"g 0*G R7N1r Vx‹zA[Yn7¡ /Xߔܛ ~#8P84u;r+`Y_ taaT8Xㆲܱ q}\$`}n;$ߥyس@_}L3OW}blt˰ IC_~^։) |2(1w¿9 s-,z$W| 1c|gr/DSG8@f r}$߲ssf'zNCt{wy%t'C!}],j1ƻ<((-$Ϫ[r''AhGI}_}rNg8p/#Arsi=]ʴ\G$;$$'1d_6-> ?/?)a*\ U8=ϑr2_NiHj~ESyf~[a7м̶w:˽ao?+’ܿۈ$_Sلs pbs`~z/2̫kM-ocB>y=E?b\ݻ\%23Låz<^ʼVܢoqVŻ0rxkJ8`a7sy}K_Nuc8+jNՁr0^]@qE7a􉮢d8d^gə`r7djD^r̼nV{cmqcAe~O;q#>)OIo~ȅ3@[ R1~7 BSG} zrz{ }pw"߲b r?,x&7}Why_k忄{S^A~|FAxw5+Qdd _dTUg4g<,7{u:;F3j#'?31~*A߃3~kNsω^aY}l?CHiO?ş?sdg[:ӀQ?=_CԻmE/iؾU">J:A,*OBܲ;qeV]V1o d,w=X6}18˯)kz k33 lQ ixPc=lK__n te,Z'*e=!lb.11zt#*pJ5vGzSsk}Pc'^F^.KL=xvMǙĤk;rU~#`͘?z6.K6^W24/?tKIns\%/5cov|sn"ƽ3tQך9 ^ͫWRw囉 _q?=żkAxrfwnI=X>p+wޯQ(|Ea#߭t}vC^Dyɘ2*e\ߏܼ{dԙ*,3uj/fxOYfFq}z.3\uO{}O՘ilDw 1$)qtѾ,䣖y'Yn^%ÓH^Ópwga0X>B |*XD1a޴KMKaMxS_WғJȍbn&logSٕ|%߿*E.@WTIsVkOJϗ';}xw-IGsP4Ӥ݋[MP7/r0^CI\M;:+´sP=g~fLş-I\xB ݖ,W*x('oo^÷Z2"{Y~e?qK?cw ,D\t y,M_Kwҏ>eKc}cގs|S5A>dؒvMwr۵/,cv'p:h,yE5|? )2#8?wdzIKzEYw۶5˟~VŒ!~zVa]PWymG@KgY.vYwx־\J/wƟ\ m۲ܯV>2 `=i+8PAtc]7@~J(|] ']gpub,W/;1+`}@D^̖,MJ~I)[Fo{<=ziֻkwo";kiozӿ߼~QY N?ۡӽؘ=]ݥX|#k0t<όχ;!,0=]L$7B?8Ӻn#|5Hԋl6E{U^ W+W8/s" -qܽ)Ül瀋!_gۊ;ΏvFq>Ƹ#ˌǸ^ :ceG;W\m+A]D%ѿ>UL#+N!\hepрg3q,޿z_D NE'œh, o^U?p g۫yn^TzloÜe xb ˣ"4u&1Ҿr~v{4qg;7G>g/O2m6f_-=E7GpKWvR2XTvG3ɌfNvQ9Aٙ#ȉ9 `5y3}>DO`A77p?h)ㅀ7~Z"1-!Y,xێ9yP*X.o Np}G?beǃH?W?ԁz!)'+3*{d\)8&W\p*l|mv'uX?泌a1?yƘ罶p_8vb'r(W.MvT>dm m;UϨ14Y\,#yuY-ffNÙryWy\g\R/qmhڝ:f[@m;j`.p2)<6ghQU7h[Nz)。gqYɶw ֓kOmwL W `D8./P-xƂV+b9!oW`G޹o^o1g*lQfhO+esc0ߎKV<߬\ŽkvW0zLJmUg E}u*#7Nm|4XLK>M+P >=30}<ۼ? #_> ߕZSmzwn5G-Ȼsd>Wt`O>RS+wɳCO|oU%Uq>>t<Ѽ!=eg,ԜUb8D}Mpb$79y~ݳ5N pq9.Oǁ.l7=} /p{'H&pq\,V7+Mn'pc]7n?Ly60قÔg+緉91 sn|{pzOn4u.(h.(.7]P0#dpa9C=<#&k5N?{Bw]#b|&LdQ(z螝\y\x ,&X N(?/:_Tlg=fG W8F} |C:knݮ\߄1=%!*R\rh1dcSu%Fg|MTBu, n;^~cԇ:F}_"|ϐS֛֗YOzsf0'))")9y 8gk&px.s`w}vB?kmU˽CwYsN7Ľ,eSqV|O|t_);Eo7 _}x g%-cV8܏~>?`cGwW9|^lJ;>Dž˹agXkcyT:tF5Zp̧~R>~m+argp,}|K ΋=AD lcZiߘwZcO =I߀]<͵OW.k 뤱ag}G蕓zZ0Ï9O+<ѩg/īlwˎ̫hS59Dz垌a̫ lWO`_3Վv-:*=yTn215!Adҟ%[3Yv:kR,B>1y]S)Tx oew,qY,K Sr>5e7Q,e칳~u㳴m?G{]s4zi6>G{~hOs9ZS9G{mhsўM9.LP}Eu{ϛw܂ S~?S']λ=z賓{&t-Hw aK9l?zo' м[aEE*{w4ijg)vl`ND:,9q0a(3bXk܇rc>߀}A* a1o'c׺{z}>C}HX_%^|X's} {Y`5! dOddcn>!ʣh vܡ=(|WE>vM4H#rz^zE_QuFݝJL)z}v{ύ( B|} x.h,|#[bN}}O(MNK_vW( Dn%\>q%D}$ϷPF/QYLQe>U )X;7XvXyKC:If (ؗDvYiK|WJV}Oewp̿s_e~Fg w] _]ƛ;rjxf<-z=A!W1N;1X`,gDG͵d+Ϗv58.s*Gge[/Se˶L~387* KrA!"d[isg]e3>$By\K<*֏eĽŠw/-j7Jӆ\ WܧVBmhjC}9YK?ڐe}`p;yj |d__Pޞi̼jMyu9 RXM9>?/|`cBdq2;4?[t}.,̿+ kſGK㈮*W\<)rO~~ 7Dߧ2oH>߱~;JyRyxP^W/'˷b˚H<8cYd&2./-f}0oŻ8qp7kH8zv\[hy[w{dIN??Ktir7F[:v6Y׿Nj9)Cwg~H_:e/(kļ_R WvPbY~̏T|GNJ+癟0|w#FOqͿk4)r@G (

K= Ty-pLW:߳cԇ~k#躀yua*Bm/d~{W؎>_üN<ѕ*qt V-9#+w 6fAq^#6H35oѼ!c ;򦳾ygYwʼAS#O6yY+a> PR_g6zJ W˼V_"]a=z;oˍ}_[0ŗhߎIV=#n2yS^`֎-(KO>yβiomڿܡsw̷xWes3A:s?n3ۑME3ehK$ѝvϰ= NG}wh3N$\f 76xXkG-Tm]߷KC[ܡqsN<x߃As<;=Wv|.~vr"Q@Nrtgf[Of7f>ȳ]|NwXy'#Tg*; &r|W_5](P/uY|?w} d3|vPהփ;N3h2&pg"zE_OՋ]GCEsv'*.GMt|~~nt+px27.7(Lf~*г1Goz+[ѕ6rx&/_J+?9,y,eM(+\6̛w mi'7s;nj㷖3es-0o-ĸV[C˙]]ǜYӠ]G̽{'/=;uC'gSQRB#/l:n HsƳCK ~4CuO^E("t !p{|N)~ rK>f- )V,sz쏱>X3yt1؝ǽVD{[s;~g0V\-YRy\3{hGՁJBq7fu.6 0rwS$:U.pq=n_e'I潥ƀP9܆՘[8g=~Ҳ|-ktFς`~o{jqx= O߀/zG@r@n.cyO)$H^ :7M3 ^coe$kAo7ި p?/tbGy㻣U}ڱ_C17VIf VxpC_iNT2Va=_H9g 7UM U8n61z2?2G.W; *5içϭ# Ԍ}keWh>J=r߃ʾC>ڷ O8 /όٟſ;2Wxf}ȗ'=+|"V)̚D5 oikM} ԷC*. $?w3җG[ey92{ŘsX_727kߍ̻fcNb1c:EdMٱ"}Yަn!cZV8O*Ƿ7S%!d`3N|u.gޣ~l⻠twH5zs{ݯ+ڌP-z7X.`GA8d)^d*[8_5 1iNb\1I`k֌q8$Ĕnjq^13ƾc+,d\GQ8:ԴF v o v &/S|) H~1#[2ul/Ƹ8צ0.3p*uU/Ϙx%0u6oSfPU }WA>bHwz|,Sz>C8b<:~Dv^Gt8OsM}=|2}cb$o1ҧ?q1_S%.xLS8Q~u4^Ϙ S<_E̫vgYK07\W̫xR.\\/1 uXn2|,~4V61!w@pdPGx>7qW>q UHMdAVb ,'֓K̏BEryIo}¼*_># MΊ;L,*6Od{/f> KZ$l~a}WY~IpINDnD./b3T}ud8kYJltOdDOOe +Xc?L_y?ۥMlҌwXrA{) 胊\ޠweu^=sܟ犮pFwTyohyOߝ?&4FiE*~ZF=d/Ng|Y_QҎgн:n@pUZ<&*='Yq3@>idK9ָ{ΔV]қwUX1L"Ƨօu.Nѵl/Y>_Vee5 LATݎ}Gf?~QƸGT)4Ma)L7. ,Ja4R[ƳkL&1RD`=dǔq,{Bq+2mG~ބ^pghהu%V 0eLGoJORiv3կe,b@{Y>ȟH{)|/1ƳR]TgEoXTPYNEe2."~G/ZH |1-m&ۿ2 fϙ<0GOfΡ&$sڳrT|wr;HEpy{ɷ?G*3wI\廡 8)9dilM\i^T$5].5( \Ƃ!`0`\jw&D,@FlCCG\t]suC{fi4dc4lcl4dc4dc4dclqj4d4dii+iƿҐiƔiBiƊɜiwꚖz>i'纎gn'7bΐrC|+m<Z{9ŸF6j&<xcmzC-gbo\|i:vLG'J9s۪2W-D.g.>m0YӸX\T~-Ż]?HOzLnjpׂܴ7=f+nSO .>N*.%o߫.^9 \WzŭsYp!G@=" n.pepgYH'Uдm l{ X'wwK7CK;g0"}%eLu|ߋ|<+ Rϣ2nW`~4+.?R/cˤSNX [)# Ɵ͓'I_Lfw|;v[4B1.c?8l;~cOq1EFl3{iJ#ENgYLut}W25ۼk"|x4 oYbb'T$^]9`9WP7D?uh>'#q}Gv}?I1c_$?,;N.qV/e/I=٭cN?%qT|X$q%| WW6iҞoxvUy^VcgghI%z][YM0%d T\ovm c[|{d=xJwzO𧬑9p^Hi'zA`~W.u(u#:E1~ c?~ 6fxq4Zp#ܙqx6[^,~߉~E?sUя /3fu,3Nv±e0^}uplޥdɞ=~f8P9\l4㬊fk8a0My?nښ:5{KN뺼7`8ig1ƙ#vu.y#IvӇo sM z^! 6СvsW]ǿIU_Twso'+lzh.hƘ'oWQwNcHgAvĉ̘xMRN:#w7/Y7!iTiw$@+1ezL{ ṱyx>c\{V}|[4MHs &H}R44"?gJw mF#kW|xG(}pcqEi<:x=4b\n>n+!)[aYT<Ǹ*q/[cg˞E_sо!eI9#a'%ަI?'c(\׺Y6K ?pø9J4sFWii_.l{Cg*'_8H5,*:.h:IQ^$!6>Wc NR[c<˽^uN%ĜnjE#NLLXKp`@˜F|g<V+`F$":18؞ ]BA]2|2w-㬎$cβL.;{8@@ׁ8KXyK~J_-q~:{|C=l_W[٫iCq=yˀ'S[܇'#:YcѓB"7è=X^8P\繘B|&Z_OJr~:cc1?[5Z{r38r脴f*q9G ' nx>ʉ#1Ǎ_!/|w,*%0-ITvSòKωJ '[i1,?fCP9UCmqz&y9<mfz(=[mCϿ_qһ\\v8/sxTIjzg/~>q*|/Lf=mVkTMTYl~HH۔Z*]"+:{Z^探gh#)ܯs8|̋ooSҘr[ x6iJgwv^s|kȡo$/fWŷ g1lkYO_Bskn\SSds_^jIQ̖Ϭ4h\ J?>>7wo?j3 gMG|9q}[9g {s#qp1{osGO8ZVmYBͫGU[vp>zvkuq"AUWN;x?*s<2gU[4H:6|Qj[y/Xz_y?0{~S(0Ok_fz~{+4o#}s-dx4c;U*G'8XOcsvTOu2ཌ>`9[9*%[e\lqa W P?y+Ӽ۪< ;?J׌x߄ԽuC|9+^5޵ y]`>P/=+7 &OK'#Je@S9d*5Le֭q+6u$6YU%Kczeilر8.9wq*! 1M?9Sy0#q-Vw:d/WEMh}چV?z G|jl|;@ZJcv(˦$ׁG=װbZ9Z9]f/@ALxA-H-& = x> i<^s7YPA:Wv ?|D:Bwh3?W'*d//; Kue 🷠)a&ohr.UT.Di~;Fj׼cLQ#}>78393g3s~,dOړʋ/x"eo2I>~3-kxT <|8/F6'7S<0$ﮥT6`YbFy$ia´gbe1<1zy0EV"8sn|wC}lo={N'}œnQEa˹4"89AY\H6$YH*Yyq?oUZGM;9~=38ўs={;N= 4M`L'mEIU=fdtͪxQ ga@Fo(]@#Ss锝P`>g08*?UM0//le~6s2'Iv??:G{?݀"`S&Y Jq&ai&?=[8wcq }#p b')z Ŗr.{ݲ3@P Pg=9zC*1GwP{ Ǚg !,3LqŸyY;NshX7*V<.6%v7Ks8=w@7pvx#&jEb3?<|Ǻqd{몿.!ߥ:wuQV߶ip~j5q^L?VP)\I;)třT( ʿ?6XxwB%Q[+. p;Kg#p2k߁ ~[2ˀ[g91-?A8֛s,;[2_2N?iR֖Q<>ZWc,rm,^}pm& *G<j7AS,dּj0=w8[y]TS8] [qs~G@; 'ώ;F|廫@?-O=T8_JpZ޼GSq[>sWNDo= ~~E O@W5eƧˏ8|0p'(,܉ Ǫpxr0ވ}wf揰[ᣬb+2{00awm̻q[]- gJ8azOR׉'^sM,? 6HZZL {tjw϶(K|=Jn} KGKQKX~_ȷ&E^^a"'}V/qC+B^p䧯HgjL_Av%}"+RNEJ=ַEGsG8P/wW]˔^[sx_uR1=`vw2@=)/<#?3 6_Eӿ&6effo>-[y۬>Qx+xܽZwB(+P+7″G_BNG^G=)*d*Zp)Z{)hY%١8G*R[J~>)U6/;N >&4'm?_&]}*[٤qAe-*])|JMy$bb%Λʿq=Q5C% /X;w 쨲 jBU,%\d}̇&OV5yW3Mal[[nٷ<WaoNj9;nF{b^z@7h|y ~spkؕ7 Gc뵥 foW{ƟGUopx2;A?neA~ږO\Szpab4/tXSnVhԑy8P@xˤl $'J=Ǘ_ Om)9ˡVzf:sB,ը/q~|~sxxϼ|W^QaoJtoR]BAڨ]Im=8O=\iKX|Wa6sƸ~II~ermGӐ=P>W9=PX҆_oyyyDV'~UUI_x9W p2ɼ44|(Yf܌eǼ>xJf^mWXTq,۲. s7{M?a| F(1peo1?tdܽI?y@16 ,c\L<@-sf|`'j3LkU';3XQ*6yG3rwwa`KՈt껡/D4!Ÿ>o̙9~muP5=wx;Ye9}g_K_? fۇV]{BPgB~۾/ˤAg1 =wUd^A\~LϨ'15'/X:ˬL@q{ /o-W#]vX{\ 'A3s)|ۿS}+N l8}np[[UiVHl$|V} KHYeX=7l MTc}Cdu}nio4 r輐< 4cUz!SI208OqmF$18z67#Oqeߤ!V| l;F 2r;Yn C垶&=M_|EjFZ<'6ބm9ö}k8 bĝЅ?BЅȗtJNǝ/>{^BgFgϞe׹Yip=<{V-:}h>9sbZlr}|mocrr_V8vfu~=3c-7bwz!3IJ)}dSw^v:WT[2vcRqP8@c(l`J h-^eDM1%WBf>s,z<>y,I{NBsRb=g-;𙹾7#X|tH~6_Tu-c:~_˪n M5Ͽ#'~*} ޥ|2|^9\0w5vzo6rוuw?@A}5]}5꘵s#NA('1ta?֚@x#ߚW]39_}[~i꘾N?x~#M],XUފ uYkf/¾ Gץ Ժ4ΫK튺tm]]~7.m_ζKgz{wraOYoz7C=қ-YVGg֣3s-(Aztfnn=:3E=:3;]ZU߫Ogק3sә"\tftfc}:3ׯ>YcKU=^*037qs[ߔm>h`P5clCZ LZXΓ`==>}nٯk ߺ40+o`q9ۏɝP򶋓?8,Gzܳ F:YU%{ p҆JVMUDloT76:Y!սwRj֐ށy!ݿ!=!g4⢆4W!B7/:7c":v!Nܶ6Sk >9+ͬ'`ۢ7ސ5@Ox7"\@łEߟ1rxn8+ˌxPPzJƣ3e_S{liX̨+Vۣ Τ)Uَ%6>J6'UaD40~nlL扭Z_g`4W)?9 c|Ѕ7݇j~9{yEz ]S yLaP~u/g|OF]xw6z곞jBh0Pn<<il1O/kY{Z;y>O07?n<tFY9Ի#M;L|ݫ,z1>tOhS;̻H_s}?:MMIGE _'|IrU|Մ;ג.1(7 ?eW˓q RX*g)͉pLb^/V'cS??X'<dܛro1TۈcsȀ==gUGw[ז,<4'~,Ȧg|MEyeoi_' hd[4te;q^ sG>b< OesS67e}FԹnema\0us Ssh#uzW!wUXVXY +޶+^;˹xycVpW|nb<|;3^鐵䖴VYڒeo%)8qVuu%UOKZOxߣ5Ox!ϝ6tI?QƟ1?*|ǀ*m#kxmCYvxsy|; _%5_aʞB {9 0:\d[m[oƞ4^p;@'*DmI{aZzm}Z΀V> lKk?%֑Q:{ Ye&zsK>lX2e>3?Q +[|K۰K߉5gDSm/"̯~ͯ'NXmŁ'yߵҧ9_ ,/+XqZqLP.0Έ_$%22!H:{F\Nos& f}#B1}Y}oVV^IOq|F LVKx.۪so/wvSy}-;⺹[KnϼV7+qK;w~z/eo@Me$,7>cqqeZgRFǙ*k;o0o ʢbw+gem[2,/#FmGʫ-> 4EgCOa~/8|SU|m-VSfY Xh?qu/81. Mt?oQf7?\xϋD*sy[7z}y_v;=H!Zccgdc-c,3[^і80.d%>[f;Nt9x(˟a/1۬Ο[xXOe]s0œ:\yS>bo?o=.5I8g8yr74r`1 u#~㒴I{.=ǫip]p1UDŽ/qMr()m[ |X֎KFd(nIk3l:o׌2aVɟv{F{qוWM:1޹ 4EVr\p?r\ˮl6va^ pkS̋ɾ~F a^ Oz+>)I[K:ĿHxNOnnMY޻ k e|;2/qXOs> Ugy!^f8E[h gd!g%2D&g ^ż</(me^>d'/Ww.Ӈ,Wj딝+ᴾKrEkϷ݅> sXv_aVr]*cV><ʕ׬=KWy?Jnt\)/kcWxֶ~ӗqxEl1 TN uo|%q|I: |h!Ͽ;vblVR2_Nݏo9ELm^RPUf+sGGGg3)wnn<ĜO_ ׷rkw_?^Y߹O;ә" &|?d"ڟ)|@`#lm#c|`3Lb$v_rpKm锗2!nU ưvls l7l|}<LfJ-O]ʷIoʲ7opkI0 ҝ'@ga\=P8}ha3w>~fܗIZLj=㌯j2/;^H潦㘾ʾ#.Xyہh~2n~ջ*X'徚>XʝIr ^A;>n6CAT jA4oh76 DgM٦ hnm:=6]DgyeO+i x;`4y?x!Oa߄e|( Xஂ9@LwjQ?##v#>Jm_]ĿeM(s~+Yo Ch|6q!g"5;ytfD<<>a)O1§Ys' ǽԓy3d,Iګ3 \K}bzr\'Dy̛攚{8~Zs`c?]JӏrKYnmQ·!f$^CL}wX/w,:ycʻUb9wXv XCےXj}8| 0ϡ5>}h#{&֌NR'u=yg8o*܎pE׀f7œ)~2q/w,NgP D]_cG:PNXzYUYR󡯬5~t ; {,S1[?u?Nb _U[B9W%MtdޏoLyMMa pHMZ*@*;gw⛧Rb;8GPDS-9L3h. ? sse^8ipϝ1u1י[Wd>}99[4O=sIq?G{ Uv+oފq~#/ՙf#i\Y 3^ x:㭀2ƹvVGf|I{=̮4`)ZG\:ek|g+2l[H1eQH=m]e^6 gYzW;%ʡ"϶1kW5Xy60K|RR3?s()l=,WЊӗw,7&3/Z/lX.yŕmjځJ<Ųw5BFrk>MaG~vs0c2 7ƴCa¤M@:[,W^xXGD7@[\EKx-32^8㟚2AtBIol}vcþn/ xXp+ \g= q _2S%!a= l>c|F1|~o`++|a1Ԋ'UJ3~brbj_8$GxA0ed^XS^Iޕf~7g{쒇)%;ao+L .EyЧ9Ww~wM!MYM_n{w>Og37FsV<04 XFq1{/X*ԻGҼQ4;3!K8aМ6 cK|#>g:(DZ Z53Z@EE}'DAxɰMmp MM0c5;0c&X_{{0ܖ|N0߹ieWMg+h |&˪{8Rglp/{3(n W\'wP<ռ9TT86@;<@xW_T6OP~!ϫyUf 4Nb)P[^&:1~|1Yw&xKT|X#ۨ*i@sa. B'F:ن ea 26Wa Q~P׶x Jφ2]s͉nq#[p"E#'sY$ܕM~S%i{>\T9moI">6'JX{~y#DS}b}Hgi/UX$Bf p۰O[I;s9|9䖁[>?>$ܰ|djLr2ʢܭ/_e3qNܯCgodz.bVM61xqsKʗΛB djOy-olg4+s~ıH_5K1`ɖ;O=2ĉx6W> L/>7yxOdLv*<<ʝVL}Ȥi͕$Tfp-&d*3#X4;|/wN~v4{4:rW6-RwQQ=!) 9Vs@Y9F.0Y֡ǷVS67 'c HL~uy}葬7Bn*W. ڳXgXd6MvvW?;p0x MwxOSƘi.Xؿ8'wz1.7+&u\b{﮿@hU~2 U~>PlΤ0ߤ]3rƴcZŲx\I~ycYL!iL,ԩ$Iuӫoqk7@4fIsQlׯΌ *4{ MuL,xR32X %1ᾢL@x$FǏ3~L7ͤO?OܟI,"Lh7ƇWgѸQe+͢y$v79b7 :ԤY?nMjۅbfE,fErڬm۞*;+qhD)}W3}_*P UY4}e? V/*|[aυ^]Uy3 iy#p8ה]"|:}v鹂r*;~ .(*yQ(Ԗ=m;ㄐCC8ί7kbo fo!P6o3> j#ƓY![se,ׇ珼c]CfNx+n杘^IcTw*1U;NZ 2^V؊؀58'ļ1q݋1]x.ި(|I鹯$p|jPnx)=:RvgXww?OZsV(Mx伲O``SsMkMj"8*;C9~w;g&LsXH_;rUI/D.wL/zGѠwJ:}sM~n)׻ {r9W}2G~如]nYg(#7k :Vl= $3IeOURݖr@.l)@ٙj+“j%-^\>_F7ekY#Fu=_B歰EYGaz#kkH/__moŔYx^ #a*\:6\o +@T.TxG9׫fiw@X%2WC JqS4dcL31O+܄_Zdjs-,|%=wx!633~UXۀkCWF~ ;lwHl^KxXʼw3N[ya4yg,wG3lb^杷 /ح8$N E =XpoX၌G|p"S\[b]lOBm`!o/?5E ڨߛRwe^b!t%1m[3~V Kz컺0;~Sv_Y]y>I/> r?H^[|!\f)*3/y<ɼWyx杝%Y.-r5X.35g?t9L*v4 g*nLA 1y9U]qTYI;5ĕX3?wmZKk؎Co$K)=7Wxz32s,߶SXyŀWuy@O?RᏗ;|3|^[t˜B.'?U^ WP߷ɞM,ހ8(JW+G ^^m 1譶Enɯ|{W __K\]W_wףV-|?n<2c՗E? +Lس¿x8_}u[8n?~O#W9sƕ*Xk6or_3c|y’l ˘tn_i~?%o,gr1N$HI9YL1/Stnls0[M̵|V@­\XFuG΅ɻqol"x08|va3s9o#ѳY|>pVE]/j<_k!dUEC]a=>lg~ޛI󶋀1t-Q`[4?I8~|y*<;YҀ˭2ߐay GQ^&L:mgǗWW{ 2`О̻ f05_/5.=Ia#5kwbY}!g¿yUl>#<ļ4u1:\j*sF~\4AN~kVѳhouܽʜD0oў!3zʨ>~Sge.~d߳6 7x_4_ʿtC3f4;~;3o5^/ȼYûN0eXw[/ܤO)pxSYj2z',f",?Gy;_+sBW s.;IWS}̶]M:C[S\[F;Q[qy;qÍg_h9z5_i5:o0g1}W46[M6` ƕlռO>ZMg3WY_R=.%~ݦ_R=؝/)$]_Xs'/z Wg i 7p 7~jyƈygǮ:ta̻t WM3kܼsCYFys.{Ё2Ƽc ~clX>?i#o?Gk>$o`9۫ǗfAnm LauֺC3u"+Hag[ʾvӌ]ٛ6=sdN J fۑ9I8|I7_%3"P~PE).9ˇl~PC-Q(/^ RsW*B- oVxޠz)VYpjo} _Dba}7]~ q38%xzWƟW F;'rM.TvWxw1Ƹ_8[놬e1;o_*|H_wW28.c2v?aa+{?7/x746d4*OL܏͆Qi 9m6n11z(s/ J㔞om~R#w 3uEaq2'UU4Ne&q ah%mEnUlG]ۙȜ`VO'{WDoq z*#eEіjG_LU6SޯٯW !{_uh4vPJ֐9wZCVwwc{ ҝ 90+,zgr/Գ-׃􃻇pY\»ļV/0Mid;)^IvA(9k!^UIva~᦬]9'uZu.<Tg\7KNe[rZUkq?'xYiO== sF݋gv+;¿[=;iOGwa8Ļ"]ʒ3$8o4oHS]]*tteX(>'R1&[Kz']%%t6w飻=xnRϻh|-rp}f"[06|v%<<"Lqvӽ1;r_%ʎco1˩K=z<mȪ¿|#}i3ZAL퓈^;vN΋C(;桻rtC/Ů _8cTlZԻ`k,[?;1ci;o({QcN<٬8>:q~y?R6aC(G*7 |/#}{שro.ؒ^0MrErcYn ˅ o/ ItGzkcM wYirop#7qEƿNt|B8g=:PՁVk ^g?S|#78KK:$O3gNz:;r0Ēó)r)c<(5ݭ<CS{sXoAy[UOa9m,<yyI9C\ ˬp|m^aӪ<^ewYg>a95gT ~SJ"dk侈5̿JG-kpqy-[&H\q34gmw+O2Nx׎yXywSRqRw߽{q<Y+8ER-GߒMK b|u:[^۳; l#"8gq ?;/!#~b{,E9: cIeyvμ\n !5ż<[mH˞þe~ |>Z MR~O+F~,VS7"qu%X`~vO2u^iǼ|w3f#8s#V28*֡[o}CqOg;S tʟixO`@;^_~;P~C-KȺxUs (`dǗ[g*z p5R:i?z?bږ |}7Uf y{嚫]~Vc :d./%=H6tФA<*2~u|SeܩJs 9hӌ[[w#ȼ{nydއ)宭 )Y'؃}%擬&3 S'eׇb9_Bj2soqrP`gMP8Ϗf쨢pNь/a sU.Eu8Im2]!nj̑8j?X|ž+xgo"϶MY놹̛ ?y^K_b<Ow}fዃ$_0oi0mK;+)y9uW[1o ~? `ܱYBrG>H^ 8اdFfsT^>Ja [2V`f\su~½ {8|wkҳM~żV]sdߡ( ]fcT =fƮ/AW`n`{39 ?Cz:\*\8?CU8'Ty`xʹ_w xWm_(³gMxc?'`}I?Ƚ!ry&杷:|l̿ɚE?o]}oRG}6OF^+؄U/mqe*ߑ/Kϼ[*X'$ɇx2op.<|?ϪrI(^4=3ӻnXHƼ8U2-K^⑽טĪ]¯gAsͯ|H/z QV i]|? ][7p^!3>{Xc:N]_H6^ 8H:pNFR"~\{ջfM ag~2wW^, OR%]@ON[6;[,R^9 NP{I}f9̜ :Qit= q]Kq;aA;: s/&##1߾4{6'(398}3opY:OF:0u W-m n_p?w*w3O 35*"ܻyWI;Rq?[_={B٫K']N"}w - u\a<xޯLŲiM'߾/fiv3rt\:7H_|qhogV}&'6%6bU \vK ˻ع+_>ԓ^?b `eU]cͫқĒ:Ћ^WC)wS潳7Ow뜢kPqsSwc!ýkz4i){J\[8#+^&lgzˆwkeOEmm;x(1o)c` mO"i6GA]O xOS)&̿1}|my=_*9k^?z;Xc k軬5_"ʟ ^IM( 2+\ ɊGw/j\w+"7G+7 \,Ocg;9{(@v a~ėBcds߅BE/~ ofAV~ ӿaG X, r rv|ݟnPub²Jp7J|_oU^/'ԃgZѪ uy}7@K^ǂ;쥮?b'`K9n pna5&Y)G֤ܸ۱RX_yטsMe*X.v9/b.v[{R9|5p/2PYm}ɼce]2 eπ?'׶ICܲU¸B.}KXzrocquG} _.r!VنUd\h/Aoˁ9z{܏Ν~Ӷ]O.}=G_^6 y5ԳPـ|u9pw.] X^vCc⩇bghQL9dvf;ܖ1[UY/Y*kܟ/rGP_&\1bx~+rV_~ >=G~S}߯3Wk( V'>O WG>bo~!ۉ*v7kPO2[U/yo_=Ɵ?p3~toVX65W@yb2q66>A9t㞊և%⧈o?w% 6ٷ编>,%մ ?k3kœ!<*"̫o d^CKx] ϼV}i^(}b,7UzBTԫknՉ/wV\KKƳy1[.Pl2b/6VR%1‚=i$Eo|~컗77qjoļ.*#ڳ-o;>P8(繻-R g+"7>Zx`^/bgN),5h~Y}{nSOyp +*&>D;N)~%>Bwj [^7pkk>Ⱦ >Bwim2gg[%7!\ǬHKVٷdN҄t֑=B\e_RPgƮ;b}6kTc=UOTɛOY$ދ^̛b'^Qw?uQmwI~ -_fIud|쮩Swaʹau&a%&4vOc۬?m4_ƶfFel[헿mi656ocۚmg{ .ps+^~p{7Ux;ԹeKxVuGK[(=~.}p z&ޗ'81.I'? }?~(=z]|F}c[$y;Թ6ʜ{ʼL+Q&]m,uzR1pݓˣQtAS¾o5&~bV1|'Q!U']{RE_Dك]YVb*+%8*~bu%g7xt9[h/}矘32<1g _ FTccʘ|"Fj-c卬W$˫_GE8@W1Rbq+h_b#xMShJv#?Nq~|j9)/**=7xXt;58ȕeP>_ox֞ç^q1pn/s [Ƅm|.rO+?vDm&=mL$ǯb"Z2Xry(/nA,u $¢j)z;^_cڷ}R⯰t=Ά4_<u|9Kl+qHJWCMQY`H;2}y??ZV+]q =θC|C|9Ω8I~󋨝`',w59O90c/eK2R" H=*ذۚMb07Iߛ^o ;x/ˋϢh/ӳ33-i4{`ڪ \S1m$/0d:cpw~Gwk= tEtUWUufIM?;&pϝTkV8k` rfNM8' pI),S˵=Z/:tmTRs۾`Q8Y?X|?/81K/0;M? GÿLc_U _UqPU>+.'}+Mva_@w.8_/Ȟ WAkgJ/4ݒn{o4޸-$]WTߓonQxq |(-k<(]U^}  !*0-E٨>VGH{f|U Xfm&|.pͼ1 osx|Xf|z7f#=I'}w]e0dlca~1yke||,Bh^S)&wW-HvE)˥cwTy_E!"^K%oy}Kf;6q}2) f)y,Ƒ|G)~.ny/{/~ޑXFˠn&gGy~WD>L61Z[I*gN6qn_w_/asbj̯ 'n_亁7*&y/a8KM++s}%K/Vaݰ2f5_b/'~CG6&:zBYmܶǾhܷFծSz;լuߧ/UX)onNNS“~IOb֝/E9[ e(zD៎6[i"B ݭ=j\$xm~{Yǖy݈_.fNYWT2 =O6A!B|8[ݦ=!;_+cp>}uYxr]Gz^G֠muRnW_qJzeMn[Ny??Ǘr<ٕۦ6M-MyIy mX6cm8p!!}^kCֆy y KZkm8p|7R1~Q  oE0ZPrTYr64IS"UoGNچzn۰UIx3UU8?;JzDk Qq ?_ 6TI_;#4,&=3BQgV_(BN^YW|*شx s)~q0O \~V󗥜Ķ+K Ux«>p{-_Wu Q=a+)}ؖvs!D]GwG v5Ĺ2AGg?#$쓏A9%<飢/ g~ T8"XI '5]oQ\?Eg6?e_P6[s:е '꺾ld.}f\s}F SMPu_SU_Qx/+AwަVQmRx›ercb&}5*=|3`>ôv(?~Ꮒzǟǜ/Qf?OPow?*KbasYakM0BwiHIׄ'zzE^JZЗsx IHO_r=g" u ?7QtMuL]*>>r{栿gga$e sJ/T<1w䊫p!%Us'TyMk3NyStY ~YO+n>mYY> UT{W_7U!޽3D.;rnPL`> nj|i-iI*<\[ 0Yn_?J._C 0\vU5VMIe(s&j=!S^\%l>E ϭW2ϗ9oNKJ,e|)JXʦ٦R6J8ZwgiVRimX+K۰Na+cêWƆկ kA־26ympY[)o*|^rVR:Y1rOp)|sUW>'7IdW:4rCH.EH:է 7\'-ww'SmN>(VB~,ɏS ʺ|;D;Lv3xv%Tft;zn/)P,S11Z'+J;iDsN{ dKm>f;DO+Tvr۱+5fX>m o“_?Ǎ8˓< Ŀ~+[DܾAtsֈFw/җF HnұO3T%+ٶbm/(mW4%[Gr[̊oeV-o_k~UT8K[˟h f~&AQ~ϫ,w|XWe(9dc>[-8MX}yW@0Ͽ[fSKW;y8bnI$n~N'zZ@G{~O:enZ0_I!}L 3Z9(sNG?ܛAk}T]ู]&ONx_o?Gc`5mAg ;)ፃ\۴SW|wFW90g%wbdy]%LYQsG ''o8 *#e/ >7"WP\)/?S"Q‘vH*7EC> W\ۑ;Cd*~rNw|iksF9*="Y+_>J|)o+!W^n$e*;y8<j*ye 3><%4v|(v~Ĥf<'?'pR_d+״cIm7] 7j C ?<n1샙JI|m}B ?2 zp-|;gHl/̗djH.}5~)ׁdx܌RǑ]ǓN ذG@m|0ۈC݆\r3\r.?!\kgq"R㷛 YŸִg7e޻R4̻~7"~VaGϯa74iUeUuyJ bAܮ5C$59 ȿ%\_Ǭ $bAgi~~ٝoɔBB3.=8Qw 8}C߿,TC{{T{|b`뮔?ɟgS%>߬uo?ku˩/_}ۄL`GT=$~A~6M- 1vTk:I7.!%ͳ#x'K)I 9M/O}x*Xz@x'Uypu86?珅o >Г3,_zOl]w JcO|4Gyobv _=[f/䓄IP} M|wS_w Wu# c]ln1%O]}]CL_:uwhƫQJ_+bRw>]s$'!ArNVxtGq#bQ^F7ew|8I%MܴBOPɠUzs~Pn{~]k+dx_ ?44ug^{A[sZxY&/r!wC74c!7 ;/J*aldª ~^e[$lpTqk *{)kȌw EJ#V 6eQaIڼzCP|^_⥀ImةPO32e2⎕e,9CNrw}-mSѩKR_^J.1y=һM8hr WM7!n<!9vil8P7R1ZğخUiY\!Wgڭ$fSu?|m扻%9Ξ]iLtY? )C Nu#Se}^kb4sNMQ7ʂ߂uֲҸU^FxmU#in6r&;[ĔM̚&NلAn盽 '͟Z5}[*zM~8u8ieJ“0 N@OW#uYK%psRЯW +~ʚwqeR>#R%ksW6q<^o=GCg~V/|'{$f֠p=3r|V:zGM,FӤcf%9qWM|(d OBd˝M9y]K%˫Ԕ&: kCWׯSé/2pp:;6Us\m' ȰMqƺ9ĎU9w\=?Jq)^O|3ȍW/41z DGiƒr~Cu;isM;bWs Az^G%*9xsH~cTtw" {W) ]DK^ml͐_(n(dIwC2UX9J!a:Ό3 / K:zSMN֨2\-RX s%mfkY׫z_UEi.< WTiyɳɁ[ꓼ=5Hy&Ù޷86oV3ؽrmgn{ig k(|=Ln:N/Hs / rɇL0O7GBn }R`^ܩNc#06|tpĿ0?rmM^V vǽRƌ LB~KivL|k#wZsIՎpS2V~~m|1>-q#cW}UbĐʒY#:j0cl~-\F嘐ZsGc -I'hߔ#¤{׊&҇m"ezϽmw;3?Q|]R--BIm~6i㦇84i#W6 LheG7qBlϠgҙj㻞% [WC/?+mse] 5/D 2_$=<xfQ<7ү \qt,>=Ά+Xȯ4PnGG[~sGyux~/`pU"[\a]̱EÐJ:1qݒ[z$xrH5#G.D xu/!]~>mhƎ**ܨˡ[*̓GA/:um<@lmchpV [ y )"' m g _2&d-Ho֦*񊓢87ŹyE%UX*> 7B'^o'tk޾QOx2~ߩpqh>n\=dG{Mf\L%12_1묪4U~s̯L{׻s0ӫȍPm=sG9e&7`?H#koƒ h>ED{Vrl>$$8ɺ'&۳`xMG{bGa)i{_fLO dl7ubM{e F籩}g!gFћYc)MWę緷0oo|·ηwjYs<= ^ şMW7J|[)+\]œT8C/ ?cExǺrUW?I{[X#%G>b=!wFsm+>̓[ ([EuSuńRNBc[Rr #'iD^%=T{/rz3l9ʢw3l{*'g(GHhg z%[@iQzΰ)=WtD%-CK8J]KrE2%g\Fݦ43_Ls<z̰TY^6'?:)?SM??y ݬ{9M:t`b?o}/?wwCȿ߄+zWxFp] ^ '%iC)[cx ^qUXﭩ>Nv3_!_RxI~]MYg)uGߣJBy ~:+Jٓڂ_I ݎ u{xyw\(q~(=H6ViL4q19i_4U:z 7cz~ i$ezڸWwdd?g0[G+2NtcHE2>ϬU2?fck=gW [+JUw,Mnxdeq^v]R> {])>ߗsv ]\'2 N9 &o K^\/]o쌡CwA% ?ؖA仂7ܞ_n|f7x{ߜFT~Yo?+Ź. 5?>l$Cd+se3MezDn33T{ 7K{;r?AouWʈ1r>/Wxw1k,,͕!i4VӒT$> lޚOzY%nYT\_ zeϹ[7M^o)|DoW[Ǝ}_}7ܽ}J}I; xk#-_50Yk^ݼWoW~/_w"/wsyd~kHϴwYo6_xty?;:.vwmV]nC쾦Cx݇1|t\J:6=6*v*wX~ݒA6g9W4I6x̖le:rlNu*<86JKE =kmΞ¡@F@GߎYkp;\s\4pucgw99Nii\$ul;{_:˗56{;@*,& OXiܽ0e'wA?ak4^u,, 3^B~u3۳S2ʲ/* 9{)Z}0IcN$rU NV1w9x S co9#^x?ÕiO:xpMj09RXoI>o+_vocbV!~^|]ܵ7^q[A*A\@gHK^7|k,U2J.W4vLFV!wN"gܖ#gF6tyS&܆Naٝ3qx3imo7~mkfϥ|Sd*2G 3,: +yʼ;Y ^^ ]lP00m:W g?ً;^6c#zEJ~>WxgwliWE"n<^x^jR/ciM  =C+9nJ*uaUtʆu%)c3ƳL}“:NOim5k9e`=wx..%O0rOhqN~V٧R&5o>wlf>:F#yKަ82E帼=ғquʫFO+忀\;¿ TuePxԦÝC>;)*Rf&4Β˖#O;T9:=ts"YK^~/odw)FE{dwҿm\1Fdr_}ǡ~;wRz W kCUr{2 б1ΧgZW>N;&y ;[CM+ߩI)k\gnidsXlnes+T7͟ 7:~c;KNPX>3ܞy"gK8̲߇|a5cW 9Dfaڶ1&ƽ%tJQ0AemJ}ȝN*CɝN2v n'~p$ s0>3o%9#1)94´;kwZ 6'< D7 {pk_RU(pƑni+-CmEi":W(w֋4k632OoCe$tVCnG*rr^mܮTdN$r!  j>i3ߣ•.O#oi|Z&2*^\&tx-WGCq >1UDn7Af8ʴ!wBk%'mo*qlȟT"ڙk!9U\];o<ݬ7FrR=kȝR9׏߱1"xs7ς=RĮ{FZBšJ<ri-yyِVs^١XBw):| SKNxf=Ԙ˯{?v` TF~" ly's55ɿJI5>!gf;_>$;2ycU*,yG1'wjmS[7@Ke2W^* X=3HYO`ߪOHed:%WxE),ri)S`y, (J3 C.=CHlq^'0+'}rߚn#x(+8]"}r&| r\`l,Ƌ|=cM\ {H؛! m}{ryݙ#ȅk(y) MscMeU³/|L~,\#'P>ANkh~ O>kς_? $8jK/,U<9ޑ/'3 ~uo]DNQį3Ύ.6(U] _6>g:FG;HoCN~(u*Z94=LrLJoƭkIm?OaG~~qlyD~expoG/c{"o׉2Ԧۼ 6cU_{ӆFOH*\WȥIEv7Wz;mi8my~ !=$zfzj+|q^ҷTdw q8ߎ^x7y,8~Guℇ!~ZM;LL{ZO45w=e78X!N~+.6rI&I~4Fko@EBg9F<A-ƨ<;>e}?Yy-wһ?!o*8;hN7 崭r~ȹ|=+ so<9Vd34;LƐ1]JyE{=r3D4v >ӂ ެ}/A/F${EwM{/dbɉG}3k5N^s:٬>ђzȿE~ynӲgȯd~g ,c3y=#qV*.6,VmSd~)?{r|UζU$(Sk0Dѕ}J+]g)FKN^ܟ=xɃWFCd.Pɼ kN>(,e` f)31(ʘFP͉˟vFO ;Mᯞ O\R6.'ϥj=g,^vpV:uF)škSw`ҷ (˕ aێLm1އY1¯(#Wzu=3*0-+)8ɦ[eS쩔ݩ;la]niֆs==k_ K']7?*oxm;, !>>H6ݿ?/Eu8 g3úOrwfV|H-<C.Jw:0O;k ǽǯ \nȥ""+AO^eoS_nwl.RCc~ϐJ=\ڢHfþ<t2/Hyt? 7-xiUz ~(cLӾ&4rI.;0߿U/,gJ,P6pmqv~'}ljyL (?ǰ&WWin5= $}\3*)sk更#s r<3)~!2~4 Wxd8 !ob~ 0{cU `h <*0+;L^ό9lgAUR7q?#-n)|p/PG:[E%qqf,"׃םvv7S{Pi^M^m'c+v8B 3bDAfs &  S؂2$B9&kOiu;_42ޒ9&k/1ބ}c~991zƘËstzѓWp+>=͸xT}*# @n"_m/g~yW?d% CB E WtԆNn"a'TX¿쵟~Kn_ojyWμR/-2q)<^\d.f^(|N&x͘Isf ___<7|+s(fhy +8YG~8P5|"SXdcweŦ>f]lcŦ>V]lFQfls XlFcьflb36zcYl)Z}%举g,IX1zrV' tg99}v^>m1cΏ)Kn:%$XbYĤp_[b0r=Iq;y'%v@Q`NoQ>ͬAtt~Oočw0Iş| vIs`~^bP(yl' |mIb̛7I dK}=qO ̛/~Υ&}s/5[t)rYu)u~ؼs'-yd0:v ^駣Y<97ws׻ #i]7~;JWc'[|JQ3O*햬Wom U~<4'Jc ,0zi G/|va_ CR;o3)`YWM|nKA9W8>܊s_El ~ vNKO?@cxb2kI*egx w2} \u =omKn߽sY_nX\~ ^S2͕{'OP9(ZW~_ Q/XYB WQa{(c׼`rPR )TI֢+_|0I5 7H148_YgVe*{=p%1W_Aɷ9o9 r5U8I:m︄1ƕߓ;?/ÿ>qzyE'Xlߒc ~[R MHߌgGW`^ u"kMx>koV+6fQi[sS_WvIMS~ sȵRF\~.;|KVNॉRuLbMQLLM!S&[*|?QJH׃j3neG@ }g(&F~?0JUV,FGkGXe):J)L/ޠT'7Ho0k #)6Hzs[}gJ%㈩?C.I#²R~,Mv5M!KnGKRF>.E#ȝDHrY/r?9J.4 ۴33A.r3?}ov4Uk恾Qʡ~Eg)#C.ڐklwKpr_v%)nj Oq`/R%&, 0I߼. I&džwSsn*'|i!kϜq`^3K5>h6/,k$? 3y.0O1mh+Alu*UxUvOz;Is p%[WSrS~dӴR޲w QI˟%_u~/%Is]Gxܹ\<܅M|{8gU] IA~8yǁ1:3+,iv(藅 }$?xȎJ*S Rx›ޣ >+V?7SL IZ U$8_"׶F n8e;?z sy2f ;^KuES<. [nK]kT$tN|]9?~'eڨݡ&r+5c}N/W<הσ*"m޽>Fov3jo$q)N+.,Qu];6XO2 nspg%]n!Xg+fCttr6%In%-/"Qʫ~^ޫ\VWTl{UxD6l3fc_)|E,Y\B wUx³^[ W+Q{] T_^\yUg3*)S(ZC~". /6]Gt${ Z]!?d=y"Ɩ1r3UrRE~S ǵu3@yLVqtzHA`0~Biްy|yuS޽J|/ 6%Er)1}3_Mz)uG6/EH>BvC{>W8RrtW0]?&e?Ƚ~ [ݼbto7o!A.6\>ٜR{TӴb;͵64Vrk- lWys>]Eoܶw>CO*|Va2NcΎr(MVݛ{MR]sTY$/r?L'6km8-܄6u7%*z`(-/x̙hx'x\<>;I~}}>3A\V(w}͆l. 8vjow̳6%Y D 䟢Yavo[|6GO]*>'s(;sm•6wQt64t/@(\"[N۶~//e #6`t~Ւi'ѿo~9+߶xM8{~Tb̷ļ%Ͳ7 %UR/O%q)7 Wl ;{r?\tVX_+w\%Dex;$<9K67x5U$(8qK/7_\3'}]&+>]No_r"oZkCĭϸ߷ǔ\q2S78Nxf;v d +uMCx'^ ʒ;c:0@p w'02HG ߹R );xq8 έ77w.C:0.Eo/ wL|cn >) Ew.yH=G=s%u~c߾j_C`V v;"L ܙW3Z : y=3;E+q'OeO7hlÝw!o%\@Q=uO,gS{SݞA~<$Đ_mvNZ7|Tt'.ulAÝ,sN)_m~iHs<0'7_mT\7a\{3yyg3nLyrޝϔ(K\Χ,;Ou`.K 90]^LGzN!*e:;g-lm8~s~' U}^L^ooGq9 cmp_G:b7ф_>{Yyo rCue7pL{n?qFkn+]7JG>xW㳥g=EfB~ڼz-o* ݜ.y_*k3ͻE$; o;lXvD_^[Hֆbrktݑz^dd^T9OEC xkn6_u՝kgqam \U Jbr3?YwR{~r 7R&^2Soy-S_"ioUO9Ε3n5Goy&[w*8#[1GOғ ; _T,T?W 8kѽ%ٴY>56D.\&sV藸9.T&ulBcV|t-K>*]{>r|~?֥/l:OSxo}ҖD?kCb۴'ׇ$tqftFcx ~ʋ$ώO͋Rs}ܬeߣp{o?S$'uon^} ~>.SN6wc}/}3|+eL¹~!G/}{ ^w}ܧJC>s+ҕGZ_©ST=rی=m+Sǎ_7knj_61m{3~r=υ<k_|c9}>s=rc۩{l;m=kW6aNT{ Z5ag;\Yc~&қ\҉O"Y'WC[{m{^+ 48Њq` x+ 5K OT|Ig~.%wNx^omQȇ01^vmwdw x'KoϿʷ^SE[Y>s d^Kf4x'燵~ӵDX|UKF3]s;E4|WwYA#=o-psm:d{'d&_7C.z"in+s:,1ߢ2K|6a]#YGI>Hs=/B`O;`-qSvQᦨpwpنLV&\-KزT-7n9|';%|WFl//}AJlL:`-:`\wf#PUV U@(C0lQ2JG#񠑛3"^7RIN#ɭ8=Gѳ4xSxSKj (4N|mn\zc3_M.,<滊jtmP=As{x|g#^'ɿ}9vUӈ%?ȋܿ!y~J:2H>!>dב*2:W ԗCLd9/(OYJ Gc}}-` J .NStZC뤳r+Cq+S 8+;[\t:SSsAnsSYSA>ۿw"/C3C\{tiӒ9؋pG`0OQ{h{ UY=~k].E'=:J wAm=C~L$er wۯtp8M"7snN^&/v[Oχ형O"igZ%=>*Tgˈ~6iq=:^G5Ge)£Z 8I吪Ѿ}0n!Q%#+eqGL]9sJV(֫F3U5y+*c!¬ ha mKO$ځw'82nXbHX`T'# rr:N~ >\xҾ?w+.m\~K8y#{*]Ve_A7 Wpk#>6~ srE{[ODϏ? _)Ehbx]^WI;מ=9/%'>qcw1ȕK%_+_I'] R7kM ?䥯^'WVSeeLFH4!W6)Ǖopܲ-2 OulE:Me'Xv,.:eN^cDi_EQ8^<޻eJ+r{(Br7WQ#x-#ȽJgkr3o{eS玁e2;DKM%[%<ݟhRE _'_$9p"{%Ƭip3 #S[P'qrIpN?6/^|)$U9%=(9g߅Th&wֺ%D&?LA:+ &Z,v贕2{=3/pEbmFi` %?5f<2m'MO~5ApjXNsZe|Y~⽢-#[rHl ag;JH"*xkCw"[EonO:#wՀs[N:(7n$S?/?n:C[ k;L7Ή9 nas Rxsv?._[_X2_6ropwpiE _1N+m넪i/S6'߫lO5羅N~e8}k3/sٳ4.K-ߍy6!nZS˿}jy-}ϛ{NoH=olKsNEa|Zyo,ÿܧ8)3Ntʌ2DV_ `VƮ}⧀9 0Ӗ$ƟP(6ȽDNe7Yt*|طP26 :)ަDzS3싔 2I-F>osXzg㲶#U+8qYˎ߫.p)ŸxK6㘀lhO?ڼEH6{qNV([pům-O@R ӰRG oHO|}_l٨v?R+Ca>lzc2Gº~S o'i*\tiSy7~riSҦ["m"ɋ |^u ~e-@强2Q3r8;LxtSzrY~{秴sx]~G1~m8 G oWX^]ߕ=mcxVƜ¿q9\wDdҟxR31rM )._Qb3fJ9ZmI֌ڀc&{ !7@9I̝`3O{7g@G'I-g̜`n?:ttgaۄ4O[6ܧܐM35=(Ggr g# 5&~6ZCM _A/q+߳vp3>k 獟wlb蜤gʐӜ<o0VS}X dSsyPamP"\|GU=vƣ9IS-?)|~t$ယ^P2x[Ti^އ۩K;vN;o1?Tpwv"wxy g\*31u|C~#-O\ʐY|k֖ijx '~kB7vMwG#1N|y[4J~ -g!1]浌aї;;Ilk*,σԯ\I :S_&gTU~oxܦd6+.{{/iG /ɨp8r*>*|s:Ć] `^;-浓 o7YF33 {fT}etPKxySoc^Bkҟ7{?nsf=O r SO][?pVY^6P6q_M 5=ء:Z~_ݟlH|~ϹDqn8r&߭=$  +;o^);V(Q{XnJ)>AaE >?~\ <`s4z*{^++ҁqۮko^0e0|Io;rG/9ߧ+swK%K8W!W!J:Y0wL|@x[* ~2&wιF?6G|* bո uj9F ~m'MxD:ryVҫ[^C^~AΛWo^e.D=%I(| G'H ߎ̏{CGi-;P'{a j)M!U928 `YӥB_!F+ ^k~V9 Puu;4mYIgL g=܏GFiO}jΪ5;|Mğ h|ȋ\v Wc,*k/{K7I{](0ǒG{+,w}3g  ބ(< g&xRᗁRyܬVO#{ʑs췟og9c]᧹h8q;wx}8y$B_ckze{f xKr{bxy2WMSqL\؜798N=YU*;Zi߷;)g-ϺgW]u[{^L |~s^hyxI$<۸мstGWNq2E)jqxU{պe ^{r_2oxVr" Oy^Yyi\=Ձf?^KR _SR7- [gWee-xrJM2ذ(A_2 vb𢡄;ݫti=q+™.Y\Rq2#vW 壥>?s_nlRq /Tew:N0 u#$U,S K?H\ௐc_n(|3;s({޶4ºfr/?0/fy-2kmU˻);~r]{t䥜aV-#o4_Je3uu䋐ۤIa}w 'SJryNߴuxflxa'~NY*~YWlhr9<<s=z%=.z1g9"{{eyH[{/ul_@3+>$=\-U4eSǯW)r_h?LeSt-ޯyW'(y~/|We-:Fw 8Bt=ȡ=қy0wlzw֦o Kk*¿kċa[&ޱ?|䏛W͏0SpAG} هRveQZa20K6;~{Tg_3JʩKwk 52}M&J##zJ_o̦4#Ql_>PƇ˯G6g٫6z ,{&oDi)RNOA+=$3rUPri陵W_U= ~ϣӅOcԁ4C=\y GZ1.%:|r'+;@-KC T>4!K) i2\8s60PKyy{`2vToKXLi @j_憧|.i[HBm~f>)۬ǎf޿4V\Vҝ[^K_$c;yLWwc(\m=$|=|<΍uǹc]lLl/n\V?Gc|^A}I#:޽#:? ]lvmR3B.aL ?9+ɳC]I'WNLƉςWLHVo7~סb=]/љ>(Axvcv)gnx5A$HA$[bN"8WiL{yx 7R܎7uo#yiwoIg#;NNNNN|)'cN/YM0YO!70{&ǝ88(Nycڙj3KQ9liF{Hg@AG4|eς[#k2tܑ6\4BWMbm?)BHG4~ JٳzJX+ +-&/&$#szN+G*dkA&w-JD&dAJ;;"pkoAv%鳢8>KG̏O9kz_motK,LNX<e!^ 'nFQ/; UXNNne-[lx9ǀy dX~ fW|nr_Ax 䝄BlFwgx Ŝpx^ ,s5 nXxer$yirIn#C켂ukx:ƌ&?dC>dݣ"e-C7m~% Wi#DN֫do%\ʺ]wQ“4|?5r"|5/) Oz:QxS0CN7oo _΄Lx/\+W)eest&[f_RG2K&[&2d]<ͻƙmtl>9M%m٦יmf*&mBk6sk]Y͵Zd6jm~3LVksl ٬͏f6fmޟ7pLnqK^Q8˝TC~A= p,Un{A /T}WQhFn,U28ݦG'Zr>8ZST)hmg%`ޯ\Ձ3RP ۏ+-J+Rrg{OB<~.o'6c {cqc?p>{ S0NBK,bde:2AM㳀\a> Θ[en;_\“}+ s=KR[_Ua;-+]M wiC]9oKt4ĩK_" 浇1 :=gW{.?|M殅e]Bl;D0xs38-J$#7]h/B;ΝF[0o\zl`k0sVw97S6x N$>2 S^5͆cM⛤ߤxT񚉸,NFfẍ́ۄ04A/4񺿛yOimmmmm?3lk:): BTvWvܦےa܉ C'̶ J>K-U88lx:o׶K˸7O1^,ɾ ~F‡97g^jm+ $˪xM/3섋yY"2p2bOgx{$&||7O[Ox:Ju%+L8&3d;t~i;JYӚx R2"sz*7<.pR+dn!nmÓJ^} ^5§Ǩ>qo1~ͥ- '-Y~- 0uM#wG|uǷ`|Kl42G1JllO!䎋򯡰>T{lxǸma sg q%UR\ 9 2489Ғߛk4@؎UoWjAEdI>]s%C1n+ϩo`3俖]MQyȿ \[Q{Gs{*ׇx=lv8pLP &myHη[i;.ԳI;WEsH:&)T (^!3)mĶ;4, 1mkq d7/GzKM 9K%҅bܽ]!z{ 'IIȄ,Bw #K,R>Q8Q¿70f=([r)Aᾊ?xcɃe&;TaPnb`=;-Yq纟C~jtӶL=_1r{߇'z =r;:=*cHB>i5~G =9OQǘlL䖶*>Sw3U$wHgܓ=M܌z^$\f`1OUy4_\ٗdwecVz ^ (_KTe'M' V߯.&~7ÛPުbxE~n#~6^!3+#&޿'._Vi$7TcGw[>U =϶pht2? .4_x}"[{@2[[DF}e}?&^%eOe'yemzr2Õ𶂟[^;js>I)gwšǔ{?~>'fҟXs?eKX=I0fM6SΫ(UGIXAO(ҥ=^>'?>yu8 O~(i$w/~Z?_{S>e?guxREQx:_ydxf _Frq=@w| Nx @o+\a9{kU/jG[%M_ RwDXgkՒT*v{,^}n aR+_ׅ\#}MM!5I%]E M 3&[G񧖺'Y| #ęÓdk^GO#u`C8kiyMm^2#&xwg[|ru $`nbIik6}mW p2W2#vJ{{_g7A~dV8Yg3'oyˀ$-O{J*4[ z 3mcp~nq=k{9й(|ߞ{~@rG&8.'1f/3F.Ge=iʚﱳ {r.3tA˶>Ax,'~29Ɯ:,Μ'p/@ NX>o]H38#NUKDx4nR p.*!ϫw⣜~ro?qR;5g\nwY#x8s23lE|?}ڋ88=L>)q'&;9'ڙ8֖y>lTx6 ?N'*~2elS~Ya(x~m θKLn7x_*")yxQxW/#pǖഋ"jhd.,gr*%NȿRh3ܹt o'?DOnOR;mۗK*]п~oTtG˙c'p@kGzdPb!qs8(g֝YnsKX3Og(a3wp%\LoEITɗ%\-+vkƟwdbq)G(]=vݓ2y&VI)~7G^ӿJV.3,kAˣ/ <|9<ͻ8(WP gNӔ΋YҘvb4~s{{aIIÅMX2 ˛#{ %!_)_.R$wAZ' qެ19=W/ {2\y39W𯃿2U0XoerX5S|]5|}Ӧ{bi khsWѠo Eo\79+Cmc>KڑL9cul;kt*݊uH?S%z80^ E [7ŽfPk12; N\ n*k?P1|@dߞVavopZ=ȭ?$<-CSMZ;!+ O棉8ߩh|!W.wiB퇤!ksdzIAudYxI埌a8J:N >YH?]Qsi_3%}Mpҙg{2%o|YNK򯮰M|6˶kgLe| ;Cn3X}U.W26aS|/MKd>}+Ϥ.8qIv>]V;d9z.e5WWg||lO wVe"E7>P)/WJv' 7e?+n4("qފ/dNE&odOtǐ \DOĺ޸hlRF:eK;ȿKt֦0gG/ݟ%w'nIeSa{wUlENʿ򿳪jT9494!-". C=s2ބDŽyI~ݏ_n1޾TV)kgWIw:yxߟ!nv5>S6)NzVMoHi(}k ;Ov|lv{&HZ$W-_Q@GҗӉr/UXͦDdžWx维5KJ^w{?.i;j367Bjo? _K{;A*oK5kST7eߔ-j9-_Xy IOuKL;Wɐ[8ĸgd=j28 Q3\0?]d7qc gKu?7A]x2exܾVXe-ӈ{ 7Jh3k7d~s.!kٲԋس5]x4`Λp,f5пᾓm>C~~`{ZO40O{Vw I2;zm~gΑ۸Y'Juݚ*&4LUO2x '׉}YNY;iZOE31D$ ~$[}q4Vo=duMm#$ gk!91(=Ǽ?Fry#.jܰicF`_a嶌~ 5 Gնi+ŧ 8{qЅ.oI-|erYۦڑbהʟ?M=BS ^7\嗈9^ NZ;N8; $I'Zr;hlJ݅˷U=yfڶCձ831ҝNkAb3Sr(J ?8:oNz[ɭ?Z'|MKa({&_Ÿ;y$R]Fi@_?:;e;r{`Nr^q([ ->"FJ5ɿ7 v\b^ߘ|&ا??^|tEQy" x3b(nܝA_4SXg񪠗#[7qdcl+;!@< 7Ma?^㾞kx^_7yݗfUYnC>W _TzyŔ?P:_I>u8w<.DJsy/(}Ht'= i^6*8kdUuʻ4Y'}sNkq=HFvn~#;}WbcʢV%'E6_7aS0ׯ9e=0+R`x56qt$cNџ=Þo6>7w>ꟽ:0xo'hu{"{عK)w:A7!~>Xm>4@z?]5_]B~3 5͹s%c/ eM;S9+ ? GO]29NȼO >"xq|'%|9iʃAg%4N8/r)#vBϏo*xAk=Jf̳-;^(uT'!Uxov(?Ok]#p!g]CxNx/'ه0GwG -x'd*=1 0?w90?/k Vq$BNiMĻxHoɽ{ߚďr-w x?()x'M}1%"s#/Nyt2RHa2W˩I%`Ǫ%§o>B'6?V~$C:h[s+"3|Dq޳UM ;Ťs>}g֜'Ť9yRLUfC,yZ|yamy-Q`k%$/]y^ϺvoJ s{9^τitkVd&[ p7"8HwJ}`觸+ O&"ߝ3}]نlÌ/}sنن \†%&zw'\mk^&`$cp|5E.L}a k w"lw纼]!~k ϱS=In>9}N6{x=)p<MfO7;0?Vs$I,\_[5QgltqT{ /@|K6ķ"VwQ'+9b?DZ:~Q&jcږ\$нIn7~(݋Nz .aN.hf~Qs&,Xxژwqܛ- )CVCr|7P6!NE'+Cx<,~W2ŮƴW|dn(3n1^a}iT eBz 5= UE.&~.난x i{%J.A6v̐mYXEJВcm{˫^=rk֎=D#yН 9qn0Ugqi =2^ȿT9Ra=+' sR˝ o^*$y[~-PYsZ5x #Ď[ܲ5NTs3[x𗃗s欄+C.2[,Ol[WJMB;~Yh< `l_'}knmJ:ٻd2'DDNԹhO ˘ӡೞ(~i[m.-F&|O)cKʹZ \g_1v}ܡ&cpS.r;O֭ W>W=o8ӂcnddR Ǒ.S8{&DB'%K'7==)anS\؃%H'Ǩ|O.mlj'/{N'!Nn'S +?Żi? Hv0M{$.T|)5%_6|6NɔK;+yg pmM܅Ǒ\a& xqݺ4x!r B>Mx<|F2*;z:TqUl;)^߻8gT5e5e03݈5`d5VYo/-zg[L1zW)v߽mz cY/bs8Yoae뫬Wtz7+wc^P|gSn^=uٷ.uZu(lIi# zڰaSh4uF Tx)_s7Fv_Iakt&ʾ O&?KǯL |}rnS-4Е%*)|]k ){j>sm:'yX1~ǬΧ⿡(ZWyC](a\_> [[zZ|&>[_C)[Áyy?Zo=|3Y:Iro0XO x }'M/)zI؞?|y'>ִ{7K-Y ?ݩ"'wqyyI=ͼy0==!=Yxx',g:Ru_Lz72 6ug} {86U_iOy-Mr3E%>sv~)G=*gsYY|Ǚ˴o[뻱fL?Ez=+KXPM{׆gN6o/FI F4+N psKR -AX8eUr_”)+LN^o/Di+ڮ5Jfͽ̺uvgGg𽖽&meH;](,M?|i/5TqxIٳNE 'Iq(EVش,W&wLD ;+'.2M۶Υ)9Ør?`r+>ok-x*{O;KJt[mնQ{Eݩ+"r{Uq_GPkt8)mppӕkN~rVEM{sk Om)L彾UAĻ \"=-}΅ }I +oW{Ox]ue _at i|v e}c۹ &I _)?OH'}͗jq>O;";D3$bSFwkT+מV=\}j}y?/+옃~Y{"ɝڳ{6{v8U^3דppl`ɾ&-9W% 6}Ho <.v*8a.5cB.~uqU=+lNQj*;UwغooU_kW CHٖwkEɿ)+d_M58cSG,wVZ~fl,cKx^ 1x鉷Aޢ)g*~?)|͉wL?wm'iCҠn1b3_57c,[=Pǔcw^{b8Elᾍp밧3#7kI^x8Vڽ ۶z.m?,;$w's8Y}|EB*c}Y7/,McۋreYYyo#^6%,?WyUrϓ\:"::@n1ɵMu ܲ(q^x3[= 9p_*oJ7#z7@sW.O7cp.*\l` cg>5>&e?\pA^.q33.'"A2_%\6ݓp ;7ȞdMyd.֑쟃Y\c88](;yWE9m glA`Nn}'0I7=)N\^&wu%6K^Vzy'JQکuFWsF*6++Goo<~f_$}=9};ں0 ˅5 [Ol㼻:خ3\^2f-(xHl5#)زsIzM?k?cX~}>S79ryp<3í9z7Ue|e׉rg >2f;;s=rrɎmkUSD!x^/\P<^q %>n{NaϿS_Fn󛹾k1eGG.RDws CF&YZh=:_C̘JX,dLӇ1h8l&w}fY>̞c#|lDç g> -V1| "q8s9 $\3 p'\_QiJfӅsEXO 7y?fYM޿8N#nxֶ3;sbftx }dM1oƿG[3oY%#W|)xrb(/g%| #l>^^c6lcf] W׈q.6k+(|Fc _i<®u.Ks><TSOQx#܍(>Y.JL,&w+:Qr_.wr:r'27Fr] #w6Gڸ6R7RadB]fGxPF9U4~iue.:ӌxd]V}.#r^r0rg5SC~*8|keZn<ൾ x/'2= @7aHq-lyc҆W 9V*?;=~r(Cuϙc3؄KpJYo}D=u͘wt>ugŚ<ͼQOEޛO|f( 0<ܰ Gm݋*\o},dO{Vp6>{,mXr?.o3ۓNQ.*^UUn_3/G ӐN#iR騒&5L;E::ˎ zèԂ\یȉ=@-u\CZ]7b}IC[:vJ:vy ȵvҍϱ'99:uT͑w=׎xضN௱`5⁢ԣlw$BضQd99_G2?J^y-HM'W*tgrN&gߋV=c׀_΢O~_s^}#\vONh_:U{^m= UdP+sUIߋ)U*q _l۔bIdϢJ]~8s=ϛl|Ku=q4L%OA8C;%+p 2swr 5 $S`w ʽX{y׶/7pi4;Ex;n[i43J3xoyoqO;[}SoM5̸L33,st˽1UKSVO$vΔ8XwG?=֬S%tH*UfܖIq۷cze'YP==s3ҹhUdNIYBnU\ze赊Hz(=˝8KrsmYO+7nMLvl6]rIWkrU n%;@\厸gXM{{]wme[{x-37vK;i@mН-%tSofmVmq֞ ?ſcH}t⭠pRiSr{ɭx6-$}=LJ5e>woO A7}6n[U'HSM;>!|\n~ ޚU7ջ+2?8H?!^ψn?R.t,ҦKg3'S{: P zN]#Cy2;Mr+OP>9齇9w-l~-Ny1іB-D[v*N>PORK};&_),GkvcJ_o/񪲡L}1urTCI+$.'v*ʟlI Rx\*+l=X3I O}\J~ϰ-on3=+S_QQ4~Ց|+std}'MBlC8]ayx&ɶ=7ٶgK&ۼ|EqUHmQR4ۧ(SZ&>a_`;}q,b葝2k}ƒ_yǚJc|t _߆s;ab @|k`u@ߩߑ4DʊAiO\犪|1Hr|g8N.79O*Ηփ$E`;}Ui{J}yxT:zυ;czFyV7ۜAv\vbНc>}ش~.U)y?zc?Sxϳ"x/ 0[DZ#%좧O1ItH6x`ol}¿[}xT쎟y¢`wM|RJg!)_wjs?$Uz*tRFmA\ѻg\۸>"|/w{lߥln(ޮVw(p_xp &7<4e](xQGSws)cЃ[IO]g6t'N0:C;C|$-r Re4ϒsmquT\Kz}*#&a]Ut`ᆯܽΟ籯YLgѮ$$?q~YFmWfK^!ډM[{7- B/6w[?o"K}79]@ś=+ա/ tY0W :/3;@g˺ߓO> ;ҿtygL`t7 Lԣ0_I~a&n|Շ< J*NW@_>x_ \^nwzr d߾}:WgUR~o]59<n*ːWCHwZ꽮9Uuŗ!eBgA_~-eH^\w@wSB9{2ۻW؜1z·Vm ii1;=C<~O_myo< NIyϻ:k?;Lw}x]7a:0wu=zYq~Jo,{ {ѓ㝎g& Go| [\~~yu=#w |o+}&|b/\}a# 潳4n+~Ba7B!&}'A~W V-_%2 Xc+S?D'c(̽lk8|?z+?KS+#;g%滷nchןv_EݥQ'M/t@7~Z~iׄ  i?ʬA{8xyly/~mHަ\FG:rIK|g\%||Ej?GcNwOn6xZnʷWCMz~QyTz7tK] r'ni)A A6(:'J1g]1uA ggIHc[5}/RrS+z ?L~/i_H=?OX \'?̻W,({MI>\~_WdƉD/с=[q|.nX  >MF:5~2sTM2cҙ?;$ߴ2y\tD\wG9xG֥3=wvczdt>]$#u,Au>~&S7DD%s2z)_)H vn?G5|÷_^:Dx^aƅ5II D%޴=cs'}a7U7csV}5ȟLE`]Ӑ?JtK]-|߂ >'.>x}wijXų$|e<%҈oTP[QxQ^ R;<+w֣ vW32gC?_9F-)9H?O昻L-:l᥿sgϱs\:ǜ`=D4G9VZ==g=N"\Fue\e;4מ{}p5ԟw טd(׉O暳cxk&$ogrM/ڹis`_O;92&w y;.3;l)O ]eLG$j4$I#CRLRufx]`CP6D<;ku-`>6w; x},kr<ToNLNrF&\c;Vn|{/zсqcufaF&@϶y #<"vn`^S8?ߜu8Ei.vp.?!x9!ou \aeR B@'g'^D8 )8kf'W "I:'|{~ph'/OQa^h蓊B{1*Dž{[cym(3 MD5p2NqGx4a\Wji@4y36^fi,3glIx0&se[{D‘LYe~Ez(}U+гܑP`no^^n{[m8!6L8&ɴIC,!'G]u7 BX WNm8>OL0]f*9aBOj|3+7D9dm$햰7xrfS7 99x'~6!6b,VIop=}#97Gr0,͎їf쵄qȡL9//7Xycped=Hr^\n›m[ܶ/Nt.Zn`N\.u;nr\s^n*Gy_o> @m |pg𱤁£$ '\! %y/0]dͻLnƛL\OQnj#+܌y"V mq]a$&F+`LCOxh -nIx'O>9IlV9N) Wr/qj?;AO)Ǹ&}Z"ΫM}>N lN"..]g;ylg8O3gRy^gΥ||ufGŀ.}1z'{r3 Xo&g!<&\D4aIT*$cxQ\9$ ~j Amz̳PqzgGJ[[rHr9G)?xlb{!y'%Il@Y_8p ؟MI$/Ԙo04>&H)0χ Ls؆P/t7[0wjiOK\oK\mF\〓 '"9I1׻?7CŌw(%-8=7[`Nxf3ss`{Oxf3O@x6c܌y‘[tχnA]-M }KV"\yː m"!N%[0wYq ܒO; 8LNF~:8>µY~ * 52#9)|k#\D57cM/rƏrSQk?"%6~WC1;VTTGV9N]ヱx=06!p?Ps L;wI?PQ}@ /7 ̳7C5TKz/%P@B=\'qBYXǭya+!/p `nr^q[i.-nx j5:p[ R6 NoZ۬6H\/ρܹp4'OL%7~|emfmΤ;gc;IFvA_(s|⃺w piCʚ8Ҥ' 57{SI'}#>Ii;IVbO}6s{JlwjPmvcOnP?=K4~#i]Sqޣ'l7va =AOriS~* ;(mFu,nVo7>+6m7sLLcoݳ!?vSv\; =;fff~ôov:rp;0qx`WINNJ;4z|~;{4䌥'"z6R8;흅oSFtf[ 8-esPx7Ϙ$o)w2+.y|]Fve.»xUp{&k*cϞq.;/FIX[wmtZ^caa7?4p\c$=ic%<&Z>᝻z|n#c@R%J+=ϊmPOpa?6룖؃puг g>?ufu{Q{%{G&4?pw+ro%E!ߒtξ%^텿k{;n׮ٿ׮D8nbP (dܽk﮽v )s'ڹ{>9c*407Gpw)dgqof8u3}veO*K]t9FL8'̧͋G41%r$/<-yyg[׽NP=5\fO5w|熈o-(˼ z<4 gzї(ΥCKa;~=-NPzo03%t 2 |R3ЍRW\qw ߵ|!81Gn^w> [LwDbݫ:a6?s-.J8  .s"9Ye'ܟ}̟v?C?\#g! OCzӳ!/D ZJ<8qbz4g@?Aѧؿ $;#Xk,I_y?h|e}VϦԗ|{0ɕ ryx:(Q3pk :Zi4::}Amq=qDٿ45yßێZd1EdX;"?YlPިG=A-'{x|. uܼ|j=DUtNOx’7~򷍷P>?@߶2Xy~61sP^ oCߧ2)j']Sk4󔉼o'nTq/ r&LZׂt&g=yҋD4n6zf,jaawQ _'j"_eO^j࿪otشӧAw-D= o]l>-E_ϙ\o$'$uhw}FzmO쳤>yI|x=bǚp#F>bޫ?=6 ''n?*?b{, Îu8'w{L,_+9ŇKPgp #g EF/Yv6«o׋o+A?;=}vtg:jó'r9HdS.ǎb߅[s)V8lŜQS5ݿMQNSlϫʢ{/ )\W<Ǭ_Fac*][JMt4(P&w#U]g(I<?%{V ~/Jˉ/xր }.U/~~_5;[##Gؠ)vF+&R8l{=i%^N3۽W^N<.tW>'nK#=)P|C'ԩ_Dс?BNtyk^9!!9|܎//)?&N[IN)}bo۩|¤^=19xρ6J̄&82] ?alh?ϨxT}% ?/V?i7S}?K!lб¶08L˃eo^2eu |]K' 'eP5|l~U$m!njL3^;<"!qK"}kʹnrLkS;iW|2bIv5ų#c=P@8$>Ϙ ɑ;FʯƳZwU,P7]OZ1}ӼtW@krt ]}oeld }7>P rHOba.}12fx^}@a۠W_ O^:'L@EL/u=Kax9!*6f8eRa!2_>`On||(ޯv^SG`Svw_S0K?I3[12Ww2&{Gx;e=agO$D9֓Ou3NS-ܿ|o‡N1SсՋSf^iOcNb:`w͕͘>85qڌG˧ /s;,Fxdͧ]k2N{4.{nfٜLg =Yy|3 A){؃xG]3vC!mOc?ُ'yt4S9YWhf+I!_;+g>Ž~̤qD_Bc w3 ا11?5D:$lo<ޫ{tP=o~ A|U޵^?~'3$hx#xʑ}.~N7|;yU#GNw,U yL .;,p&N@~`EӁ[r6QI"pޞ=kOL6)׫.XS8#x OnIx0_EI` ^FxϲL—Df$E8;0wzɄ\Hy.{_4og?ieτdLVi.`PX_/*x>Ew[bdߗ|C/Uqwm)?اet|tA|WCL] gHy9D1|ȷW-/ xw"?r:9M3q^PE;~-S~hdS}FE&%noym/qBiUaϋvlhxμDriK?Hɟ؇ρ[B>DgdX>㬄]{Ho 0E_5%3a]USs[WwɎd<mO[O[MK.#?TP^̌=HS^Z7''ɍ]қ'K =BqЕ o+a?A>g)64l0+ޔ]=t{6 lǵN ;`( 7.9+)C|>0@|JWLZkҳ"_5(N9G;2ZfpK:@?|hw_[Yۈf-:锦sW~[7=# up))W/iqWݾN=Gd_6ߣUj)1ʜ9K465s2_#`_Q;7FJV)xyFw!3sAe[9&=_!+陇hցi{׬WBنޟ%Aמz_8Z2̻a2%9 C۟L<+#S|]Bx)ӝn3'œA/_t#="1O=޽"c 5K\ǞD 0ѐCggMO4qS43 C)Vay8̀{[DFtHV>?7+p[0nI6S=#s s^|| a.[F}|v=/k/7="+]9'kpjխ=f_[)DZuIj| ٞ:鸏SYuGՆ-{2d/BX<]k6iZ]"s[m_%ShX[}Ko{AJWToly;eVH?sMmK <儙3n~AJoC#r/v;;$mS> w=x냆{C^z*$k>#{$@1DzNfsy؟r<(cD}n<ѓ%SJx0嵽&>pwL0m: }1It[ gQ22I1 ƾF{C4ᘫNg0We<Ե ,pK oe\.{3oHgfD:崵; 4: Ad||LLr(aC{ Sޭ_|w<^;ii=E<~CwӷxK>,ӷ=JtX'L%mAx0gaJg'~sI2Пr\+}aF|lS䝁!xћȕtg`.O5Uy}$ wA_$T2zr;CǐwĖ/cmNNَ!Vć[!!o|(Ϳ)ӛK0ն-ē#e$ K6C+$q,$NMYkz |Yo{ah| qO ߯bii5T|%lj!<,'st.|,@U>yG,Nq@/Ҝ]#`DzcAW7q(AZD_/CL3s[Lnw =aܾ 'Ώ6u}o[>eJ *JzRj*drXs^n%g$M^˅n`IڎϿ|uik ;jsrMtuZ\c_Ip4<05ބN|;A膝2at:N=Bp㋞Ds xڬ ]Cc߄.xqu [7t*qe=߹|Oz;-laϏ҇oe3A߄Gye-Oωp[M?Jea^3wڹB]?>^466AfWHӺKIDT >}&2&p.v Z'iHӎ4xyтmI=_8N/_\wӾ~n2'T#g')}>miQ8cFғ?ʔ9#*^Irb%$\቎ٷz Eh|TGs~T 1r\OGY[3M[[~xEo?VLǁm}9_eǙ)(`5*o8=_G.ʎ!yo n+ w>42v:w_W¯*/)­-NURѿps_$2ءg+1pyO5=/5~Ǟw'SOY^Bp ஞy_8HGw$f'C鈼/%r_$ߒ|Ƴf-2̼ƔhS74c93}-#Qw 1o.zAtEc pX֢B;P |#TtyǘC'9߱݉c!:l=~BZiISwCZ{㾀1?)@893cțzTV9Ls&k?¶7pj54iziB6f|oz>o<— |3=zJo^yrߟȭy?{2[{$T=ԫzry}R~툞, T^K?|<|{n$薨q拏k:ft_JxytutI{\ny1^s2D_x7T*jK$-&%W>g9=LGumKsvq8lNQX޹L8s '2K;N|`!w&:rʬMX>x_i ίs6/H|&FY~,1!#(i&K 8ϊtLYci%[` _~`! 'OӬ ss# ~G"p2)g _ՌŲY༄󙂽 '>_p o:|'C {ʿlnyu1g6˿bbk5:${ > :$L6Ul,޼͌ w(_v9=(xsb3{\6 ž0wL13fe4p\U\[<߻_?v'F/1B<4?!K&=Sm||w&?f|s9,g6ʧޯ 瘲EПPuT~ó:&Q?y÷ԏξ6:;:Bτ_n`Н 1/'B7_OWq,WxWv;Ϥm$讪5pϡ! r`'ɳ-Ho9Hy˚cϧwۣKFo7q>ϑrڛQ9L7taij,ۤv:{ N>rZCE2&#_ol$Ꮑ&:w=xo+^ZysA_ӂŌ! l;/'J/ҥob:0Vxº,AW̓/\F} ?"gXnCWJLq;;$|WF3F~+8ȏ6w1J@u'Ik#Uf땣Z}UO$+rNxM+A顗fL'@G3ybQJ_}'?@sgEds>w,yGX(9B0c\Nt2Nigw>;*]˹ zo ɜ|gFRp:߬Zޣ%~ޣ[cp3EOUX DmuW8_++#k!=MᜎWuLߜhz7m33593!{e͓}y'O%>DB.Y{̫ٞ[t\w~~}4myh , <}E(>أ%$x>H#@~^gBmiwTS/ U:Y|veXOtG*^'Hz_ t{DbџU$0|'Aǣ?7 u?o=ZK~%W$)9'!s{ ˷)뒬Y,nt{)ZGƷ7ГPmJ oICiY: ,pY % զQЬxMyTt+k&M{vprCB 9#B(t)Vwz)IJžNЏ[m%~_(u<> 8kܶE/I#ɞ*–haKSyRѴR4] =c{*>+l.lG%&L0of z ͦfA&YQ{n(}T7(b W,b2Y,͞m[O1mw>.b|aE> ?_9+S W滍sL. Eu@:͜u7ç󷘿-n1^W:,n|<>3ϰ|]i'dGdCۛ/ U{dTNۣƯJĸǓ~+|"+%}9W%9.H!jT󄊋?H\"9|[1ғfd:*m|?ԐtṀ@]ysIp90ky)GxYεLCEp@=w'Tw]'39,G=1Od98%saS2 :\w?+!00w:Nr큟+sSSg07K*=Qcu+wܒ|W=oȿ/Fdbz}ƫ|%xyOCwWw.;k9rZH'skP1A9.D7,p'3K]u,izt]2%m:\7||{z&6^d>+BɔĖ\NN%{3cPF4W?>4W>{ ۙRfY-trz$*-U:T:T:9mxNޛ6*C:e;^*-U=*uwu_ ud?麝t; V˗44): -(a)'m|Ag](cJ[3\Fgٶo#< O'\k |T9~so֢zȸ#9 9/9iV'N+P:t TE>MsDn?tu۔뱃~Veϐ޶?qWd@e>徚̗WJ-_kܝM(:__7ʤm7nܖNJQ9{ߜTrx/ce';8rg3n}\cXz}ׯHye ;RٵNۥw6mޱ=:;+g̪b{A@ ~IRic`IG|0S݋C|fol. n{rwW%$_o21)eB곴ToMۣn/v>Y#gpy4dU e@1G@oVg Gvws&;i5N,n6=c+]B)Lud=%ߓz %_tttZu"X5>|U;qt|ݕN]|?wZc WY*cx̎kT *HrӔfW|u]?9-Jf3@_Bۮyb*v* ct|Ï4{}:;}w3;vRjV9q%8k۸'czSlC+z_dz3[7J  5rmeKEއ!rd_ ˉEKV ꋖveo7ML?xe S*J^`t|i8Ļut[A`=[mΎ&_k“T&;)ϕ TM{=.=٧8cmī;=P56w۲w*Au Z5mq՘7j8ZUՈ0g‡R~C Ynqc+p{-n[  /(\O*³S:WU%G*OQM*T & / W8U?(Z Wgq[G+E WyW\>px)Ÿ+Pc 笯ڦVe p](Dξo>2D߅"|l)!“BO?BB"=B~5DxZB3DI!/B?C mBg!u&C>/CЏ;Q! /"|h?=xgCo͌O^Y1r΀}MGϩ$& ﶕ9' co.uS iП%ПWS !/Ha 90} ӗ+4}{Pr*Ju8}Ӗҗې3)d)_Lqx2@O<GtEgtH) EI4U6з}N\(W=P䀬O^Otԯ8{V#<B(GI؅C ,u"ߛN?N⠏}ЗT/J9~|㓎gx xBїf/U!t"/*rTVTI~Ùӭ^+ApP~.kJ%ߒy=YOOT-M+ߵ!SH}H]64P4LG>j!??bӰ;->>JEnZd| O)Oîbӓ9={6#eԋ}iUo*m3RC*Vo(~ T|+@P񴅬'>.O< U>î巿'G~(twQ:oI ]C!d}xT<gZ 2ޯO'}??T~>i姿ԥF&*A宄P@FMEjO{ %0div.?2D gt7_oL:/7<'~c~JߤtO#zIM̆BO7@Jt _4iݙx#gtI?V3dxxާ7y7YYJHP-̛B/$Rѿ>UO~ t?wzQ !H#Ր5۠ۘ}[, g!-*?"Zm?mWHG?V&~VE~V=%2ѣܡ*},OVL>G~(Cx菪4u>G3x?q> 7H'}69Ur60#.dEIJ\A_XH*&W:O!:xx3 _QȌ}WRW*x=Q٫2Uz*܅] o9~ VVVI.ML]~%Օ(Wo>ҊdA|j۶2PrkwާޟJ>yʥ;O|K餯9U~J]{V>BVU󨊧Q*/ϴ}#'TO?)8dCժm74g ,+&+=#LڝqـYF9W o/jl[pyM̷r61^ӨBs6Vsg& -o/|{&nO陃#gG4q,nlgz'?O|gb?=S;gFг=gaEٍOYY?г8=Ks>=g1NIzbГ|E2 =YzVFuҳ='ѳ&=g-=|az֦Mzqҳ8=guzrOO)z>@W34HKEφwz>H|˟=YzlD1c{a6<i2(=4nhM1 mm!gS{pn$#JU +ԭKa0o}2Rk<\6L5Ž |v䵦,N,Ω ,s۬4%'D5ŝF?N&5038ODR[m37g nIOiN&&V [ӶD"^5ea6:0 0W@d:JoMH \.4sGR/;M[k*~W)(D ?S ? ?c 7RQQaRA*@ +_pk+|µp +\M WQ•p+\N Q¥.= P.pE.p! *\@ S8yέp.s*C gS8Yάp1 G+p +I )Aa>Vx3tRxSd')r<_",߯bda{E|>i {ffc)0MU| Fx3^6iqx FϪn)* w4CWҝy2Ҁ jO|g^0M<]"_gziYN=@rϼ`Cx3 7q͐Zo=Pxސ}k̜Mo2{w?pmp?}#E (?y}4?93x>743CCffyM>E{_R4 *h}S>~s*F"ד/ˠ{1.~-ᎦcYq>{QCs*~{c*޷$}L~tpDaчn|iևu} WTl7ھdzWI~ܭ%߁#@s9/lv.۵̈́܎ʶy|/jwK:^U^_7զW|BU47=@/sxB0puȑ{?*CǞ* N'w{> $E`x'Lk-l>xt)+/쉳')G:7|b;Q\r&R~ _l{@?Si_S85]ޠQ'%-YongoaȊ(pCHiXkWDO"t\>e^DЍWz Fwyj&n-DO*F:n4/tTO9?TSA^;FOu~M%K-e}A /~;}[kf|Ұ1']"\IXPC_:ێWm]OK࿙z"࿝h6Gfo/ve L\eTp]{4+8|\>bVyb.>eG_HODŽБn \'߃/s|zV>kzI|}SAro7nJ/W/kc_H_ěrퟀэWS{à/}2g"*/F]==!߽EsЗ ? 2iENhЗK^=o_4߽ +G$/'g[{xmWK \ϦF(nZpSGwHݓoZ4]/JW iRO|'I|7맑/kF^囲4/ G~ٛoc{*a~J_R-dzDW!ķ|ɎŷԞw?߻ljh;Z䕎V~ߘ:Z?`WGۿ_U8w'+f'ۏ?owN/k;پx'elڝmԢkzwζoXYgmm߳vn{ֆ]{>~߆7}ڜ߷+E3}k+vomŵbmc]x?v.VbmEޮVڊ]v"aX[Qk+~`mgX[1kv(|k+*vnV fmnW᥻[[\wKӣӺ[[7[[QO[k+fVDhmZ[ƇV ڊZ[qCk+V i̬VimVڊ>ӏ{?3}lmE폭h>a?rnlmE^V<ڊzY[C/e+zY[qx)ڊX`m_ VDVok+V,mmšVD~bmEOxk+>ڊyXڊ>6Jk+ڊ?3{S}-n t*|U,_$W)|RO-ps?Vxp\T#g(C F Sx³ީpS%+U&)nZZm%,HC7Ck?6DOZ3>tZkni_MN#i5/5#8xם4PZOBimYw9lSRvo^ejy &Sk85Y֣4|DiR`]4+o8N'wm:ke)Z;YtS7a8ZvYo#'KYfw-Lxײ|4ֲgB^QayuY< ?.(+Jz׵`zk#iPS,D|uՠ?BBt'Ө1ПJx׵LzssП Ou]w]{/*j F|~xƒNZ ~gvpxNF|0xԨj"I wV8y_4MW85}Z:gN^|J9Lyk>6ʫw3rH+%ә^Y+6.n}{ay/F;|$eQ| 4nI44E@C?…=yI^s91ʼn8br8&9"hl}/IL%ڻ%,yHjeT~gjG),u Y>̺[ؘWdoq\Y8'@WUQe~޷BcD_~4V6}'MF໗ ܾ mKĔu@8 7;xYҳgȐ(m7,$y8c CmIJ)k#Гϯ2@߈?RXA'T#9tO)A]c;$:?ƙϪZ}SO佴Z{ۮº,$NicmAR]gA#7V2dнBtUZ(|˓F4oʹxCƣG}]޿p' ƗB!KDz'1|=xv|z!YǮ_xS;} ){2n߭EAKIS}u]x:ƛ 'o"O)oGzK$&,w)iS_6{q3Mщ.ǖW Mp_#G#@1dYN.5i(E8Ӻk?ma$@t~iC>4919L2|\#s|FrNL (8_һ!"1&/1=]\v#Њty蚇t8F{59ƹ2徐+TɆϢ>dێ<#48󊧿3wK}\/N6ƿOcNQǔ_1xwml.ߊf7jnO dO\.xPDVCїTXO)4>DSR틤ϛ 9!IJ: O{t =`f'T~z_R_x<7ϏξSAF9-\@7L6eI)GdJ9fxVzگ5;gs~$6):5-ݖ>t[4kCrt=9]>TNCӟleyc]3}{->dv(ޡ?{VDay9e1QL"JM&@OaS}'>A(<6gݪˏW%8t^q⺶ %r\[4ϓ0ݽ:Y:s>NB9=vxZ aO$w{Kzuuqq:oC[O\ѤAeFu po~[^Ϩ~t?1#vyFLvጴ1=3m q^Y/HRr8` cRӏ{3Do@\3Mx"3=foY7ӵ9+s>pY+^^Yl Txc\O6"/xBY"69g9X LyU[w 3̱!4ܳSE 﫧>M'}wJ'} M'}f@C?'po`,Gݒ*LܳCqOsKZ|*ҙVz"∿𜀌ӛJO94D'!{T<%"UiS!2*a#v@2$>y4d~;'] E_ @_Ir*vʫP+BnȯWOEϒ*d]&kktIGxӕV~_=tZιq5k} Z`>8'IxIc_=+|H>pK?ޯ>*G _R>9*|F R_O*'>Nv%a§(2z2toII'e$ RԺlqoO n sQ8>AMC=6zccGn;.1ߌJY|+t={C Oou\ T8fAW2&,C3ylI5߀ U' :-V &LzOq'*5JN^V|{s,*2 x=77"I]gҚSG/T7cyNq=v@moXE2/*GNn|yәmQxf,2;۪nJzlYxtBItItNRC^;W{sINb+3an6"tUx¿!Vu]Ӿ,7ew^oėw|}q`^|!=_ɼ̩|̥Tܕ+WdH^j/|=C 6Ϙ2wOܱ˜3fN| - og DNOCrC#e/ ~=pj Kmxr,utR[f.ukR[O.em-rˬ]f2ϗټO[fJ?4_]f\WiNզy6{~iKid4ɦ$I6ۓl}tRo6 q.-xW+|R᜿[|-0"<{\W[; r35r{Ӳ?+}C#N|+,w}^yc(B1wvw /2 k!ߥ|=yE!{&v7J#8)w{mp[З"> OPxGδ]>~wVs&yyK2kٝWuތN2a \w&QάK<׬N$Ǔ-P^#A3{Dr$4wmfz"I̞yϝF4]/ܔ4Gw{W!CХ!Ӿ߫1[Yɜ1^}W,!D_xZ_XD F#Td_vFHr|O>6#'(yrH~o?V%Zβ}|&2=py]E/|Sۇpddrz.ǡ}^.:Hztx݌˱5~}uza3$^'3;wFɛ|օ[m[Djjxtx5 <.0'|- DN"۟őkw=2 jcF߽<>.AQ ?D_aE;_56T) Kpo쏰Ͼ?!>_4;l3{x2Cw඘@o@{ΐvP =278ߵ ymay[L񃸺iQx>~gߑ??}N' RgYgqKUǜ;[g ^[>q9&IgJqγ*WU:هtv~[9H 29w;Q)Lx>g:Sx'a#<"(|\Ƃz*39=yڔy|zgOzr{ӝtFӳ =gГ̣鹒E蹅Ͻ,Jc$\gqzV.) 9,Iϼ.)CO?픡}J0WQadT4BT4jឃ__vK*̊zGJ*leJ9 ųsAw{ 5"ycH/>b]kpE*eײvW8T~s(Xv}p66ώáL!OsA_`/zC2FIa쏅5(:-Wu]9I_~" |/ ]e <`Ix%>F[e s+Aϵol6ȫAqmxnIc=K6x4їcX{K7F4%`ɭ?8!N-XLyvk',(KsS| ^jΤϕycuQ1Ŭ/>iag]ǫ Q@ĩ I#힔~GJw W0*?JS &ѱnoW_rjN=IU Gy-] ޿HqS/е A Rx@ᕞ8x:|Zӻ |qW8X:]}RX4NY=e)'Z 7\y~*0N?wsQU{N;W}z};~y؏@eg Bxg#u'>Rx 0NF"s-2'_G:.mVƧ2׳r>y}Y_88C qs5д.Aʬ|''DρsUWk`oSbk( J )uHJo7 -dNͳ yuDtˁn8U1cܢFΘ]C8Խ4 ɟیykEMޟv݄P83&`'sViLyf{OY?18;6}&x?r>\2T˦羿'_v= ၎YNˤ03#ܗes|8 _< O$|x g<`YY\yݧȁ{` aVeSxR,) L(~$&ڞw`vEqlo8A<3"̭qW80,<,05;wٖze,ݯm=`,qL3%p9M:9=K}C@9u5!hI13@P{/ϗ3o2#);&cOrY 9R/!Cn帺WⳟRN`Ȉ]@Iߑǐ=:+;o/A*YFK`NH`!IWh۹~Ѧ~^>h|nGfޝeeiI4pz7'Ix}`RcMg#Il.p^wǓdpd_y!B5D3K2.(l!f(+w;r//t{Cf݄ JaOIū2zTywK< ;Qѐ&U%s)_~7UTHPu AՁ$9ERGԁxUZ:e:CʲN$M=drȬm:dɇbW'w읊L.rlpEfmaYG[yج:qج?lx=ю6hu:ZYG+L%Tn-{މ-:d7*Ru Ww ;;ڑsZ9IK"C})!淅 !صɫ)}̃~2||3֕j>,xO9f!ks TSܹgRɜdcML1;wǬtYz=b"{ҏ?N?#K|?_TyW.kwo0m)ޱ6/ڝ?a%'qiz͹jץJ)woyg}-p">LN %M>|x3N4} }=y Pgܣ) Y 1gZ@A37::C#IG4>2˽9]Ӭ;7s|}}U.p9mI4ܞܮ>Hiq;vk |%¹.G8 ̑.8|~ KPl؅}[NaM>d§G bX?'?mx Gƚ\MzIǿ W-+,/i&| L['s;RWiۇ;mt""i/Þ9Xxڞ'rxbiUlV+քg^7 ſ+ x##rxo1_ ٫O{xo(S9m:mI$+{==מp{>$3v=a1χ9cʂÄA{pݽpƴ_nk1mW;n6~ӾΠ>0WO"c >Kx0gpB_ w7SlzMUo(ܖQOc8~36O^t3ۘ?MlsuۄO7@O=Xd>̘Lw`3O"lW5 ͞ujBY`KVSxu*;nCעq }Ooe O =ۘJzHεm<-`CEy?J9'eq]̛d}x[ˋnaǴΰs/rO fDZx~ }3(4<Ȫݒ]0i .ѿ}Nq#췑]{n\{2]{r\Lዦ$[D פW&/{y'k[ŸBsz zMXC zI~H&<}/d%ᯘf?ߠaS΅zf=Ƽy3 N£ l?#-'~'<}JKfN?5 $ݿ "Gkޛ?~?$<{62mw_h^'Bi{K9U B>cӹ0"`F8y 0l*]1Q܇px6% [mDo_1> >G.Wg p:fg/OoLu8Ѱ\3Cc%$?<GMSe >9S?Q'/.D{s>XոB~7Ȁ:^bF3WyWycU=U9m5^Eㄯ!=_GՔ=Go³ jP#U{W_ Wq^cQWm']m /E]pSbv\kO?xηjDx;wo /ٍދg|_BOy~-M )JBE{gcp} }wNudN7OCهY4I4k! o@ނ4/"i^OJ_$gåPF\2U) QMR ~Epu~jY秔)zZ~UE/T:6_39 -s_s; wc| tݶ zwV{h i2̯wt T{fs\kW횵AuY5k{fm옫5;CW|~60HK}Tx6jIΗ{ӆ_*Ϻfoi6*T~Wݤ皱_⽭⍼nNBoq\ŀY?ص-4vA-G=GՅ)JW\7s )Wv) az-H {WLs.\p;a黿k|g= {oI CgOv: za nb{|F#9y"pXOK zSPRg ζDt/rdn}Ar 8_ 9UP1|^~L4E|[S+hOv ͺ >3Mu_l/c&wO8CaF 4p5?[qDf着L4z܁ Rrx+YxpM/e;T$=qqOKǞ?nK_ upS7I~ 7Xy`%<%ROOyN4 +z;ݾ6u8Ty]k:QxfM5UN$o|~2zx7u ̺ ̺ ̺-eW3T=>g@А>DZ oet^Dʚt `EwIͬ~cw+#'mu~J<籑6[nU: ?@V 9c|X!G׷,ES9iq_e /d "<e 89Z?eh$/|e^Woa|O*¯"=Y_G]{=>S7mc0vlw2]F~>u#aLYo<[ gƜA<ᬘ3 8>/"pbJ2:~s)|¹h|fۧO|pOOx'ܲهzqfo\orlV=[pV-U%ݻ wxyQxf0S'yi7L) sܒpxFhx.Wo_s?v#~>O٠!g w$/2z@x0'gJ89ߋ2' 8){lqj G]N%kg5{ gd0Ε/]- #Jh#QT!>xt2듙bcbc7uW=Sl`_YL/湒!2 9h`326d[ L+$i +osܽvg޹--&1Lqƕ7R:!yR=*])U4qz3kIGر"도Sm ?N]8"60_񩤓-HJCV£"LvEqw}Jr,oS%'39gKL8;W*}q%1'49zѝNG*eȿDQ4@8%cdŘc>jėo@YD{;%@-P"g^!ZFP;026X5VC2Zύ y mׄJL\g\S+5/Y ?3HHk TNDI?˗z?xOz$^1#z 3e7yy.[NϸH߼C,EHboRlq#wQGenF{34xÅ|N _pR@(fsDzU lr0ySdTl3@q|1qe~(6Yo7d29^DUsTO}}&^NOf+Zrt`1>- ?eQ?n F(K}c]秇E0];{ ֒Be֗b|ajʎtsߗB~%_m2W?Ǒ~^cYd`^iC8޺ELW~Qcw>Fh;uXYԍ+;سw^`v! ]pj?[` 1ѱ1x$ڎ5dL [ ),x|g~l80nIx}Cxt24ߊƸ>LOh3K*8F/*8I8~۶/Kk۩ѦL"8~;zb0d)2߭&/$H[Ǚ{LoP9Nsէ,@h=K|i7O9m Lwo L ;8"37O [J^{(ky˄3=9zo\`@(yĻmLOO{s_fpC{ 3>gy'd' >̇|/'>̇#|驜d6vCxL03Y`ߎv PL_6c,7dugIS [`}mQ!|>al)֜aN8{#'Ŭo س? ^DMP"pe^DX<Rخa)TT+6~ /3j^}FxDycYAY T]̧YMf_N|~'0 kl3.yqg0{v|t67uNdНZ7.$p邔|L_{ibPTKtk_LrUyVk@1zGu-DOҵ)۪rfjak:xv$_^z~7U'ew,;w+~REw H] CR=JQ` Ad>0m N:m ^zT8JݠΨyfqF1Ybn,s]!90b_>/֗k>'12!{ ,~20oQT^)H^҉6Vﯪ*{qMcyn xyˮ+}wY#c?.ҹyHzXՁ*I$3 j Ίud`] u pO0EVrFi epμv(> M_2Β- |6_a?~:۝‰uH $q"} GIALs64]s[ͮqr1:C`OIpNnYDat:݃p%t=f׈xmf유z}T%N2ؙƬY!<¦Ky895vMm&{Jv σv ,G߳6c{Bވs]s#lLrx_"\6-{t!\ 7285p9) Nm}AUS }ADbB'ĞEֆ]1tj y"'x,H'e*>YQشbʶQq u xо` $x_%˽[z͗7]ZnY+~/$A FWvZx=K}_ގ4_5Q/@5UZ)\UAR#]K&|/y8OX*Wl*Yا9/-=z+hm݃I9O阵zUg7zJWʟQ?SL;QlOeք8 9kOߚˮ7elLe ^e}ܱN}-kn?)_n1]1rчm4ˍuDq5Nɻq?m)?J۞J8 Q'Hr}m7TtR=- T5$|֡S[%m>66'¿g@7]5Gtl_nr`\-Z-j 羦B/L)7*vAC' Kdw]HwgcSycAs bwz.oʸs鹫.AR) 7w+{!͇Jx/r@X>Gs/6` =r^%} =rF?Q{+v혇jkGFIx(d`.E-LWToUV^Ϯ5& _%<qeod&8VN_%[wx ǼgolB7ʯm~>uH~pBFǙcm*dr۟Kؽp.G}fEE1VNoU t!A}Nz־JWxڮUv1:Lw1d_O%Dq5 CL9f0k{60w~_h p0{fQ4G\O r g)E[_C4ә>K*F͠o3qDP r?giYjvq]&|ȌEbYs />!:P ֐ČFW=p~_BCK{,4uoDÊ_*(brymcJGh?цwPz+=cV^o4Qh~V} g*Cٍ=s;kB6$Wl_ Rqr78e|}̗>vnPTjdR)q6MK柬.z);6l3mf֫ʇ~g!2ov35PxRє_Ԫn I#$^i{-A_y$ߝ@Ge?瓢f/A/\uCQǟ@g $сA^h˖/Kffi'^~a%^ e< Qv xܓ7fW>Iz?ZែcǸv)_ ] ھ?r{6Н__GB:.c0ؽZCqceN}nۘs{qG HNq{糰,?pAn%Mf=BO:;Sg/M91%NCBn-xG>A?Էx/n3]٠s9x.Ɣ%\?=׼]wԞ']UrLX$ܼ6D`W߳P}~#-6mxhw~ S'3 ^ۿCF*<ճ3 +}8{L>Їph^xv_ہsǁn$sl' 3 ߒ󺟂."C>_"z3er?y~%o+=AmJA`>E_1Jium7P Q9,y㧜3|!J o{9o!g*̅se_rQx4cAWtbO x_wT }{vp w6*J%k'=jVQ^KyyUGWU:Z UuEKY_ꔲ>#Y`uk&*tu]}ޢ +_,;gi*?Kx%M)zVDf$az~gg:5k_pe} +c^ YXYG+5@o.?N@]zybߵ[@!%+| Y\Xj K@ mx^K_N}=}Ftvj* W4RWxP~^}n#l㦭cDW_CF T>nߩ\ެ=1/(o\vx-p<no1N^7)$7DtPΗj}ݳwA@+W!W/0y90/Yÿi@/e4iP~"ܴ换7itn$nq/I'|6nၬ+¿//z`_}BSA6g,e$uVt~]UzhEfaw5WFIaU%<+1!,kW]rz^h:Λ.GGg8﵊f3:t՞}߬6z ؉(8Sf|4]44O!h)ʏkUxG}Z– NȀQ$[d\*yƹvIY鸄wG x&dM]JtC+۷XbȮJ?>h2G%%n7x}"󕩒[EaJXx82)>JЭ#Gϋ~#]%[ɴDrXz^IaY~AaT&d}XA5FQgʢbUT\٬O'_>`~kq] װ_OJe{ 9?v|~ucqyw}ޠVCk8r'P2R]k(}q:Eߧ+BT,}7s qo] Q]#u}Uج?s쳆x=+%%>f m,( ~1~uߖ#zOe{NM)/^Z٬5%^܁4Vge.ڧZ]EPviCV{K Sx»GT1%ᛔiT1m(O.JNsxEП9U1c;:J i>|QӴa5>j >$$8 V)V${EZ\Z$X ]RPJߙ33w ~>avu=sת^Լrp%.lն}9i-Z1p;E7nA;*_ba$,sr{0M5]ՄM,FBc>-Fwa{0}_;p4ݥ\P>W3w\w;nMmq0'^z@ũ?\U;~8EOe5f9^Us_apm:*?&S_ɔ3.dW,W)\K!ff{LGoh8NbŬ$rVpx('n.0&-aґ+aXw%pEtMKPߓ2IN zi\J7rF;ͥd+w7|Uz:> :9*3oarxG|oLJ}LGsԁmB As)/8!=kxW%òXwbg(9vcyc|zYDgN?_ —,\BkQs |˴ϘxSZIwG}ST>ͼ~xC_+M!'*eLx$˚8`gWȥ;v0穇' qYu|y>'o K ,KKsK9iKiY .k˚ҦY'|Zֳj˼~CX}_܏9韒> r򎬬D~y=XsP ~0VyN~kiW~ Crn)ۣJG{ ^b;LX*mR2wfNoVѬ%?evj}M#88Fp9=I++V ZfSEZ/G*kEm"שq>"f5T1ce{r;LDJV2sMJfoUɌݟV2meD%ӮW2szc>rCY'qx=i^oF9g$2KUubʴo^ΊU6g";eDZl]Ų;Y4"ז}²kMPNZ%s cc]ߞ4aޓK_>Q+P+]Yux;Tw{s)К-k1g'obS4mx_s*dG;֑w n@mp\v<[Ʊ`4k3_\r (,Y+e^t:[;K+}A~qee~Wv2[eR]֪Ju٪wǏm2Oua]kYcc=֓j}wa^]`m8. `Yv[yweB^6㼠|53֫Q;ÇՌnݘB{\o-FujԟWsKѽjԿ~F[ƸTթ߽_/\_DuzfuYҏt[ϴFT~: . n 0EZmup@? ܤ7Q MnˁuMnpS47-_iw!nFpep37 jZYM n6p>5n[psܜ~ n.pew;y= n>p njZM n!p[-nup?%En1p[1w&%] TvxU[WpbjZ?p{r[ZRԖJԦT6-MmԆզ6umj;kSYRv֦s6뵩jmOmg}j_[4]np[}nep@6 nUp#np 5o[w:>.V=p[6 mp;&.)7n ̎r T7mn$mv=+p;܏nGvp;8ppnWpk 6v`y)3=͍/kL}Y勵 _R1}!"&\EZ杈([/Ѥ1ٜWu9϶9T~SB! 3<[H؎;AI>[ބUA;!EvZ*ܣJ.7߸ lt2D"qekvz"~ %B_Pq9fq(W\]'.`9\6羣Z׵ą%.+.yPrۑv+xm\D?Ƌπi_Pܴ Mߑw10xe?5N^Qw}>uT#ߔ_cJ+kBvλ~ׯ{h籘hGg~m8-;j7_ (e#߽ia3:ӵ4w1fmibk['oeVԟi3Zz_4niIGCN 2mZ;|P_|>S_ևgS}̇65a]$&t-ΧmƔQR8K r/( Sx~pvQs;ܯBܽ?{l7| _חwy.#{Oϻ?>|?߻j~-ÿx?s} 3a#\(ayX?d-O9 tx-,՗Yǫ8W3=sxq'w8|bUqQGNoLߟ:M|w@揎`/0_+2?Oqd~Hwb}WO:Gr|U|vu_ӱh~_pG~f?G~V*9v+ﳮ)*<{'$.3 ^}śus닟3Jg8v+'󁊧hڵ3w"s#_9|#}%9b/;-=^TVC=>2NS^'jϻ2Sʭ/q,uT~avKmX_*#9֎x}?9$q)󶸥gX0xƽ:vQJqh]ɺq<_xFܻ潯X gg||7uBLRL6mxw7ȲEXTwZ,}l{Oau}sx.&ٞ|gs8?_2*E)GiG,`]Iz_%kadJU|ҿutt_-^k9|&7oG{^ϲ[Ywk˻ofXg,7Ӑ?a;ۣ⋉%?{wzGwayg\MZl u9B /SxK^"*@)a)|P Wx{Qx»ޥNw(]m oUx›ޤF7(^u Ux«NU 4^HA; 坓\qr쎎ςqwpܐgiIq qMk|9O#>|]| O|/Ϙʇ^cyN1iޑbH2/}ۼdpr>?&Nr})WYߦgw=S>l;|Yo\8]\OLOId~wp*_b=|wE|K]S?勾Y9_e>)'Kȟ/AK|)?%2qLbwċR93:,_:{\EҾёg{{&yחN, g,Wㄼk]{򎆔_a>W`^}a=̼30=̘J4]fӌ{eJO^f,,ˌoz1W/s|/3eƔS/67{־iCz7i{zM_+~>}Ǵ}L>.1u}L}kL_S_)}M9k+RL~NVx{?v?S_*WF7u 0`L}`S/:W݁?h/h@S_/򙩯j󙩯[6)ZL\E OPxdpk(ަM] Υp={+2,Zgsq,/;kɗq;2Zi~\_{8v+?g_z#,ЇX;)/=!+4#Hge+_HT XVoƿVtcq,޼#^_,o=^!sCLz*(n|)CCB[c{{Vv/Ɔg7X(XCpӃ[c,Cp,C\f/bY0}f[p;] .^W\|?n9-w.Eps{<2|XUB-u(g8n!L7-npˁ+׃sGuz]qTw_3wcleqNj8{m_S?(m!w'='=Ǔ)]%S2kel;ɲCXv,`٥=/uld{Yiqu{ȹ=1ԫ~ɖ;A/sXt\7nϊMww0mŷx|"jXowvfTKh=8N.q<(^z+﫿ba~wtNxļOw{u.z2#oλ17iq1y!#"Yw/Ɓ_ g]|g~pρ7xC[x/9>*Oug),9ŎT⏜dN4Bii;֦)6?֦C- wV S1׹/x-ɴ)[I9dg<]8Ƣqdw~pwǥs=ĵo9ktbYcB捠纏Z#f}2\Y +bEu=pj xWeRN{g'6 N5%x_ݴoi1RcWiv=@O5gSzG֢7Utki{h\,{eC&m:6=t9GL[LPZU:z|3դTƖB Uv[wI3{l\gI6j[֙Aq:8O8x[xJZ۟PqLᮊ‡^w Txk^^(K Rޢ&G*;c&ܱcs~5 ~wSOӝuܞJspi; pK(1 Jv§S|kpoM+)V U K-/er࿔ @U¿*x.ps{+xxw/PS >ìאiw7̠q ZAk3h xuoΠ5WuZ3.3IgLҙb&L7tfI:s\W6W-npk(ǘTŕg'GTq5S}:r*C V})g5gh̨Lӏ4lъMuZ{bC ΩpY(_*cD~tƈ?؄gO$ȯr=)'6X2˼vFL9oYofg^G/>ǜ!6c1gx+=?օ{oX3i?=kgd,u,;}A(,xk욆$փ֌ܭ c<0g} -byoXn630o/f{yA'MnA o1/;(C,%IY[@xeE?rAp-hǼ̓WR|&Z0Wx;} e ә/xpʼKp|zi̻k)yFoauG~p?=bMYowxa qLFt􃋙afUQ?²!PyOiL19H Q0\1n};#9OOʵSo<㫤7|küf<n}r3s#,O8y.o-ZNJk_Nݶ4,rN/rn/ۺ-w; =h鞱to![~qu:4mn%q$nmܮ+)!+9߆3+)/wWR^^;**۬*Z}q3W:n*Z\E븫ȎUd1f ~Gϱ|Gv-#;ߑWߑȎqwdx;c|1&;|Ɏjc찚&;Ɏqjc~51>YMvkȎ1c,[!;~kȎqc\!;{kȎo-1f]Kv6Vcp\|%$~ҚeM#\lp[JEy Kב5[ 򹯜XG .ʱ3@L ]m 8`ݚ4ƳqϑW'_o:({J6!c?Ws=0N-+s(뉝[z>J 24bVi1[h?p=!|z=DB3exRoXc|zzi(ԏfgJyRіq4ߋ!{j)oz<浃 ^R"?ZÀ21= ̼3-7u:?2Qe4cKc][mzO˽cKO-RhK=9=5к :){{r-Ͳ]: :m{?Bm)1e=oZx;olv<p_Oaӌ|׀Gx/nلBg8<!:G&jKKջNyNԙ{iZeZ?1S oVޯ9x~Є;3}wdUwrtqF݈Pm,1[W7[~:6@.CnYrnGf; 7w9&e9}51}ԝ.tqu?6{l]%ǯ6;mo8y`Jc#xdW}[Yl%",:;q439eAy,fǘj{ Qq3h<˶*(;\>U1t~6j'I_pm[WG: 7go:adi'h$DtvAV@c\s@@/T&i3m8")( W~oW'cswz!ǣKٗGK{[cx6@Iw=r3k Olg)9>w{Xn#2\ oǂi*_ѵw5.u_ֽZgetWPry}>!ؗKҬg/СF]_9OX.c4q.Lس{{eSLsgw\w j.۲W,w"3e3\\t/d G#t;| 봉!yMsHm;rvrt)!= u9K9ϟrU>ueUSx/cя~npIݹVȸ::׌w9_~W!ڭINpD=Y>q*{̘lrAϩ=P㺒͡C)pBH88S9Y:,ڀ?_tuf^ci0yMp)·*,:-Uvnkxؿd&|Ƽ6~*zFyh'exN cc8d-1>vI?%ue1Vڑv}.)I?LkKG'z>=6}!kxOXMKK0|7u^rk\G;~ga چCg,^BXs㠓?AaG\{[b[6rXf ޵$wUң&,!?Qcs(ucH9)5 `u>e+3|̽1:soyܻ6֟mÏIHcs1ҹuJչ`p (δ?yίpA +ނ?TpU+\S U VpMVg,3op;(`R R/y~φE1smQH s`sknsg;0*WgGG G*\L TnpS (\H| gSXۛQSL~]5k)\Gz WG3l״V^K^b(<_E /Tx7 S8Z OTx8g(3lg)Zd+|R]ГgW5TcK櫓qK?0sҿ^ROSV8n|3miutd5y7! ?^-f=|CAegqƷqd}z?{S:wNy#xGIy9ip++DW$1,߄;i~ !iz,\rnscW ~$TZr8_{,F@2-Bƻ#2{,2v!DO3#_Jf—S}v>yf#g^w8⼇>>^/Rә dv|oY_@4uQ!7gϱF)S/2__&/ .prюGA /iCZW8w3{ǎ((}UiDWCxKT%ck p4u].==g}ߘ>h;F>*d98_234;#.YGZ(ڵel`?"bN 7 z$ |(#[㝀{Yh;:?)DLg%c35=e-}y0طƨ2eN#-{tvvA6FYi_λ\W;I]v]fY^KrՂJK qOdL_DQ^deݤc+5Le'.ӹRe:Wp\5K G+<_y Q8]eY\=2gSBV; rZo˭c|ɝq| Q䮲:{-ˌx otF61TW]gfGߖzx}xծ3ky} ϻ1octA0wwyW v|ChG 9)ds]f=L*^*W J}gU:]xm~TPgsgꪙ~e^m jU-}>m}ˆuJj}{  GxwQx6P@yU]r3Z4gG\r |c8%|(רo\{v#Nq$Nuncugpۚ=yt?:G_rq9y~:>ܠ{}r[ Eown)` (6R11YOEgҞlYz: m -V*|WG Sc1 jS/nP? ̧B7rpsgn|kr.|(,iFYxIμw!N=OV6ճ(pB&G0H x5>Kg{m3Sp9ase5,ZH04b=充_dBxyU8ځ#?oždӁ{ KU,?=]~2VW`^G;gM UvLsJciqbź7Hs<};Y.JSXg~>W?a^v7WQI{ ,/_yMrBNMC~P4uw&(i'6y _i΅v<2J#lQ_m6kзװ+e+/Ͻe XK-kLG[ī \;D?5;_K~X}ǼdQ;~о7Wy цSPY}s~hgxƃAp97<Ì9.8]s焤ߙ.rH,Jz$tRi3qa^Qz"/qIyZ***^,w}}^}0<zyډXaK+Z՗讹C҆u1'Ӷ mJ3?㋻ǻ1꿶=}R U6wOms—}:dܦɼNJ}|iw~O$"w>tnćg<Ƈ?G}gWxOVQSlR{sg4r]`>;26 [8\8q;8'w%Kb]]cߧwjRee{X[:Ⱥp?!#?+2َ߂IU%c:c3L/Oq䧕vG~ze~*LqqW<Yߎx|px_%mᣙGS^-k!~:;;/_DX#Zϳ8¬)W\W"+b9_Fl,\sW|YgUG|8sd|#/e~Ujn[~58 kԉ%=㘞ZFMR됏+/pn,?ݱy_>r3AWuwy/ |:L׷x.~Y%ogNgY+{Wr>w8-w[po&rYn~Wb"|=o̴]%ڛܱߨ[IEֳW{qq+|ȋ".ŪB~'Uf>*y}ԃC*w(W38Eؒ}ăx(CXncĦ[CN+>CU(7μ>fKb~w^) nÌ 0aaƹ?̸3>&O_i?Mz;!/L´]6~a^={aʪ0uL]d˔sL˔r[ſL)^2)/MziKS_L{ep+S߽2ex)_2)2)o6eoS6e)_2|mpkSǃ9/NT Vo ήp%;+C>'A`oiz6g!)]VV岃\+|H; fnJ| WQ#k΁aC3߲\{\\r+<b,;j;䚱\wp f[r|Zog >7:+!?@a/|8⸞zޱo5Atkm?밼ˇRQ?X? {+{?m|7~15 E&\+rYrk\{o $I@OJ wQǖ[˲[;vv/ v?SxG{\P/!4Ix/xw_13G' 11^dxƻub@6(0J 鍂uA@ҏDK&ଌLD. ˻;hcyg=9P]? pxa)1w xnx/Bw /A7v ;M8Gţ}.w3..͐7ʴ(zF^E<ؿ(ٱO[ M6Sie h ضi1 ^Av~rz<602u`ޜv7M;m/83z8 \:{ЖX+yI }{BĶ)(Gw n/w1j3y;O,CW>4|{6e#+al#v[ B,g 29c /;)A݉І9Ǜt/ Ya<0Tv¸>eP.!.ӆvラ_`r" ~"Q~+J$TBD6)e8˰: 6%2 !}_9Hu:W,]㖪 ƌX wHy/IBG1=RnXM̘,Ϝ4m5LS w\(!W*ɼTÿTs/%Dcac.>>f.tBۂcz+9|%];d78Ҋ:;G'ޒn/aױSơH)'O!ޱv&! H~c p7WBGPH'~[ܕ*wpOw..<Ƶ}̙9(8;[<&*/D>H'/}s3M O\2y"U)ys/u.ٞ3{2ojKXOYIψ˅%;]ʷ𢐷×J:.sL9J^dr`S Y_yg5~g+5sRX>9Z2K=]T])<1Ǽ@ USX}$s^`"UexŲIżXO|G_bX%c1p?2%yAg)=_ 6yy/|:1}$=+ R'=F$n=xLZ.HB{I쵏u\Op~I!)[?'.'mPpq>\Y.u\|M1Fq% t{M n]pӀ \Tlpޏ– fLfAw" E eb\kף^|QmqCfyҢ}0=};!t$YQۍ gClztO~Etr*_\-ON(k ZpsrC\ Mnn'2cܯ |Sb2*9'HmqcC~V\KJw#ǘHayJa׷Z^LJBe":1R~ ^1ۂ1C?|x,e\,}[E'C1gSp98Jy=&֛`)vÏl(0~Jҏ?9}7?IZ7U, `[׭Y#˰0W0YNI}˳iJӮvMJ/aϚB2Bi?9%屝L(8(e<OII4FR}A@sw2 4etUd#[toܧ"O;>KƘ ́1Rϯ;RR<8Hc]~vzs9pZnWog-ߗެ6ػ+d\iT[KmM N Te8J6!98Ls1~Ii/d~3 ɀoB.p52*=vy.u1//{{&Mv[T?kp+c_S5| ;OaZ^VxĿ x)Ge#g1Ë:Y o{llљr i0{Z{mB]S5 ? nv z2Qj]]VUuP-Ѕw5c:{?*KIzW8yWq9oO@OO9=g qz3/s9|_G:pLzVsxG>1K>Ѯ!#w>W3Jm\d`p*SoCU]MQ ι?+컷8>wCYb P=Zh(|B[iKx>ΜwdNgE1YWc4k%SU+#n~C <5n5le>bŝ$Y.pM*

K3xGu 3a=l @m`plg?|vms-r:ݕ7d%[H'E<NÔ/|R?srۖ yH]๫<3N]㸘}7ɕ03\/{~g|Ҟ1(=HydF'=w7J莖ȥ(~6iKOlRY~F$ j3כE_29739/QXȑE\~mmnaܒb2o׉-}[}*~n'p7vm'H6į!% Ol2N7y:V~{r!OIsC` ʰ'C,M8Maم!RW(# Mi͸;?G{ㅌMIs-Ub?z'"'}52 abg9v3!+GlSWb<2>_d{U xqib#T.0iѸӊ+c1!?ws|V J+‰sK3む3.k2n [EʸJ* W3:6/ㅔǃsRl/}JC?@s8J2~4D?~chGj0˵o]ky8ym~$IWP.k#cRkׄ70Enurԙ/2ceoE\4EhMsl4ǖMsl4ǎMcقܶ.zrU9?{cY r7g1w:73]Vs%ЖTG&ӌ*ykD~H|G s=dn<m㛽MJ_x~n>1uhp׶|fߓ:mAgep%~8 tfo8#apz,]j7\a U=Ok4 )!CUo-gR~sVՏo}piőG)a09ߩw~z?dk\b5?U:e?ߔN\ _`Xc,`ų3ˮ,Jfql oڧ$dق%Of\7UsYSgϚʸ.pX e-?]7g03x. M>[4Ƙ}\>]+yxu|c._G'p{_0gg/nkpxb8| @p7_ \VBG]d;xc{514-QhahaVR> OYyr-^BlBgԘ&2zI)Yf#%KqޯsZ?P* Ux=',O2o7*emq݉@/hH+,R(bp~4v{nBVdyw9"4N,BYF偰Hl#Qȍ8Ener!7'r$Z݅~@ԶnV "lߪi"ގ5 mrٔ `s8sgQLu;\ztӪ}k/Ur>!Yav2_s7^3xż؏?yNwS%=rΝ寀|lLQs.[NXƭ}pZc\kz~pcgێ!u#}ho&ޞToeg#G4KrY p~Uq#[jk͋vY*F{{tIğ]S7beG me'rh1Y4?w0o0cWo2c\#)f;ݖqk}69R/׳x|yؼ/Rsbfl!7gƓTI S܌MeI ]/T{~݈.u)ni;A*Q8?85w;Z! q Wf<(QZ wTFUYaڥVBΔod0y~{8 ~l0[Ng2`ܶe`< b֯)JP߹.YSl86X*/w970[+ZuË@~?_6>mj]zNc>3Y se6c~OB,~%\o*#?0=ղ)l st:ĻGs[gl~,wۄ1_x^iAAVg, IK1E1.bkz-~m[ Ly_E2&iK{thY8PBg=m&16t OFYӶ+#t޻|Aգ+Ѿ4sˋJG 'Ef~Y]:qU6EKަ|)(Eg~Jj;\+Fj痢wVjuڶ$2Aׄ-E}ZhW8W@i 2*\XaNZߡ6kp+312F{.-5=,k*T3Mha҈q\@>u+s/2?6}d;x!G 6xϬ=DNEnY;b+-tc5|@W| z3-řyws$-򟰼؍;%z,!LSn49q<"YW6?bX0 .F~|'{pu",w_~ۍ,V?UϼȲ1{,7VݛzW}TQZMnK3g"q8/:FP[6*LLJ1LI-|㾛`fn"L|aD~I` 3o[p>dO/Y(Qٞt`íjEot 0GqY]FO+X iu=+=Cpw"7Ҵq 3cBq Z1Spr1i6I#&/iFIs)&#[ZN#&͈1nXr{bLGh++Tba 1=A*=zXks)1F4y Q5̲Vۗ.;/U8{[<;ybuۀcC9󾿬   ̝wG`q/涵+%ڡtk3}OZoʅ7=,k9O S͵])<)7x\ΦEs,Lix2qSpm뙷~_cd{7_0$}ԁ=31=X1Tz3?ò(8}?k7{rU <5҇kЩӳ9Qq?[ s+z*}_AEDUз)S UC*ޛ*q}XY]C6 h?Ĺ4{rח 7*-̃,?ieU*Iάpa)\_ Tx“k>JAeא J!(+:l-Tu+=M=P) /Vxҳ_9_}p`SV1zޯbb깪7U}J7{I3megxG x.%x\RkxLݐCJqJ;UI.Qr^R\V{qmUI{ժfی1w?ܫZ ~ԅsAA>'0x~U3oم܉0s]U~Ê۞b|L*`°﫚=୪fʷA_䊂j{%_{CKlD5jXS"}JjfEwA i̋Ĺn=nUbR;?p[yjgW(5Kh-?;)7GYUgW{)WL]N7u`zq3QO -e^ylGg^7iw9bע>zנEr{1tضb񗿺yAuc#2͹=?Ji|jnm9sZnyƿ\$ Vnꨁf?nh$gB c7r6K,*g69+zŲ_g]TZ+<=cd:V_VuTayd^g]b|-`"#wRM8שGs]̙1rVRMF~SL?tmcl'kqou'r\՚$ZISaoe Uxt[Uڞ3d>! |\v!ṣfY~$`XiCeyܓO<1^)zU3M(U""rXn2iƶyq7bݎ2yY Ga~t:r?M?XH:lQ|!(MNָدkXZad&嶃y뵷։;iӑ5e)|=<dǚ\!kG;U3Yhsp kւzqYJ|9 c`?в:)/*jW{ۘy9Uo!>{6(ݕ`eW>䢼/Xi,Ʊ9r2?,by)e=:=}1=q,{|ɺ]oSustW65 `Nor;Y>|۾f0+PlҒIuOHurX.%ȍqp9t`f2/J^`k :NP;xԹf=y~WjH5 jh9I&]CyYhhΞJ6!+01fC1#M'ܮ!pn1έ59GkL7ŅQY1k`z,IΌ,ufG(d7朆;D'>+l%88?Yf~oH-%xT~Ь54뇕 a{C݌J y=3_oh74v{/yGCe^E u :!M#N(Ьr62gXXwV\w K x#J')kJoo1J=݈π0 dl9Ȝ[ \ AiDX3,3m#\J%O8XGuQ-ndl:1U]Hu S ω,>u*}>kOcnjD%+[ܦuvZnnmâb ˬ'1RC]Χ6\=^{]\ έp6s*G S},N\}%Tn%;X{~i$cyH(K;9recYbJTp- a=W%Y]KLe~ olu=c׻}%gXkƇV x3R1X2U֤MZsacJZ̦Bx~ Xɬnb);uʁ&|猊z&|ugb Yt$۔0ƕb\p&OM6*,WQ.n=3'x[65Wݛ0הM~eJ e0)7c?ε˓ѹεK};876uqKߺAz W8|!~,USSZ.q3gS6f ^?p59~Q01wc,!r| :Q J3{n4ͨL4w43uhzxs3zѯ-M}y*y}]̛${/OS('rt$;ȝ2>{9vcV6NX~y?\a ^ %B̛ϼ@.Nlv/b)Yo@oZeֻP5xOTU8K<8|ܽ׌C[^>F0vbƳ-̹\zA ZKf%$n)PV^ׂ@#.7Ŵ>ՐW/ea^ӽϯ[Nߴ;''mf8N4>"p& xCk\XVEToQZi>\u|kbK͚3bc1;?k1O;6kߵ\^Љߕ p[-ͺ]K3vW~`XmJ-&Y̏ۘ1?!]B4v0ߋ~KXżCEx?q 4^hEGb/"iOk[ Ο^ceqs)^ ؊%@rgTZGsO`7d90~6ĥm2X *"[!,wtL`NZ6 w3wA*|1Tx Fhk'|{ƿMzX2:˼2r)l0qAӮ5Wü;!8ϩ`3w׈`|x(}dp$ Qf<Xט`s׷Ns ΋D7W"t3oN7 gMǽVKxYg>ި-3/%e f^&9Y |)\(}&CswY[ s{ dždxXϘ_=]Ra~^vo%.~v2\U5e^3n7ްvo3r]yhn_ɼ$ 1Б7v6<̛}YEvz~a>S<e^9G;^ī8g{m_v\*z'G+ImX*>qv(ߑVWem Ӈ5}r02η̫ eMxH{y 9wyO2:xGx):c ; [p-!}"5UHWw-xv0kQ̺oݺ=-]}xK;{׉ӰCN~1h g:EWߝt?<$t6Wz'eOM}ガ')cO%#HøzyaG(G78 Q/PKpĝ1QWrݯZwg+z:i~>׈ߝj_|D{ g~鸢9|js:\L23њRc7=r Q"'ďO:6Î{d>*B>Z,7G^oQ/!|>/pg Q2fh3Z2u*g/\G6H=HXn%]ğ`Wd%YnIj3o :(ۚpp>i ;yeg9xg9s|Ŷye,ח<7Ag>grf:_h!{̋<֎x> %';v[iֳLû Ǽ'ku!.{-בyN9Q] z2 }t%45<Ӆggz;ޱ;81mޏٵ=tRp_pG۔vҏ)|j븞c:3ge>\qrn}L q)KI1wxgwus%.\׵C.4~n=S}F'3N=y1Yݗ]y.o«sѶL&#|uԙww:{|;roQ]Rcru _pWOw$9Ro8!OWd}"Wi[3_Brr%b?@_cﺷF |h=ә58OѿG; `BB}רZP~c4pY3h}0k nL'ycЗҳ?Za {ji 88qc pbƅNXIǀrEf'R2Neb|8io [YL p12 yp8'83Rx x0{q_} pVyo8 Q:~~pO8 0 8`\Kq.$u㑀2Nb1XW3ൌ;5}?FiW7K1 V?x;w2~ x0 -y01> k)p=]2v>$c,Wx'#Җϔ0s>!} )Ug~Sgؿ s022 x:4\w~ :~quO>k*O|SڹMw|JA;‡Q~W?"f*mcI!;W`8dA/.AWk_~x `$3xEn-9_U^lixid+2^xzʨr0<=NQvY>:ټԋܗ(ߧKӶXٽ# 6@9DS^K+J:J^ &+Î_'fWYG=S"Wa/Y%rxI1+_S%8a-/}ߧ)9Q9IU*xiǼJ^x@*^[yg'wbӤSCO/bɏ:^Wz_ϋ^'x_;+\[#/N$vC~3/FX~k76^x϶^#<g%|?˻ryo~Xүǿuf^7ve'q5|eyO/sw^ڋ}$\c wLϽ7'W^I<"Xc3/ =:yzfT,f}=KmK|:]eB,ExKXK*yGo'tK9r ^ O,/~u':$|K:%^ǡ!}R,>٦k^a}.r˼!s~;]gҎORGx} _푗~=>?q7]]?y'j{zz%pyg1P/\2ׄ%1,1YQ,;@cl|?;~dw~X#l~<;݁'Ov6ߟl~;dw`'l~|;' l~; ݁$Hv6?p7~ ;Adw`݁&Lv6?!dw`݁OHv6?l~;!dw`C懒݁%Jv6?l~0;adw`݁ODv6?,F'&l~b;I'!l~;I'%l~R;'#l~2;''l~r;) l~ ;)$l~J;݁l{dw`#l~*;"l~j;&l~;i!l~Z;i%l~:;#޲l>PQgg l~;g$l~F;dw`懳݁Dv6?Ldw`3݁Lv6?3dw`3݁Bv6? ,dw`݁Jv6?+dw`݁fv6?ldw`݁ldw`'>dw`݁Nv6?;dw`s݁Av6?dw`s݁Iv6?'\dw`s݁Ev6?dw`s݁Mv6?7sc{ֿ,xG?au+pό-u3ƻ~;1N_هZhƃ3+ xa-·O/)D)Qr$~N20:]@ѳ1=, ,Wc;/|LC4qf ׃H_`~]Ǹk'"3. *١>|Nwe}ߘ@t}nAtEAz-pa)eE=}ѝE;m:.,k? .NmeZFլNTf}.LQpqzZ.L)Fp{ZGžzܜxq ή52j1c!RC(UP BytiC(OӆP^ By>px![C(φP7ߡa8uXinNp̬88~T\s0\X=- 0pщC=hp˃\|\nEpςwyqp"@7u0{9>U{ j.4?3xfp.>^ .5Kmp#=npσ7OwJ^1ϰ0 0Ӏ \K ^~npG0w8w G!s 6ukr0#^Xpx8 e,e ;4NwhAwhAwMaYv5_'r;:{NsBE)Ύ V">8~ࢆMǰƐ1*O<m5Ʈ5.;<ώ3,iKxF˨s (rUyq?qd멨tWQ6!zGͅ~38^jw17FNƿ2 ۆ0 x \3 |p4?o:344ི9<e\%|?pr1vpS Swd$89'\qrSq*KOyݏmt sQtq&i"p)gpW9q|yp?g\pׄ gbp%e\ ,eg(3_ƕM*"{oka~3如o;osdw{c4I7ORy'«sUӜãc+}3#]—G1o¼% o%/s_wq ~~H|־Ȣ8wW9ԕ, [ȿ>Kx 69q_0x ぀3q?+㡀}_K(ܐHwcAqf2}zƸyg˫~V/d]h?3*޽M.0gL2rNGp>g| s'0O+|c܃^)d _܌:ܟM]'1^Azs8x0?/w\cL3~mxnZ?.*ӊ{*_`8]A}Yq]uǜcSet%B }n1{!0~Ό_^8>_c g!xv2U3Dq'Daɘ;˞& f^Sē1Wrxz_ȸ lr7*|X ma]xlgN  fd13(?1mgI'|c)g߷ NϮ23ƳB:c<ĸ$QK^^eִ猃aI:`]+B`N;iM(ȼ\b?R+>K#x~K Oa Ks]}Mnf^G4WK kwτyU<щnaoW]ŝT̠>Y42)W{?^`UAkgZan@Ag SfB?rפڪOs?WS7|~g=H,M'<; .(l&Lڙ ygl}g}k ӭNyͷeYᴗX1J5*4߅;qE#LG2A@b{"Wm,{] cpYɷkL1}Uƹޯ.Qb^ւJ~(8-ԭOU:3/Gyz֟2:>b3p d^Xx E_< vJ2o(ʳ̝.x瘜5߫m?qkb;l x$Nj{giÝgQF*Ym987"kG<˳: ]ݮZWYgYO=w1ˏs1naGzwµc̟/¿ߘ2ЇHY>Mul} ~Q?k (1Y-[**YU E6y~sUuik8ik84} 8`|p9dNɫDz$$qs(a6d &o;D&$:)OMۖ4]ch77Jul9M6y;M6yYg@L&<,&OM^+9ihpd8iɛ8m.xlV0F&݀~9?M6yM_? :M6y=M6y'7:M6y7E>,~4Y!90: EMjCm~_R E0h#h1>KD=ǀ+3Ѫp =&䎴e꾓x8qL 8cJO&E7/;d><ŵb5S|Y\hC9=!ٕNz\ Hji |`zvEӷgrR&GU|܎6wN$u/./p>͇u)yěmeNk|fCQ:ߤy|9M7i>|1|q$ʌq^h8njq&Yla}|]~)'^esJ{|.4~) M]hgBS>KټДᅦ|oo"oE&4L/2~ڒ/T]d⭻l"[ϴ\d7hoyp-]D3,K|bz>j1=__XLtֺvͳ(tt^B/!݉jKIw-;u)ɞ\Je$[}Y/X7r6^NܯS<;OL$tL^A2;W+d%q˭4&4 wԫ(* k؆gRc^~zdW|S; p+~h,c֘ȟmZtZp2їFmݸb=){˝&+WWoQ x?qV Uu;cKrȦi|9e}G8bRuxD x:͌+^[P ୌ9f?{#Maֆ6ڴg,w{vj+{eXFW!NQ) xQJ?'[yv f'ӢBNX}MiF_RxNڭ/[dr<5Ksj^O{&k\ε 8''Fz6]Ƕj?dG'^cۼf54_[Cg54~N_C54nZCc}4d]KU4_5YKUϵ4_[kωҵNGkg 톕YG}yudy`C>ZGIד=ddn=E^Ovד]d@6}d d$l ;gNdYv#KvH6d`#M&DvE6d ps;|O.Dg7_7f,(n&;!wf<.gܢ&fú NBv5ɶ]ۥ=G⣋[.sV#VL3n#̖QVw5Uqmn'͂n.x pk n-pOm'{?=gdYesAmdL:+~;ɾsN<;I}]dYlƻ[fw鶋d\ڟ؛1.Dqe[cws:c? m;c? N0W0%w6рEc fݕr7\Mnv}brn#{,clSv{'9Yx%^"dl m?wsդ{¬ej;|ʜ3Wn೻LGe1@=4lG̭N1 >aPz2XƘ{q-{wbv1w1|HCƸV1ε .|_ &|1~(|~{8 cl Ey`]xE&)t/='ٵהŽ'ϔU}J3ϊ3ߓ9/6 x ೲ>ce1(v[cӜ!>2*t&oviS+ZuP^r'XVx"}EBcW=9Vidx6r(ڧ$5PlCKY5RVlXd{dA' {9b?t什f )i~:3JqΌ 3Z̨:3uΌ3:3usփtfT 8HgFA:3vΌ.3_ҙQ!:3uΌ*3h!:3yΌ63s!:3J|Ό3̨a:3yΌ3tft0}ΌR.ӜGL?JuYcTgQ\kO')ON9)SS KNw?so1 sӟ=M4ӔeK#-Ӕ_NSP)Θ q1q!2Y+P?c׻ͽϜg%n-tDճ&Z9}oM7;#I|r sqٯ'}1هM~_]nχlvWs' p~-5g !g{&p76Ryc[zqjhӝޱ9Y[5ڵVޅ iSG<9oM F[49 xGp:1^Ĩ(a_\#C9g ø|.#lW\Vo 1_Gx?).p'Y_r.[>VqƂ_ ۜM}I|"}$_#cYg`,,db>+i8|I'7Y elW[|W(~Du9eLyTFO7C .Y#Y0`,5Ld\,9)̗ZĢ~"g2g~)֏ih81튻B.k1,XTnqpʪeaiv?Sg G)+<gᶽ3fŎ :~-|ֳTG2Yr"=wZ!Σw% ?u&q sOv__ΡGڗ-%m#㖌e2vj啌or3՞x8dfTx9ɛ4cL\K8p|ݎ2qb)8߮ϑ߻y=!t1#vq!GI.rmE>sGx4&L*xOyi{*8IC0=Tk_ T>m+߹|q.Cd,g~`DyX>2_OZ~E{;ͺ9yY5KoLHWx'1_ G Wxo epMԌk.ઌ8Q?gP6~N~Ky9@JkrM O'#‹,+|t U -` iC/od(nA}bM*'-߻e8Y.~~o8 ݦ2ng Ke?2k]PNdh{q{d?=g$M:,`?kON M6{ui~%mj۞gx(󇪵Š3/7 š6w| iLPe o{~$*kI):o#! @{]#Is+Vc|ݦ p>=s; :cC3tyoW<Ჶw8_'0kIMO{ؤ㳱 Iw<<tƜ-O=iOCezn.ɠw̚SyQuA=݈N00N gJw̻:=1e5g5RwMߗkd9 Yww|}Qu{g.D"[RY|0׏P7k {"]3&kz_$I gN 8J{~<#Ǹ)6RNNށq_mV2F[Ujq{Tב r/X4^}DzY/r-ef^;C 7Bpr(X wMz7̝G:78z컶mȺt]cÁgXvoF x b N8C x p,{zl`Qx7Ø]_Gszpc7 3tF}P*;yPFMpznW7 s=cq&)N=duYSqMVI!; q˝T:U{_s3|2|.`So|K_tU{y*EN,wO_RYZ3w|1yOCew9}q?ȼsWXt: ztqq\a=Ot:=P3^xHա7}&:I'.4΍nue}`8spZs\6y_~k5foμ͑Z}3oמ߀fO}J9A~-mUv,fR zl /G{s7C4q{62jG/!5}Z~x@w;YJ8߆ck)-w̵LuUzc9*#ue5N9wKY zAw\CWl -OJ5NcwMsrIy>،)yNc3^R\}Ǟ{dI1 U๙p6.2>%=̻?2ֲ5y$Lq;8ϾNh8Oy{WQcl %[~'M/v 唞b Q6[B7[dUX)pQK+%Y~(Xɸ\ ~bƽ 7P:^p;W]>d~$SU\ʼ2{U笉o7> O.q*2nȘq=Uc_x07ž8 7{jvO=zGqEэyJf8›B3on{s Y)(DDAYPBX\mAEO"DD,ZQ T}(/Upcj*`vl9!%O:̜_|ǂi!@qvw_8&I ݳ E'Bq܃gEmDFh""p#rmFܲF[D{̍KxM#d&ԗ??7o1>xRܘȭjKgΊgtFῳEn%/&^g ߻A"< ? _][MG,kž3$|m %7mzJUJÿWÊkR\'#eק5|? ;!Ӊx/ި<#(y\ N@[˽ JaW1/pq[.<F w..8i10ES<8??;Bx܃C߬:qҗa+0o?'u= m=hF8b|6qwO<31~_W ^1WIun=s1oscs̜.MOvKwǚx?|t1cG)z1o)?'b2&_ VӻE- ]'L;Ӵ0wst>6 K+Ϯ4?|dq%ߛLY*8[?ӂ"R: erpM}G#aX6תSySf_ލHrjQŶU*U<YUwO/~aJgWrrY#ltޗtJLr:*~'}q~'#;R՟q WUkX_ZհtqP^ʠpw]6w>"]רַR^ouV 5َN+nDi5)\ _]}pgSmU__nOpg 6QmYѩwp R:  ^wCmQy-!VyȨ<<<gPpS\4>.ż}װ邡< "/d̑;N6?cVLy+:/DY?};|TK3hSR5³ >0%|?< ; pZbp_' ?!n#+}D?'pI7|[982Ug\&I߂ut,^EBػG*JMIݥgS[Ok,=Q霱.eExR2KSq _;s#%Rs%:AؠMxѬǓ/}\~38В o:_/23ۄ꾦1;C(bEDa] 4Q_1a9-iשF8Į[0F9- wʗ6]DJL[8DFrm {!l#6D_,6YƿM氮}uW#hȃ<F {{mg Ķ%+Uao/l-pg Baro[X_ 橰KH:}ti/v)c' ׺.,Q@ͦtQ!N l  CDwS_.Z'%q)X?hkbuIg|pWG S'L*2A_gHrg={׋,syIb&H,(||$eg^~DWU]`*k2yP~s\.w,=;ѫXkK7ВB\%:o쭛eɜOҥ^46^#2 q {-nq"SO$sP[=-Z$ 3J VU%;W~JϘL6ox OE\ty }Wzي'7J׌?{GoJPw>sR>V)ao~b)c@aQ—'9=ųO 3_xz¤3OIZOS~'%ћ)MveT_uNJ eQ֝6pio@ ܸը9F4?*iU9Um:0яcxNiAyЄm4a;s>*Lm6QpptDp Z&2[!a)ݵVt \K+VSM' D.*-rG"M*v r!ߖ\"cE>f|,K\T0qrmvϹIw՗!f 3Kne.OtL ܷe7Ɗs|:sJփ֔turZ:][8tόI%ϣf~)EtGsCY?n'yjȐ~)2d2Gx| ym$fMM-c8M2{Q<ϝ-oMϞ9V5?ǧz#gyE =Ȕ_%CPL:'s6ӷv7};e\m.xFs<]$gױ2M0:6<5j3;2tǗV,YvdqzѮijYVٜlNkf6`6Ui5ӲELeYׯBppp9a 3>̖0K$Kf)q cO{2kIwN=˶"ܱFwmdO !ۯV7: _wO wה$t,s--{ eOgIyJK i=Dz I޾{~_熽jt r_Qx%$=aLC dLO:--qcv'+󕬿W{g٪jfﳍQUx*?S{d/OcnTxs,7]kQ!=^jopyǧX21(g>Qg;=woOz^{!y4gbÀq_<֡[/s?xɝoQzde")8Kg+_e!{*JWf~b ռռ$y'1yw')yw3)ƚwb-wnwܹp d-5o5o%ƪb[;d1;d̻\q-7l]l%[g=2]WC;pojX>y̻2>D 7{Qn~*։]kA{]-sO^zǝ{BE a_1 x]3 =g0^WpF9Nܷgm>jŌt6b/s{* u_繡xnx z1ƾ> <7L|熅‡!<7l`s6xn S (>ँ>};ѶH)Jghg ˿3ED&pz]g %2k1)ـ`>xNy?pu_/5-<=*'ئ$_Q&c[dl:fZz60mJ&67q\Ϝl ⿂L6Qr^_:gK 9Y}֜%q[ƺN7Tkԙi19A߯/sW⇢NkFqؘq~k$̩LمePqSqWdF/u89 C̸qfjkxi;nVm¯+F 7R+\ T­ CfޫsQFg5~VƙxyvS'률8;y[ x? ^?Kf3|ǽkc?p|g2Em*'sYK?>!Tt OKGw~e.cg#~!Mcnl,fuEʲMZA*999A֗g3qJ6#*y?dzPPܳ)Y͔6Hs5x =i=rogX9>+Ɯ[5~‡RV pCRPHy{#s{°a)1/Ac1}+p/cܯ8+Ή[9G3chsS1N8+cX aϼpyO `pgh?q?#9;Cb?7-pe^~홇z✼ xMN[3wc1WOr״~tCNsfW {1268rYꖬܟ,~)e<b9 eT5>G˔˜ey~rfr.>%;]r39dz< K5shﷆ%w_9@Qs>掅8NA.FCw]IqtVv (~1 7 %Nl=[ssݪ%]y}l2VPE^,,*9]mƪi}gǫhNnS>qi[lB3* [5o`6&k+Er T Dz5w>Tn\s07فwC;>tnL87<|*9!RhirgRV*O]Eqa&;+'-ʌv8NchKx+g:57zS:qC[ Q¿9yhsŊpԻzVwApX in˶ ƘxOjƀ;!:Ҁm;m9ޱ;quWUޓ}76}{:量^3/ *^;^gM{u<._cDo Lzw '=g^|òKob7x `d𫌗nxN<g2|S$pQGo؞эےy;05rǞq>w`n5żT|A*LG!㟹CG`=3ܛqrS >JaiXwraHƩ@0Ԁ3N (ͿB2Nz~byƙ+-B8+a p=90E;̟uKm:W|9IJ7?e=@q+xp~UF_SɌ ^%qq@ 82+3.g)7'͙5*߯h2 qe<*ƣBy+|\;?YQNIƓ)3x8+O-c4 ˑqn`pk.'?gu9+GSzF:mD3t׃l7bY1ˌ+7^>@.Ovg~v7AVqOofމmZ.L'}1xd|pmb2>.\A| yIGSbO[}d=,pw?1ʫ+ 2cP)Pl*W?w[?[u ۸ ˿zBXgzGtӴB9-,Wg|u>Kۡb~ŃO퟼+?{)WI7mO ?3q2F+79Sq6a܅qr_1eƸKN^ |p{G>=lOz4s~17r~z̷fB8+VZqSi0C,Yy{1_/<ɿ! 葾qxnGll-~܌~XC_qюq2NfӁcb&K8|ApFc.}1v)}09 R9qqk*  ϋO~  .t}ޙ=Ԣ,< k;Ƴ9}݀O3,1~;UUL*k6uԡ*kCfN<0 aY?M/wc纻uν;AyV83ğd}|'"L~h>V؟{ZD9'},*c\;5(gB{hÎho;˽rÖ-F[va˭TmQqlָ#yWard{7)ӅKY;|<}j-Q`7k03/pߍcdɆZy\TJv?c^XY~Uv9rgʓel9@;ok-MZ鑲@>pLPʎ8^2/*/?15}b4^,#8*)qV~܄|ҕw6b|ƶ!շ6m Ph+`1g}|^|7![N5KY{xB`yl?x&87~QQrk̼)&|}̽miY, W(g%ȕU:e='Ȗ=YlxUق+\Rɉ|{u6s)|`W]P'V,oHqmNlN'~ ۘ{=eohb0=_ܯ 7W?I~+ԕHM5GK}KGq{v0U>OKS瀶t`{VƨȍeT~r,,*'o#|J'mi"tzZ㝄asFywrz"Ka^J{O*W5|4Rf|QdSy@=,'e~!˥=?^ɼtV w= x/e}oV%^d`om-g{8fr9XN'Roqx._V$s)\IEW-PָoGI];f3R5'L֯h/>y悇ٻIuFw`fw< [S>7: NOXvquGg#*h1^=zy!༌gD2A}~3\v5(7BV޺2%~y_Sv7sais0o}WV?gs(]tHu:R:VCts3 Yş|w(? }3@wYۀ1F{x<~0)-/qơ:Wgk{m}sSF= 9CgrY/{п2 g3N85132N 87ԝqT\5ϟ82>m1H ?XVCS:+\ы1NjJLy]?~wqBG[ϾYS|3[Ι$9= y[a{£q8޻Ay4~:=0g`z'a=DU_n`6I[YG#a>N, tTx`Zxk`?0Y@`\˅",Woku^it_#^{ky30-n.ub^JM}89DGѕ~UTc]XoQog cI듹& lϕ/}.+i,jCTr;D)}`jMM|zX_13r1+ɕ>&.JT<<)Ok6?9y-[ CT xgM/\!, (X@}ED`71r4kF{ê|x1 ~|>' 2o#m5*+d0m<4ulqܖN;{{? sӭ?&< ( ע~8^SXd#,_2kQ_8>%3ujCO3IGpWo K 5 ܕ o%1W?6%zC/L!ix7Xi oܥENcT4mV{UZ*us'B(~qs?w,Z׼x4<7f(|ݩ.gS^zcuI~?!}Hnr{!,M-sy޵`YXUc{mPa]e]#5BJ]:>co1m8ٙJwuyulڟ&Fs@mE>AfͶu@1;cو~V9Щ΍Q=+m3ɝ_AmlbE?~#wYyWcubo|#`Pi,e˷"ĵˤ^>G(>em!w`Uq),r؎}>߷ G?d1Y_Gm%vy`þEwYo"BQ*ޥm`=EYiosxr+m Ry1Vz*4'z2/kżw]O2[m-2+:qx&_qy(X̋^dCl M^f\Ќ5nѐwl:7o @z3w1 2Gc"eƽFY^]"kBFxKin\8ѡ=PgsaWUi¼6?5G~Nè0y @خ{x44s扆ꛚSq>6y.쇛_lG;ISQS~1 e`,4/ I&aS:-RS2BF;hi-2Q]jD{S^Fu#>Fdֈ"ӷP4ohLwnLAkט61}mVcɇoM;_7!pw~=4qvҁ*_Z?Nuh2/(%a<#[0oc׀;qZ-Ӆnף$x[`<.ߘ=ްIq 5?UӶ |s]iW`,KŎnJ>~K~s65tSrr a]/#z9S8 ew|qP:ͽCT96X'c oݵ4evTlj즄kP{2ͼ ~SW76}J^w̛K**f&\͌ Wkmьl6mO.e6\&{x܏qemUZBgՆ>c Su*|I3u.xR3_h `-P|os{\G@y8sBQƟ5q.e?'8t6߆\݁"D8pzQiF فoxLclz~{~'Ph+vx;/{f/mFc,6qWx^;SfڜrsI4'۲Ys.qLYogׄ 9[^ATvg;f\ /Rxh9wgA4\}k 6;e;?taF?jeF ޗ~]+-~]UػX df 4oWObN?`O򻱎ZZ c3?ja- \e`{p'G{)C_s{5\~)ºεL%~B|T|3/hEQFa>+@1 Vdk*l ;}[4MZQ+Y~d=YӹҊuV.iMc`ִ.5˳uyִ./՚[XY5Ûuxִ5ÇuִKpwCk;.6p3\qp3{WpgmF\2ce5n^ d>]9UAlc<} ߽O;܋uE}p6n:ݺ)Liuʶi؆lCu3 m.iCuq ō6T)SYW2Oe:"ta2jKe Xp_: 0:uۺ!uځ 3\N77K̓wm9m n2jgDtx*rGFf6>u8/sʍ+;T;uyMQyюBvƴVWMG mYC}dY7o6wߴ38;Yǡ}gʞc[IFlGk q#"1 u燸8k < 1Fp&J[BPz,ZJ,#cK)5j>#x5oG ?yy6t# U᎟pљ$n ɏc"kxq 𗡽?uhΪZR- BnsO$$7y? 7sx? i/N#6p6/P+T_kVվc7mOXǜss%9+P*Ն3^o'*J=5W8VOɬ#U[A|׭=ٹE6X⡋G̺nYl&数0RV+*_h˲nV+pk,_0볪ou[7Z?c<3 n[Fu8ij1i< uI<̼V cox{ Xe~C8pV¦1œpIkjm)| qo߿с7{R{to2Uku$#͛ 0%;8WqC|1}f9JKO{13rZmGjo 36b4g| Rw'1pu4މfьu̘|'3fOOODk-l;]O2ӆv1mp'G3>v&og}m)ZkjGǶ>>ANo~Tvz6~+y~{o'2)kcb}O۹*-~NO-Ʊ"?vK[ ×Y87buU]O{B a([;~=s:~agӯ\{뾻.uv3=~ U)9ژX>yAYė/Yv4?J; <'=w!~ȼD?g6?my9|8| sS^g/nzv]LŴɣ]L{ŴȮ=gjK+]nb\g_gmY~+oxWc:'d1˕z[SWyc%NW |bɞ qk%KNrW3X /W8PQח ~PXs7!ZV^NϦ0d.HqN #O ~RiEC"F`Ɖ%>D6{2ޝ@'cwϺ;=[=1qg!ӄ-~WLFgj̽o+#TV=èNr aƟ)xB(!ujby§mU8)0~N|sHﻞ{D{])TُKNva$ Ǖd ׫C{>@z_]@py iJ-}zSOj_MIey5sjlqjC~M wp1 c4QsQWr ~ZOuUs(\GMtѶots]7ڰ{^F[y9տ$"䱯`4V*?7Qq,VO[Yw2^ˬ){5 5mWܓH7-ls}ma{y~,;y&j a 3{_j 4q=}@fo3wp̐gf2N0>kWT <v݌ly}7OG$cRO.}1iw; +%\&_>&}ik6^ҖRxGn=r6=s޿ĚR8?125eɏ.zeeK OG&_`յ{q"K~S|F3ېʻ~ϙ2Rj `Rnj4/'X$쮾\5׬5k{}zVB8#b'=P.IJiq\Ε%g K~&/?3=NgO=;FW%Z/Ao?fggL9S[VOw(9o7uCS w:u gΏПJEnl;q΂6Xlҽ>ɑ4r8EwTg=ދn]G]y~$`_rq,<ߩ1g7YMiw*]dN3*a|mKT΀t># s7S`Pgv4e* _4#~?jS~Ï[㎌c`;Nܟ_@yɕNt1o9<?f<1V<#zx!*R¶ n@@3hƟ p;;*I2zt?'Vqg@;;t@ތC<%yw|5C'ƫ|-RӞ9Xc|7ק@3_ 28 }9y_2/ƇB̉w<03뤄C VEU|dwIݳ==n/s/ iK5KT%Nq'`j/-l/ml]T@^~ZHp^JCDc&/"zW))JO%UqK 6_o)΃M[rǟMIƼ׀7i-+E̫26m0Q៹. `Z_= 9='wGDpٙl+tzRqSYȐ0t;{ 7 <7z_{۶T\}GUylpjk(٘ ZrSp0=@^o 2C B|i:Y4tJwtĩ@Ӌ` JAQ#ř'wlƉPo!,,w7JgKC2vT9C?6Tr-G_%.JpkܐKWbp_{)[a b,ϲ} 0ͳJ:{~>q3 Or3ϲ<@穡joaf)ؾD-3Awx0>_ -b Zm}nͳJ=63]U>S7>&O |t', ̋)×ܜ&kw 7R2fNliK*ʿ*NU6A  1W;zvW;ABgI +/<mBpq%gJU37g8,%AOTcr"8& f/IoJ0s5^Sx{3I0޽CF=ԣ=sK*[2DG}YΕL2L>hӏ6IfO1߃zW [w c}2n!?Pc9K @k۬37sYWsxqm&]Z~X# =nڲL*Kce&?ɕ,}Hxtڳ 3/TO}y)WŏnI=q__=1 {glw>Gwg$x8s_) E{yK4,f+E:ZG;㰿bYߋzŗ9N0Wy#ƹFs)2[y ΠpF)xӿ&7f|bo8x͟ezA:L0nߑoM?~u/rܗ?)x!SY wJwi+h\d=MA4nh&D߱Oxפ~ΘeXgŷK}.|95m&yX{*;NO]6U#9Vg$+ v0e(ģm^8d79xxA] ~j x*U'p<߽V{TޞIohƸ1Db9$F>_97 jj6 ]|^Қ$|(L}0ɔWon;p cdg`230M8ٴ']#xn|W_Cwpg =5&G{SqTu\[`z#9pTZ*\r_305>cJ_c cl[}n ` 1vAcD˦Ov|S[U Rx4SL=6ۿ!Tc#jlSnׇO5f^ߢ8 aߧ!4+46Bim̟g—n{x;qU9X;sI6bMs[8u=clx^swL3E hi_xm Yusqj9 o)[?w[aLsHsS§7d'3cH,;Lw^>*\j؆ypkxga[ew2Fqq9t=qA8/N)|61GsPǩ<7x?ĺ{cVX c{ӜvϽr%Jv3| :c>er=Wz0Ug@ǔ, gʮSeҾ֚˜}gqntƑ:v o>^[@a7= Xg9*/iPImV9!|5fR[/V[ |~v |lq,+@ekggf6כ)3ͭImnL*3Ιt7a,0,,,,5>=Vϊ6?{]GK)^]雠wпp7m ~@oPKsh,= 'k>߀rV6օs(M0~%?lD(9 >F*#rol_Yd[;eg0Msfmc>Ʀplljc7 (sH+Źui9ZGsoȒ$FAG\RuK7)˯np2proּ#iߢVΔ0Srƴo3Ёvw!|?;1|߅BQ-`W“ئWڻ-ĵf- ȟ6F r·?m";3 /pmXg;b;d9p=6͡ [%w)O~VsClgsP?[9sh,?2sh,Kcy4Kcy4wKcȹŻs[H?ϥKs\<8<9<<<قh59mnIpn[p[sG(γb3zݟB'~qR {ԞoGg 1kFo㳜1Ƶs'H̝Tscԉc+hl]@%.~Ƹ;xk-0`z{Kx /Bگ c,W\Xhsє9xpN>g*CmRy^ܕMxGZ0[hq XxG|S \.u2 =`cgx>=Jbwx]<3dhE9=);(ۉJ jh{8Z)[ 0;w~ 6Kk.,V: ?RgWµnp?/.nV0//*Ҏ>RZ.k 6"m}yv q 0v^,fb޹9XkЭlZ6f^IXՙ_ \ȷ&0ew_BσW;,leQw.o,o,~P~)ߙlKɖlKɖٻlKɖIlȖl6Ȗl%Ȗٴl]߲&QQq ũyxݻq~g~9ƿ% qn3\gGY:˩q<˽ dQTʐWwU]nWt\M78en Rxkާ 08¯ezm6o%(0o"tb6jyޏym #"Wz"U<ϼv͎wN w>0k5㑹$ûa sܸ~@q`,'rnd %;8]/k,45Tq0 ME_;Z9Er@یnja߅E3>qN1n 댳;G)W8ncb]Xs2^ƌwf:Ů S 9K}HG揁d6?վe^o'j6.hi!* +**jgNla6FIq71\.1{<?`3o0acۃySVwcۂ`j+ߒFk}q~J]F[ G~$?$t /7֫Rc_eF\1^H1oJ 塝֤]x '0>8d^Ÿ8b% # %m2S?pPw3yg;b>5#qːKvxW"Ӏy?}bmNk<Z"!|epFx -+lj>9nZU p;r몍 Iiq38}Ͻ?"iG~}'m2Amw(.;}am{r2FSaFm$`w#6w㝊O{cv P z*=kN< K-l2/1yݏ[ӿ}z<[|')x!l=N(k.qc, ~wf粼Z/ʋq\p=8?r_@/5-x2ƂUzƂ/Al#UNZD5v;yG~|lJz #Jw㘓w3!oI^{"|uz>1gC/)8ip)֢%U|:T"cr訵,,v.r8y*I3i4*rKs[0\ocӡ6uI^ ߁>z;Jg25g;1N@’tU!ŰUiӼ rOwo劂\K<Ǒii%X>?ʏ@" UJ.@yM,UbGC,W["go]`r wSa k koOܟb^eU:=i-,'*UAZ3Xk+"eKK|}mft3Xy9SzuUς1\poY|AS&?@yL1eY%s㗺ׁ%w3^]G߇g8IZty͎A:pZC;M&w#?Osk_o5W4mߵP~GkKbY>9kinOϨD4uPxF_ֳ%[E?1[Kupy=@2X x/+HSy}|JJMёӋw9Iw衋{ґ<{[< = OޟyWiqO>*@|2]dޘ<ܶ/'?jɼIx/~@M\H?fLy3dny2/:+ ļ̛׌y5N^A൅}yYu?)O0ߟ0o1&_rGcy=,}yУL =hN RAxRΊt>j%V>:f?O} .qf8Ig$ <8G8{hYOξLdt a`=o\OzB7D/O@r/n0Hmd{o ىs!kC.?:Ie瓓W8So$$͵I7\{4>x4&bLh_8˾4m'+ށ!MXϾu$wϻ`Lajڧw)W~{{>%$//,g[eFgNž-_/`<7XVQoa#h.ZIXkh bF yLKy |iYO/y_zWzm5~nVz){NG~>m2+[ Dx=t FxyD >`s:/Mp)[}cjAʎFOوmW< wi}T|!&n*θ=YxҼQTSf}l}/~h܀ G7;R$ ݳOp!nO*=ODsjS,gi҅' 2kcVi޻ev'q&y߆群$kJc9_>9οSȼj_95ڎ},w*rX~o0*|i|ܗo[w)|p[xl[8V|[z${/ݑ۽y|޵I:3O,k;)#7eUe7vSqFo72e}fZd~)抹 S<;͞656sskИI_L2G q?~h> ӇQ<*OAi/jΚ!shL}D[v ٹy[f 53Qyl8ǴI/P Ŝz=č*>?I<{ gE?KaA[ޒT\f+N;}?%~N88.7Qŋc?a<2 JKH#gji޼6:Klw]3Y[3=Wf1/77/vxӆ2[dq:WqīB/[]oU_ǷYa_jlo?etPx¿*kK*0dmK 2kC&R"񺘟Zi7}var٦='yeWXzKp6d~)bѧ_eg䗑ygKO98WyF~Ef+3(y *>#:=#<#{F~)zO}O7`~a~#_~a2P;7v$uK$DJD';Y$v tt~wHNFNo:2s&E~k3θ:ۜz{'SߙɦƫX*E gR8Np:_PE (_| U8¹ΥpNs(]JP6ٿ5}arTl4?s0kzF o?bYO O#iex],Aΰ̎swJ(k;eG`LfHp?͸H]9t:g̟|ʗ= <ȥF*-AJVnK̛ɼ#"tl+7 Dr[6Uũo`VYqߥxԓs-d;+>Sx2m1Zg_ vrfk[ 㙋d +}wo8NOVϓ=[ J$)Z/` ik/9s2NvA}'ZA9g%YGϳTw *NXҞ6Y^OsPyLj(A>vPX]JAJgQ֓)i=%|%ڇue^=7Z sa~.p¿9.]֢wulďt%tHp'>oҊ<{R쏔|V%-8io:STE E.zXF_zËo?d*7^izg^ 5^$'sR[,WgwCoH{r>/{jo䤝!>/sڷ?M>ȥy=X>[YVk0UJߊ}*T?m*8s?O5Vm^Y& Y4)TFN(뙺3o1chOYx;|(c</żG5u~ W4a~SwP}xTT%~/WT+n OPxޥ$)??g_TWQMW'*L7+C \PZ=o( Qx»>-c\H wWx+IwM3=iͽQaIf5/ 4:Z"k w:?`Ƀz4qVފcK[xՒ8swtOW8σwм,s3;/<3Qdw4(8i=qJy"Qs* Vudhoe=?qsI[a%R!|ah]٦iJfJ/g= :@l.~X#6QݚG(K3X]RSR cV~?"joK\3 +~g-k9]/&SaiKd_di ](y/BHߚ%xfoSTċkaa eյDze%> lbsw/9޳G{u8~wa=,m lƈ蟗`9bƃfG|wf՟Ï`Y1ͷ,JGQaaG&{mF)1x3Q%Yu%=KsGFmv336D_Aez4|MͿeS}Տ,<vu5/\AӇϖ<Kޙü @|(Zv9O)bc#}eVnj?3vr*17pA7Zn1Z';f,BCh2G-pzƿ/\<._Ϙv3?R&ݙ>Ew 彾'?I.7gW1/760cDZ2xx?-v9Pu#bkH?)N\AKλxx/wu˰\akϜ8i״f~T2 :ed)wܻ"7Z3k1/xwPfpj`MƑA=pl3P:hL䝽>EezN2s'KǼ[x3WC+q:-pK+*_.o2m8odžANe?g\-YnZӔg^]'t\rWsշ׏Zip|?gY[4,XrK8u_yڐkayyTmټTgȎ5a3$F/% 1jW=P{~sgQȎsrqT}~]֐3 dD?GuF \<|^)i]".\>xJ[(< wlU;I,7H_~EهxX}̖yvI2{|QׁVļ5WWw!]EgZ),Y_92c|䬯mh?'&o:H+7UCyߜ9_CGn_49Y̟o?S#r?:v?ۢ9 P̼wN{a?oCr;'2#ybM(ՙAWy5yꋋƚpJWyrO|g#}ou>U ](ض.03s/ֿg({ygAӧ\0u `j!P2+Ȟ࿝z^,sy߀LE1c9_,[l'3oœo̼=:\9:h=xFJǹ0v0-x).q8ę1>/E:/7te *ZwZ >O3G+Mg̈bI=T[y r~SΠzXr_&yeʞ;ߍux*t:?Quq"6[q0/hf_+x d%g t 'Na2O<qGy%=].,q rȏf 9sy<?\_n9b!u}"sEITuXϳ/o*Q G*pnQi?DPmWy)}Z5WF|$o#s3>ڜo ,7@< 77N/r8@Y1^];JX @0tVx‹^n+|Q?.pLͷ ,BTYwNOhɼv{C?<ћX~oO!>oҜՔ\w;m N@xe>V!E*O64Sy|;s>7~ߠcrobPUG~x2Mfugu8 7R:eG .Mnw#߃ &s$8>I?ż:92߼}9{*Eᝇ>и D:zОsuUG/u|`]sܵA#2~GIe9$[Yc2WYdG$F(&4xJp,7nd(Wwac/SZ 7U}_T7gBZ;`GzQࠬAmϳo/Q6 Ƙ/c`~pD$i@!G&{8Y'.}z mwvp?%%po>3ewooSz1mrKcN\$ʋnXչ~[n1*6cTGN;f*ycT;fwیQ1cT;퐞7Оv^wqz0?ZQ$37pnԼM9`XّdGӰӔg*Y )qx\7geo{E_/ƸW5pwxd7aWkK:p0G?qǘo۲'wEwx񵗃,,.VeuʺQI{fgiR;L;wIGWrӈ2w\UxYe3YI+|܏u离fݹS8? G38$g1ೆQ_oz̴{^r(j#;,ST]~韦. ~X澒q Gc(~[~r(Ӂ28VYI4V~T%jd_  \9̧ij qopwsan,0϶ǧy}ìy)험jG~Ṭ nzާ9s,_q TcE6;2==+2Ӕb٠x;3{*VGecw8.lYv19_==+Nzpq8 p^ƘRmU>vx̳$َ͙8#3N<$m4:ό g/Ai}yo/؊¨>ut8ws߼O|&p(;;1f-%M>J5f.rZK8 ۠%c|֑z0(e喧ȡK+<ӱ߅v<8潦8U' ʬrk}NkG_sg^M|  25;Dϸ{1y w#z 4Yܪw!/_QVoҋ?ixao5Z OQx[wR}Ym ce8v OYWz'ngi, ~t?Kg4giͳ܀{q(^giY'ks 5gw:6npK[)F So(p.p}0h++J#3eEkǟwVi*#ڛqsK뽬ЌRә/1[;i[bK7oghrw_ v{kYӼ@o<ˌy'@7ɲ$͵9|^Q#Up ao1oEa[e< +a#޹E8>~>5kxkOaߛCݺ+ !: a\o>^[j's~9|eOW rȽr+^o%5AL0Ti3FaNKzoY.<=؎α5*]eU;tEr;qiDO+/i2п4=_-֐—':3*Z/oQq31>㙙 8',8HO;pf*{y);쏺~*>(zpO׽x?v5oS[l/J;O8ۖ1ތqM?q `:ޘ?Xo]"RR 3-=,x1N\hP\9'_*inzp;SqS= \09T=|.[7&gu\vۇ\|Ɩj&w<k4 w Jhl=5z \\#-kCRFAHwNH(qC.p™S9ŘS9UC)C)m :~˜Yǜ]sO#:c&x3NRvih̘ۧߵ΁8J#)ap ь`لS\ș<|Y8<`?K:{؂%wcWKMDh :C' dpxIC>]6> St/b^ I9anD{(_SE'r4IC~Ӓ<}7R8}dxHe?;q6q2N\E_ 8z )ތ0y," pҦ4k<) ⸙/qJ7V)faBJ0G>B,ixfJHK+vE\|(6ʝ3rX—[lKb8 G-x_d2%{,pb33Ŵ-+i5T~ 9bpj]>7_G% ȳc @Y |e~#Z|Y 9GO9Ub&Vd %י͂'{yzКs Q/v1G;u`ac%-bψ-#v0bf9FKŎe*vhbmc}miT\.7 @z\pH_{)VϙoTK\*%eΙg TX9a={k=gzU,}Z=e._kb2oȸ0Saa8=O$׼cStq9ݻz=kBc=rܻ~fjZߖG[[.6Oޤ0|$,q"+{szyVMq3q\9U:XIxZ 1ϪzZ,OvN 1ɶ 8ymq)/g>Oϖ" vdN'8oV)BGpOsd1}mCy:²!~I6R'3U#J4Kӫ~32wcx/O9Jzb] ݿ+Z\ɪ!KGS/ib^S<=Luw+Wi)dQ|NƬIilOMq_̴] 2g?ނAiK'3gz-s#9d1ϓ3sl$Й7C8g^Ky) Wy) ', SbTYBT\H c<{ҙ1ϘBc518Tbƃx缼eB ՆN)C\jFs6⊿L1&y}cd1k5Y̚e3c|gev唎1>*?UaϨ8>s3Հ=`=.j/㳥}e%>7=`'x qF(FNa0,&fqyUHik.7g,ۋ9cL5)lymg|8vtbi,@s#q׌-(n dK綿}{ِzؘ8m9?)i8b{n?!gg*3Nw1>F vUo[g&MQT?W­flNfK5̚Ź8솓1n85fm>L) /Vx?(|Uĕ9To{(x__)ſs}_!7iQ(~\_(We$)<\wިQ(mU­{ WfDn dO2<_m<bo}wM,?ٗ򘽄yCuwV1ec9*1Sofcי"YݸcNJ{8 %pn1S:S-NypXWXe"iü S#:)<17;';[>\≯iww{.7 l7h2n6/A}όz>3vxNj|o8U TY͘Za1g$oNއ-S%Ľ't3˝U:(|[g)Ե=PA]8* wRxc0IS tDtxnB:Py^48Ջ-(Hł\/pQ*en{a/"}e lB$۩yw4<䤆,drBxrmf|9 x^HH% N_!s3Y'M7Ww1o{!nhYY( ĿZ/vMgVįp:99Lzb$o2ɚ"T gݲ+P8~u ZOz<7Ndi6/ 3@{݁=1ܡ=ߣ”M07ř_GH҉QÝ}Z}~oͻ"+tK/Y|㤂#,G gQ%?}W1%ܭg@8]> qg3<`Iܨ0WN/̫bWZ'ާ1:0⡮/xa^-e]-_0h[=ۆ$i#uXS./!R ̯xKyQ^j8Gc\әIڄyM,Ex1U.y¼VɧA1<[Ƹ2`E˓1 L7Rٷ=a lu+uwY.v7 3 ǖ#/5Y VX]YgFww'2o o1oBL=MTu$uLnh17U/AlēH8zw0{c7Ӛ;0ׯG;fl{E@2@ym˼y =Xr9 [܎v{E{_e<A[vA."_i×[w>+T|=NN1ïs5X2S /~pqk>QS?cY?ERvrVQ8| z'תyUJٌ GV u+3p!0o=ۤ&VU2ok?_[{9!8hV8Əor6`G=c8| \tpn+^Yl +Dx!ǵwsApq]1oeOO7wL> qߟQ5, ?q\Kk\]l`W(.S $EgG+)?*{Q(KEֱo4+AwT͓d;gG~(˟|Jgg.c$e_n(?so?/w/XいK*[̻by³-{)Xʒ1{$jc=~9w+^ z"'J%iݴEٖWGcLWݱN_ 0)>oQ6,ڭ^T߶ {AGj/I]bn/ə³g *EzZs-5U ?;󝋾/x^-Ec{R&|8>E pxp@hIK:$IsX>io{.7G8rk\'2/y1s0/*/;U8K| wygQ4Ok_|^1뿘9y'דQ .ĩ=M dRBɦłqAq&YVՌ,^4+E!P;SW]U7bɥpwi|a փn{p1\仳|>՞Ξq.10]K8θWVμ̓y!/kE3,W)r-,9v6;rEևx aEmPX*w[ۤȍzWIȽ07JP{r~ʿ KQ)}Ycqӆy:D/ǩ< mrroLi">#嫪2cndc^<39ͼZV[ufEQE&^''܋ KןD½Tg:vy.)M'V5,),*giv҆R)9 Ae(x;rDy~)wi rμn~]<]{?Ur|+]#z\q29{tb^'Մyy}rއ¿QXt:y ky*؟нrC~Aeyx1 K!'LvE%~K6,lmQm\la=Aϛeɾ+гlWa{`g˜ձ֘?$PN@qW?y͍  m~¼)V{¡"§|B&buNlo^.TgO(ٽҳ8̲ dla=Y3"M-~%#xL[MC(nKmkB?G͸z6p 2?;͙( Dr!=UBuύiDӆYRX/&l/5dFeMn^*CS_K~xsg7;E<%QlY$#뙲i G'UF}@g4 <=ɛ$\r)Osɛ?q{,` JH]2?R OXqU:1/) ;s-\7;kν9#"|9Ü-A>)C\,Ol4p1ﲻ]1k}>|[_D Tї2+ y86+cb~A}!+|nM Vw qZezovHk7Ld~QPo1u;܃r+pX 7,W?>+p \)Xý{,W?} Ur5aebV;吏{ E_CO/*̫@135c~gc{kv֝k<U>7>~13ݬd~ne~{V ՇXj{e-9,kx8eYt,U*,)E?ݘy=qS*©7{z򽲬ýW*U5EI|@,3$ q p2zόwp9\*b'ٝV1ر4w .!\;p{kWNNg g {ۿfdq1%g%AUwE!,8fLcPN5UJ 6ln{!q,Y:r2z" HbVtɺ N\!.3_3o{Y?'0"+ܼNx^*4^Ay?J'N,GT7.&/[%'ԍnvkK}w 8?xUhK2=?mW(>.½߻Zu:P |, w`"kJ-ޤPo ӥȮKI-e6ݺ+v;A9tIp LjNxԉZnqs5;nLm{k;*s#|Wo.8?W[%E*ͣwL_ԧ~Y3 7LRUcqV~6wf1o#);5 /uqNW8Y7ֺ_H,h`?n[O~m9!2^a.0ew-#Db!1W/g^'ΟVe=X]Ü_U8=KuHoq:QL^:tngjs?YcHAg(<#PWşCcbVɽ+6:T!8c/ߦijӌS=AK,ȽV7a0rC֎GZt`=.zdqӵQtڐ󨶯\\6q`>?EmZmvÀKanNnY>oY|4\gdJy,"j@ qDzھ#? ="? fZ=rum됼4c~ZzQ۷ \:P\X6iq/S=Qb4W1tQsDf'* ,<.iN]SԵ\_Ԧ_֦cSv~Mw\ɣjtզ_?ܬ̩B :..rw +T)V^ XfrZW73PB94-EwgA xmu5r~ kz,4$b; 797'$=G?xIzWg&{~<.sxs 9.o3 O$=uu^ŗ0N {r^Fs;V[nP9{?=gUm;Ɉ)9k?ա[؆c;ɮctalVxh9{y/6,r;8uY5ƈN1%W8¶Ңuy;p%]͙ +õ둻1 >#H-|Is^ Vf܌w`9תG{sfƜb~]Q0-}?.e"w!d~"XU+Cb^{U: 9.ZT}~N)(>YcN|wƟ7yrӖD2޴vK=i1r=Rʼ~OBx7ya\{>9y]-gÿׄ ,'rB;oo* O(p_6kYL`a?(A;μ^OI>7`#p{bLoCbm~$y?mcdm}ϰD{ME\<<2l`ci %pڤq<^ G*`70HvZ8ۦt\Ѐk^瀿g@v33'TДАğc7Ϛ %tye*'/rYnߖ*!̛|qϘ|+ߙ|fR^q;M3\[[A=qO쾏G khsaY:!Fw~Iٹ]CZCۯhwϿ? c{s2wJE72wt29^TXjvğȭ5d?T22GEgN?AizQSH $6z\05Moϸ[I7)7Yf^:TX- >RėD5W?8|7V1?•<҂w}~٤%w:}c{}r{.K5B}f$9:XH>?mJNő4iE(o}raվS)&;j5K6u.O2g=ǭdwR7[o&d/D"~='ºز2^N,w(̿(1V8e6c+.UDeI\o^PY ,{qF65%XwđuM|we,Wt06vdRK3>'| C?2ՖgW/Ed{\|yAW') ig3?2P՛km޵aMݓf+&Zư7 K,2??%tEs #U&G!9c]rSXޯ7uZ+v3/~Qagx3 >f[CKo -gX<'w\jBڧ"l,&='|;bc3(4:Ē ĉ+7 j͘JIn8L s)ǓUaɣؕxkT~/`)0-x.&':,/Asۡf~aw_/}7_T9t) nXsǼB;g^I?׎Wfa+G9_JM`zq5?cWiWuU#ɺVsz#^gAG(oӯ9Ã<<ܐ+Yjؾ{;`w]K{Vzsj*=T[ss%:V<#:᥊R\ۙ+&L~5 \s#c^Ukv-$Ex3wߩނE zV׿={emnAηgo-SKyS%m .Lpp "pq\OW@>mlb2kEgq{[)Vw%xc"`:}v7uv x.ikw (N[Ÿ[{ؿ7oX6E| =ߎeWp恫shXJ_'>|c8],N+w88XsETzr*NOw=o=?' ޿yee#iVya) |CaaG[c; jC#T8cN/'<>ZW8|,We\:P66fG 3|FzqgɫEtoCc~_;mMu}zxmmh^w;C_3 :!1xRśkyvH/;F=EıwZ|wncouGN's'{(pE7`~[`1o2e>+S(`"”eai::ۗ9yRxFLZԞl/vTZs- $M;wW]HNGC- teP^X ?ֳtzdLoX4,w*@OՁMt9 @nIv+gLgr~ox}g; yv*w^Zve^ʼȾ>i9iyG? _1Ϲj_#aHw<5;R;mڑ۰᛬'V:+Wt4AQhlNlHN4WDsEN4䦅y9ՙӊ8];Q Dg{v ;5&w-s7̜qCvw4cLav4əNf<߹Oٙқ3[lW?W1kj >!ۙtL{wݙcqLeeg*흩lu9,kH tZBL<b~_5߽vBHՅ򑫋9#$wpX?ĄJ}>a>kYy.>RJCjNJ]L~u1mE(](u⤺JgvԞusx:ptL{Lty=*3? 8+3 dqGw1kDy2o,W Ψp1Y %H_W( 9ϻг-˭x)x9}oc_X.ҟy\qhQ^B}.ԗB})+ȮԗvK>blE0%X7<t`Q\ǎi񨧫y>,椎_2-ӕZУ9%^2ԕEvF^7"=G;2] ?ʫ6yXXXCMd ݟwY֕ƻ ]iӕ3]Jm߮bQ[Fmn֪ti7z_miwI9=85nm໭6>uF ]X>h=*+3N\:y)}&3mR7nEU?}]WS r^.&0b:8Ʒ}VJ{4|}'d`u3nv3nU<}s*yv=s0'a=~F -/I:^xØ, {&\畢SpuPW7rfa/<1aLN֝Sܝԧ>N}ԧu>u;_SK8>sAFΦNSq43㉜x|X17]K}<0曐z=ITnqb.5{M87}i WU•p%?s{y(%kxMM6>2JW*A :}q=(d)]T\/ͯr!ArOټt`73kg@iNI}Լ7~{,rTާ+J>›Zq[i8$/ݙ?ؚsm8 e^(ilo{S$8=DMWLl!M;aGIzƑޤq$eO+GOґr%gM3x8KzqˎU|댑*L{wcӄCzXQg3Y78oW=um^.WdT5nPTl?F OPG_4x|S d($|ړO#*YoQ)ַ=P8S/jQ{h 2EX~>*%LOM koߍyfp=pG"uw:Y O=ѹ?Q/j8NB;+go:?Wx+_gڏu?/gDܯ7?}i GTQm6e_}[Ie5Q_]ωUoT]a;7_w9^~wdz-W}S1<+[8jO8jUzUzv] ,+3\qo\WM};m{S:;8$ƉKA`gs8g> U|]`MkgL穋{ n'p; gt*nNע22G`N`JۃM q.ҙԱ[Fڛ="C̹ƊϪk%Ja;~m2_֓]QcH=5d4ϻjM1OLAָ${ykO*_N~b|y)?&˦\]}_ktF25:#܍/fs`xȏCvK~>&J1OE6x~(jsrVE敱`y^#a-gCxzp/+w_YѻëH62z/g^<C/1upѓlYzڡ=b~w _Ι5d~1TH'+ G!ٚkdIq"|ٗzpikPη4gTe燑8ɞe,=9ȗse'OI na˔@9'e@n)G{jQSqa=^eQu֟A3^ Wӽ×Zs 'mv +;Gsok's!l_&~@6q(E?P:Vx]E:j%󡲡q:NOc7aُvIΧ83Vs\G;~(_ ߂KJګ=EIEy rb(E|Tu/g=һkqv9W{inS'7:=Q&_>6*EGo gƽ+ ;Ld^*LEAO}ݛ1%͓k{)0A]b~i2:Ǽt*^ G!7Vb xYi7-9Y[dLU~Ty9tL_[%}d2Y&/Q =>H0Әż[0bb;w,܃3RSݍ4J> B\+8^<#tP7_E.g%Yf|17Rȅot%}a_Ґ`cH + Oo旻8gmOݻ_Dgx1O딯S;li>UXPx "sM )`ށKJg ΖӅ9#3{y? ܱY5!Fgħw|I7+󘋯ֹYn,vo\DٙAN k^' /*wbRV2`\x-[; z_~sWO3 c"ĵ\:( P'4_cFשn|uWۭ꼮T_uTl' ]MVy|;*ߎ+^ߦܭoqz)# ?<DgFƓЉ^Xv&xҖ-ۯG2-u+r%XU"gST<d嚱\kޕnލípҗK>+u0aKmUT2 {[h- r Y/ W[T<dd'&γnzHlCt]fhWp"|pD+&x7sI /?ˏiv9-:%?o3n^Ay}Wi9#2saXn.o1ƀo2Y8&;J/%~SO P=$KZ8ϰSPֳ`U x|c&wo^=q s+N_`u5Ƶ ro kk oy 1i@:G8f #?Ќf]Y ")ѻ1ysޅVڵqn@`eQ9Żq- R࿮0,f,kwX~՟?M ˭S0dj]xV;fV4 ,NՏv`J]+YnJ'dfVa^Tz< GlFd;YHV-fd./ߛºrXEVv)&Sy{^YLkUf/e[?vu@ ]:9@JKZ{9{t=}y2;d/OmX},./H|KIE{ˁ?y'[;i͙NH< Cw>af}sl1};g#r6:'D *9?3 qX!Pr]eܫ3&zC"9빢vb5A82V cPy {( :Fq/ߚAΊ!omB֑!B׽!Fm~;5]d32n5p'\d)-|d;2Xgm׫rU*9do|/w\4oc˿^p|z__wK?yኇ#<f0/2Yüh9Zqۃv)nzX0gd=z܌̋}BiCʲK⑄W8;ccʧё%iV{}_mGYkՊdl pqshrRd^T.w ob] xroFFs/vOaa;xH{ZA;P0}ϧd]\ڸ#,W*O"'+a\wtToR^z]5|y6?+/ r/5IkRz+̼w5#kј޻FbzWf0s#\@c[j0|>%ylBqVSck޲Lѿ0 o~Ϯĵ?W6aG;vo0zc>|߼7)7tFtV3/p+\β)O = C0(=Y2:{tqN\uw0β=L ;ɲxF!RJB_:$eJ! *+>qϡb᛬D}З\@(ঀO@QJ97'+hxg#-1g?Ɯf*[qo p[6guFrGlʽ o(\S;)ʽr]%19kf잋?0L[M`}wE\R:}k/.k ..T$Y /·򽭴67Y__ntWM(t-my]n%wFo}u Ux·/?Oig# #_*<+h܏G ݈/C~g0:lqe<㽧'Ρ1fgd*d5j[m& 7qI6Il(w=}CZCh\{y 7h$`UCwMfV\pΈ8/ǧ|y󷪸x8:5n:mgi}P!Ve6PFMf@UƤID*$OʔIYAߐ{P2p{[2l?qЂkz > 17^Xwe>Z|y昻^WyXዾFQ_b[oSJoQ߇ʼ?(sP7~ 3V}?])>Y5t\l,Oc.(>.wJܗq\0>⼕ ڈ]c]aŢs@D472<k&nQI;Qf?򥮎N꓄3߆<"t3}%Ük^JaaPbE:wU ƙqΝy>2:;S0Gn81st>MܤwqPaxMǽ;n4' h7{|gZN*T>/.|y`tǵIi]Ձ?tC,CpQ?OtH"dCѴu'ֳ ba5'k/Y*Թw廁?{x}Fә7eǩk%{j8#N{xf?-hgb4V;ߍ6GS[~4햿/hZO2/K99&̷/~0}qTr*=-w}OДqw|.p282/w܊~qM˜w;*f&i= Wx>p}XiqK?Ekfǚt %"ZcYmrN;ֽ{.&sq1#(h3ꩺq̼G | H3/^fpo81EGpd?*X]űTXZr,~ԏcL1@qu\`$HGq|?8Q}h>gm<707_0m݁qt}5H铉TL4m"էT~HDO>D$O&Q{O4nCwէ}\D)Tr@T@>}է?P}_`Q)iY+ΓsZq1o?Xmу*zD\ /a+;6)/a{o*c_N1w^,G-e? 㢔#qJ pɦ>Ki. =핬Hu2T:1OdgM0U+YZV9.:O:>='(Tdc|ƞʞyʞ7{0^e3|{OPQTwO[b tv Óvۦnt[0 [Ϯ24^`ƻX^8 _t~,7zZoOsvoOYkvo}`L6ߚ>-)86BmxB'e4AQ'o4ټH爙:pɧ>=ݴ % `l[=}RYC7j^0=_N{,ns8oޏƫb!<ű_t<)b3T*Ke~aLـFq6!!I=!ܢ(JXq*, 4z Gzh8Cslk$rCfS{y9b\B\^O:NMziOo$\q|jIb|;}6{\_p/4ѷOĽFvǂ#N9͂ 4)B8ܼXĽ:%A{qf-8\}Qֿyz3q>˲<nEn'+ϐ O9, *[U*'+O!H: lὦ«8?o_쏟ŎV?o#XxX$ EΧ'`e]9՟oCoz]txonSA9?!<фÊIvPvQ{r̎V~Y{;g[s9wUE9xZ{*P;]|'ݑ{s?z8 ~ۙuMKkg :-]g %Τx8^Ò\9tYfDEc8?+7',1 BO(w0f~wDѐdݵ֬G7|s#,}|!X1oR\/_23_{[%w/gҞLqE׮ĞqAPg/9d͢/_p* )UHJN΢=P$^ߵtD!)lL9\LՔDs<1 g>ӂTt3/ 7I w OV~{I mB~ؿlV!.NlC:WuaxC- lSWuY:ԩd fgTb8X$xoڼ7KJlʴvBΦv =+}{ tٿǀ:9":ŒL8sɉ^an,sy{L7x>`w9Zx(eo~ ^C5sLW֚HuQ7ڿ #/;vtW!4t'.E:R`7O,&Txк;qR8}>&cC1!s^rJV?qO}3 acf(cp#yP}yusCYxN8pFơs3\q8 #{| pmvdz'&"3q\owxaaC8Y4ڥݶrp?& VtZNimJl#q~gl$r˵fq҃I͘n1WD,0{Z) wکY+t܇^&'/WZ`ht8(qb\䮨eƥhjo,0Zk[M\ۓQS7ED?rh8q5ltb/0#v%%9/m/0wiY w0ƫ1@䄱\`)#y*i2fi&/0eQR6c=ٻPVɆ. \_NJK< QԃkUƓ$ M[}nORF0<}:<'tz5ma$^r-r&\H6/mwG8, N|dsхfub9傄[}B7Kyk81;X8u\/?,?{by\Uq fK9?n!-![ׅv؏E 錵;m>5Nh`D=x_&Ls{=ݟ􃭿qz%߱s>e!n,oO H,v 珳GQ}ycL<OY<":Rt: \":ck1dQ>-_Dg˶/e9h3z1slYtb:[z1O]ٲl٘tlb:[i1-}1-j&ZBg53/EYZKf%tVw3r_Bq>c yKK|% ;D1n&'f''OOlO(xs^g ufO':ztau3IS[OSJ m@Ϙ'jp]<$L.k}ϋ~Yn;`MPƘsC|~n띲0t]b~SR Uhfp6dւ|[w6~2`gbw"5:m$Wzg:=-qf1Ve|o#Y7Y{k3~d1n K 1`lYJ{qIp_-v^ø*ൌkN`\bEN8Ǵ:rod| &@f^na|(g'A0\ddYoM81NP=Tls26 7jr&(pۑ=œ*߭pwÿ.3{ ^Yfme$dކc\G ώͺL~z/>>wA2mze~q\{s׽uy~a S!lyox1e8c' cÓ1)<˃'Fm,K9x횝~L_AګN&mg q 1q\|> Vr'pyyA-S0ݶrj-r>b5I8r>8`Y܌]4ܴ׾h-+Gkq*}N4MpZS)Ln V:WW28REZv5sU W _Qxg)‹%(U% Sx:WtVx oQx›ޤF7(Sޮp:Uͧp^([~Q\ T8Φ gU8¥~E p +\L Q….p^ܼ':r{'R;Wm̓vAS>r _9X2؈s~ ]VUec"Ʉ Q+h=?|t뾫~{םcf=vB czh2k+^A`z_o!}p3#Vn tCop+C©89Lڪ02O5`Voe%/ vO)R~<gwUqϾ`>>Wk$nAxpAvNH=K?ͽVgK_Ku&=P{-_Yg++\gmjQזyRxsw5]ϼhF+޻=kzP?r *r{MLތR3GoP>8pφgKTe{uroܓt{_}rǟgm sз='3?)?`L,a^$lϟʦ*=aqϥK(ʽro{0^ᙊR P mfKz|; ]7LfNWi|˄HXװ]ƱY.%'Cz:>me.>r\&1=[=IY9T88c\.N8Iv ]˲6xo$OWU+rلyٿhM߲ʃ/l J|8@Q W78}4l26Jw:++?2M?^a^˸xe'vxSAʆ]"̯W0BW:ff3ʝxz1^]?2y'aK]˼ۺٿ|`wc,IzFfoMn!^ˁy)y .p-j@gq:d=Dl.S.~,c޻xX'0BMrSx_C?T9'?wc<;YOgfye#V)v#X''$ Gމo.YOWsc.8_DZe: +XVf 3?^O Wr=ٛr(sz7g}Cdm>{J>zܕ")AHxOt\ c=@ϜmfmDo7>vSV+o7vSFzl7?iMݤ&}R0q,Ysc;7v;34vVi|sNc皝S;)v;_elٟqHWxw~np_(C mpI(,'*@9|ZY}"˯d;־'U}'Y~'vkބ@ﳴ!t$٠@z}n,%HyY~ۿȟ@<ֻ?K,):Dϳ{I^Гw/Y] =jyIw2U8gwZWaݑ{%R9Sq}5g4CGMs{=mg=N϶=֡>=񝅍a7ŜGg|q {{n񀯊NWo.ږˈCy0't_s'9$b3laNZy|_r7C >bsfqrK@~\Dtm}TNyJcЂ;!nS ?mwĿ>:TyYzV8qx*=!ybS wyvOS ЖjR?.Yv~ʐu̻bâ߶{Yv _$n2y4<ȷ&_m{KE޳ߒebls$^4aKǣyJcw؃ρS+G;rNpį&Is3 vw={Cߓ~>ﳙ=ٖc wf71W!^ïigth{s~F_ο8W[oFX]x`^+J[i(N>%S kmK< D% fG'}X_o`;[oRMC8iT YQ3}dZfxĎSY/ku3>˽`Ƚ_OFs/x|ߌ`N|h';1w P0 ?(;/{'t_]NǮN8߾(7syڃT~r~*|)l}cc_/(vee13 uŘWxƟ9_KtoUs3~fok+ r=sw{L):n*@<Hz9W x/trϢW"wA:^ r4ls{ 3AAzA ;6 ;6 3 5L.E]OߡW2Dۗ~d^CŽIֲY_9;ΐܑN 9 S`|[O\]| ZauC/1Sxٿq9/c[AkDy#irM]”G1X;kZZeRx71GFiPrm}Z='}PZ%=LAx# LOcW -0pZ9I; ;tA3Bxup%!,3 d&J?ՅҚϼ.V oj؆yY s՟nuV'mnɼ&}$m$#5yvHTg{]3r{i^6 ү4 1l1Jʖabzs7ˉ{{+i$9>7w /%qDZ]W}#ʀ͗g[rlb˓d〟2U8jҡQywsw_q[7ߘɹfx1xL9/(طa+?)riSƸ:+?OL$[̟m‹:Fsr/=l̛{򸽥Xnr3oq;ѿ5/Ege**Q('2o*+=_c֨|U֒Y+WT1?һ/[oه}Ww.o @zvLoCYƸ]ͼs)o4^shL8ޡ6ZyotWA.C~3wG=}KdßÜCc:=yPx=D)>+9[i `aAeoytyGCaț:_bo"_ϼ߃/ya*|Y^~XgVI0+9/4J8iȸۆMp]p/nqՌȴ7T~q;kdo-DՃ`Zr!Jm%,rOoPU(w+&nGIEr{^צ,NjewW;Xmk;pG_V dN-g& F2~imC4_G*? |Xe7|.eOr6bgMa8.YR"0vG\ T68^ wO&4ᯓ؛q&BWlOZ@z wv_QGaVy:zէoa`~5d` uL2~G1oȣ(}axÎ_*~rŗg0;UCTVaq]kӴo%G>A &G|{?In7I,^%_y>WJ5? 7g?cTSExmc(rClfOՖ;rs淙7C*;s!p2Ee<-fI/P;1o5IU0 ]_i6آˬ(UpRlom: e'/vodxϞg-VY\2ײܾ%woSC4'< 9ނ_#܎!^G=ߍc(=زEB[t_!Dr0Z ?y7[aYǀt5cini-0띘 ϬP_e `YcZ;=:65e C ?s#ߜ 2WXb s+^ǩ MߝC_ =eZ/_ )jҸ, Ζ?[z1]fLƦ;5v6 *nb} /Dq <'#wt/sf\֡ql<2iAgws@qhٲan^O9_b^e}B2ؑI-~FAO)/2?ƲKnPoi3/}пYd,R¼xK<*^O[g .52{*lN;?󳫰1/ҋu)g;/Zi?4ә7ϹFGP1/?-eΙ'.` &e׮acZn?Yy>`~ ָ͡7ˎrfłL7#\=9rW™u.pxX^W :K9 GR<8؅MrϫNcźRyvPʼAlc! _)|a~'pܵԏe̫iDfy_ďµ(_72{gגiN=65`Чȋ k(0J#e1.?O>p\3eORM*}w4)''/>E>˷ `_ yrmT:~^ +ɼw-UA,GdTlK!'_Ab^'ڍ;q~/,)8cz: Uzs"/r}Y#NaI'[dg~RXd?A'wJvGΰ"/rY 74܈!ޯ-}{`a>XGHGn^*,eӮr2[[cm2&y0o:0>“&iE;'1o4+,2Ȭf޸ q|Ƽղז5¼IF^ܶ>&'ka75@'e)%~IJK.<){?ByUrw\齍}x} e}nݛ.O|11)va;Y [n \vab]mxZ݃" ߌ~0r"s0;I~#{ghH9KiCO 7v|cd;2D V\9X&˿%7ߊ۳6˿dzwoOxs%zg-]\׿6xWk1lK80'll<M7V߮9srP 'sbg⧃+ٿTD :_8?qZ:̯q~}x{S_] VOfK_[WK/c` sS+xy1us?qӪ6I[%uO#S]1X//yؽDݾxct\O?D?R/s",޽u4:ICGa¡J忼DG*~)~b0O , 2Ѽ9}繉8NNd&L?Z o+Y~  /Uxb[{C,^OO*̋z^I+9VTq$N&+|ߕ!WEqi1®F=8e^}ô?297th a<M<A;U=08ơ3~38qG8 ˌW Ksaim5 iC p U&D) ~1S3Ep?=~O)Rwޤr<_x"מ--'Mwl|{ؘpؘvsn;o;+o^ަqå};}tw/ݡ3r\};}FCP&1uo#y뾁1y$0W¡w kD`P*[琯-s6.g`Pg f~3qj*A%:--] V<¹Nu^l3:qX.J:=f<=tQ2 Sh*r@Zĩ=~Xkc3i=^x }qOu߸+ Wx6>ǁmG* OPxr?( U+#R+|L ?z`lNq/pi*@ TxE?e V1V81ykS6c6mڬЇ}HpԆzH O\OzvS4X_gǛquO$;]!}ws7|Fܵ=|M1 \G[2+7|w+R/0 f09ٻzZi#{o9O| +qAI;UR-`Ghk؝GKx|㡖; 5oG޿Cޛs |;l$퍵zN0F=8%kabst9^g_KVq;2/ǚpX>yXg1ôX*b>::XYqu&7zhL9 wp'ONv]Vi^xr| Ʌ|Zyl_fu5T8s_R6d|ڼnYn\={\i%,(n wcU|fG*n_`ټ ^z 0{Tϼ[@h~+]j˼(W'=fDXIRKAexF *BvC$\d]憥3tNjK)ixJګO=JIyKQujƸf>ٳaؽMљi+, |c=]"NܟS8+i#ke늯27_3y8pμ^B*,L8~͊qO|bzX8Uq(xԏʙb޼~6`^isSZxT6z1,GXaiaY~ +.|z,`V(z3̫ VzcQIUk"/{ŢlV,/5׍H |<XN4!̫ i'2y @fSdp8d.(1KpC댯meao]x֪(7/&Yy-FZS;)˘e 7Q:_ɭ C`xZ7NL}Ygg1ƳV<(|P<嫤 }f3Y(xs/kzN&& ut6ɼT} Ļ9͜9}G>lp:u>zyCTZ9i҂yC9_(<9c8g(DJ7/(2ƾPǬzgO^ds֤%}Tb:'l#+ʒ1 }*[̛l,B߲4rG4ʐ;Zg|GS3e/cC̛ [ Gg ,3P)z^`|0쀣Q۲ܢ eO0oCOfOAn)-bmgJu<;żA;7żت 8#ї##o3秤` eG͘Y}Ƽdfeo?]ı"X=D뙿Ix;ȼ_wFTx2bsUa^oAƐI6X>c 7\8Q Ux1Zerpg6>’ԚVfޟXW}_ឌq=xDf? >p {l5o]R/C]QS$Y=H ̿iwxe|8sF>y1>w?UZyAfRK[L2{#YׅOٿ;皊2/k* wj묦\Nj {_{>8J + 3d^[%]yiù{KCm|:4=]/Ge;rퟁ$U.w|>,3q~Ԁ2bn+iϩ.NYgr"'>bg=ϟ"T1}3/pMxES:+>/jM[>I$/0sx?}ME7T+P9o8@{Г. Ίc[g!aa3>Za7SyewuKEc;'a.Xm u̻DNsE9 cPH 36!SC:U|[M߼YKo| $/.Mpʽ szpp 8n=._y|b!XS-_uYsc~C"6?{,ݟQڂc[W?|y[Y>)XdOgղ|g_@~#ǀ!gt:A5'|队 .pVB8@f OI~ސ図|&V2Б岀\CRcNc^v-aA =x ʽ!~0XfiqOe^m\eo ơɿags]l}U٦ot۟v䗅~?1sES {."s1PqFk;߾ȗRp~gJcXo[ot潣8]_V#e'v0Gx7V;(:s +j E-~3Ū}j|3>Sc({y*O. }ߣ:d]""ydQH{IsW \o+ܗ[\7w}A](flhw=pxIfs ?3m r]?LH=:$ʾwy/kZ???>\Y6eED>Shp\wάgr('f57X8~P&x:FTۭp4þVÏhpgo,?UiZ_cVV 0?o(܁_yRX§C{Or(@SJg?F߯.XNߩY=[9!^/_ ;3w*\e2Dߛ/vf>ȗg?c^Bzyx=>5cJbIlIS(iޑӃi;̴wj4bUO=۾I1orv6Woo=uh: gU< Vg6^نo*_Wݩ8-WT1w|W.&';㾀3xOf<|?z`<3=>ӗ/ߛU6ݲ쓾Stߡ {ǿ?UiO[OV^x>5klg5WԶ|У\KqlG{ {8fӝ8E)ASxc|g得q+uFyM*sN4y/uƸ~ZMϟ(g!˖ ~T;"we'cʃ tT;P8?N׾n73n@Sv:O7=e& NeRcI95Ae{qi$l:9-o g@^q&ߊ{Fzͻxx5NO |jV=ɡq܃,s/W^y矈wX&Vp00qflqfqط[DyZMqr*P\jTs8.s@zgʌsaz0cСADJVQ ,̽+*·1n*;/2t}5iX32D[l 8q3u֌k²9?TќZyP/~_A5N,S[ѤId%2 /1Ƹf4cLjL:QɤÊta.ӡu%g6muPdVJTnWWκUSZN\9ֵ)3eEU}`.,c|QU½ࣂ1lB[b6y*4(x7ѯNaNc$zuj[T7c{e!.|!#${_:緶 s;/1R9vXɹnQ_~SLc:sNq3S:P}i;<\O݇'LbqI*xb8)O{:=N~99鏇g(<k&)k_$AyG-PΝALՠSE O,'cy8"w/"&x*|B'3Ω>i]IzMYM*w$I;dI5).KkRlIPqu~7j9Z_/"ukQ:v'!g;jQλ|RM %2\tMܷgYmb|6[یKg6RڴOmc}W:=#ڔRm397Hu>pޑܐ<,k,uI}3I:q8^[/C9YIuXI3Vo['Oχuxu8iߊ:fiEcEbQ.C;/CbzE]w@;&AѮ(,[I{ݽ @: WL>wřWYc)wU줖j+K8bm wmޠXweFCk an ̎Ris*㕞mVX9,P% oWx«-3zu36HၯuI oT_S8Q ^>@ ;nk?K!]ߟI?w"ދaӖ_fC _U8Iz=xm"5d[e/}78~M& wV^~+&]70MWxsީRN;sb9.J­BA hhzVS8e#s62jʽŸ+cxZ:R1Ƹ'baqLӒ쟈m=gd>&Kq~_1Õe k'n8p''c2uS#E(K']t̿3ˑt)5c&bX-2LiIegqKZޒOlν=OXh5wh+L~/z[t^j`heQ/9ӛ碼 c wFu|ߌ _iEkkR=&\׊g)}p0ox^Rwٽ=ފ׬㯹{VՏUܻhejV x w)I'sފ/1}D%u>u>/wĻϺblJXa/vRMWo/dVL=SLPߜWG㼀̻1oI^~`oknCO_HgmpMe9#n G pb]'oM{͘/zM}Yo-_Yk 2/.Ҭmi_kihi0OKmXO}ݺԞa.= 8RV簰}x]<8~X x]3l.Qx?z{~s"z^4V9&}ք$9@{}M-`‹{2W!zmeF("L W+c'(X>Ag'lRU&(qHI[}8Ogv*X¿rbˢ^cMG58u[ewss=25g08q;q}l;[vp\Bն4o֖.my>6Ώmi|8N421ٮȯD;Վ|~^_ܺ)[]<B%ub|ݾ/ {r:d߽gQ\sh/ HHLc 'IQӀ׵sCQM|ƘV&q 2V8.D{#JG8Y w,preؽs^9΃)3#aĐ/jcĿdndu.k=2)ŲpYM2. A1^  []*.DKX9YOOO?/ecsM^} #ߙYvgߩT ¼vL{#ϯ'}Î9$8 J3?t][s۟|؞Ӟڟ=۞1=:ОY؞8,JTײuߊvUoxvx@䝲xxRB\vb8ƲVj8-Lxh1{8Zɛ:P@irIdGӦH<cT N t4yFrcew_[񽂥 ޭE)쟋-1ofux`{N}tϫ͏x~M7W+>}Pl{5x́EIO#ncxKf ^θc<xƸHqqYp ㉀d}ureNSm vk1-]e3~pyx݀Cfw̫C/X'nk\88Q) S2NL*@0^:qS9c\puc\pSƕe\p'UR9!]WތkNn5}6ڐ7f7奵=_G:q>ø8s"KaYųKcIq\pbX2/g"z[1>IIm[@fg;1\7CQPc 1ip _^\{SӚ/l{5l{b Kܣ-^Ƹz'F94[O}{p _-]]tL\$WsvtGu&F11L̫ѼZw_Mdˇ5jao9k|%?rh@N[MAო~e%-yhx AoLvMn~*Wʯ6.fgY: -Y7č%流0a$_6 9?Jzdϭsޣ);8Qo2Gw߼7b|{.YxjCMR#R#\#\1)rXz}d]`m[/g0tXەM1|dڶm3Vz;@E~l{M]6?䣴I^'`;[mȗbbOT=/I4&ќ庪6F˽mqg ܷӋ݃vh8.I̋b7XZ'm^RviއwK{w}@O⿥/N@XT)$܃awl'F3vbc{.dmO(Mqtuad#gwG'~v%\y_,3~y m^F-]M_8~]i}2^jUѸ;7iw좮4>QslO]<9朙nZb _{sf|}6-Z3%,>g&g0u5g{0 eZ% Q!fƒ.XDTݯ֩UxLLo[{Wʽ?wGyE|J;qgd__Ǫo /8Yu\Ml}b mE0a?pn۲+1;bkt//c^-ٟYw+/OYAxx6a}6.ƴG5kk~8Sa?΀nĻUX!N{E._f~$,tFt7K\%>Tk.ט^vn,JwIo<2&'įYVɾUp}תk^<5fſfT7h+C;X#";<]f$QX$k =>~ O&ef^<< KbVp !1=ze^}c).'kOu8?qc4&}Yt*'@rGg,UZ/c^FF5Κ rs? g7/9Ƽl“pû/RvQr󆙻wa|Nw/uodٮCӬ^Ϋ,wZ筤)k^o8ʮ`|j\iµro?T}rܧ* QrS_Rw{ȗ=ٗ=rtt!ɿlom[H{+kkR wtoɹk# rIkpC_)}jI{]x;/]x'8NTƐgռv Qg34}/7U y{C^PɣxYߋt :W>>}+n)F;S~o}hpJLzOGZ门3:G%"*p,cܻ1`{O~1..TEsI|~#w2rؓqOp<ьq_-x玟y!OЋz>tg>e"^(h9otRsR&I9맥=5#EO5gylS<:Oӹ,G_aN`wC5M M{Y{2|fGa 54tTq-üoq^7=̚OJ~¿ЫS?7y=ZI [#=&L& WWe/J=7?E!.妩x^$Өֺ%pS?oJ<$i3-87yV=C[`m--x#+3Ƴ 9l?0-^:)O'+U;ϚuX~.{Dn6eZwdO1ƽތqzcܗ1'ό,YkOQ~!w*Ko2=L{|eߖ>o/n% /K|yXn=,^^4hnj@Kl}Y\Wa `cx/6p$rr0lgyͽg܄sY|=ރت}|O^AF}ƥKo}𷷗T/s{y½Y GZ :}cg,Էv6]p{ӻC}whទ,qkpy|tsm+I꾮r{j~fiaoƴ7qi&&^b[KNN[ uۇ)5:m*f[Y7{1 'Ό>TVK~߿yi'2qgd,5߸GR]w\c*n:Y? Pp{seg4j2ƽ}MEoU{sR_ ϋ8bkKw/}o?Do>qN/#<Lk_x_3yS:w!̗u,J@o޲2aiYvv+\-{|SgE,/2Ec)I'++6N<~c~iAr۟-z˧쳛"ϯFg\.X.2}v4mO>֛vIl='z˳Hك|Kx\Fkߴ{m?ܟڽMڡ.>Sphj6m7ot2?c;=8N*enj,K9NxٮmʾLU()CscG+}1.O֛˄ FE KǵGǕȭٓΤy0~&g ?Ud=T^^yEtJb̉P*.Rߏduqq#Tª Ϣ^ޒAڳvW|#ǹ`PKH^iOHdetz^[Ϩ4&ŁJ~pdsݩ '9'nu=d9@*?OxpI0Te#eJk:]U/k1ԩN hh*AÌ:x_ah/0g}ӯW2.MM)5Roʨ5 R5Pu5^Af\}CoiDލ#y s62 cD-h`"[zp>k;12n{)ȷ'F3of1o~blb<rq6vDZqxGE \5X.c}'/bO; c,/odIpz<ݻ{>b}wxI'3҂n}, ܇8O/aWnY+[Xׯ?:?w& o)8Sl6~ӬJ/IuX/߀ &w庨?]) u$&nZ6wtMy ?1 -4?th1z`x5$~o vTvsa#kӚP#RmgwQyyؿ_pgu0w ?@G֠w0oXjݩ͓ML<iÿ ;Dþg]<| OjOE١6C!ht <{(ENa^atEa~\ad"X_ ͖q.Wt '_aO fnSiTO?ux/`9OzwD˖[OԿ{7yaNZL m?ytX'8F;P"6O.Q;ת4YB)\Qlm9Nè-ϽTo֯k}QߐdL 7O2ow8/}7Ux7f=vnl~ 'Ϳo»?['X7Z|G-b#&3EwLQW7ؿS?}ַ,!}ûpOCWNߚqo+\9i=xAW&^VX0} rb8uwd ~:t ] 2.^wη̿d˛&Ե8%+'u^{wĻ23}ɂe676QÚnMIY~k ǂr> =ztYFn$yc1۴Y'S|,?d~uV\c~+?l? tgH[Ջ#h$_N&52!2/e͓S y&dgr6s8ҀYӫx*>2|$MHreNJL>e'8{vpؓNL6|>0/ bcG>t}|eECj2?٤~DHPc}4Ҍۅ(vB{8]d _Qw|+fտ-injk0;?}Iߓ_x%a`8cVi1uNw AgvK0P_Bà?=5*+p:+cS-i)r*mm.[WTeu8͑ot~UG)_s1;؊(\Z 7T*w' Н],!*C˯3.b\'Jߜqs yoZh#7ZpK>߶D,sB /o6c&9c;Zw-y=9;2]cMߪA;~4r*wƚ@~J_8Xӎ^Rg1 ggE=*m_7y  tVx^\1(1 %L p5ƃ۱ϒv%R<}<4æ ^v]0u)Ǵ }+kWR# Fg&z9CxCG~џ^c}ߠMgxZY?g+i}&Z1gJM;FY/ps_4G˙_!n4 ;_߃y&Z_=AI<.*qJfY8{^=W Ø? dO0Jaѣ~}Y#f)}b'{u1ƻtM4ӫ'Xker/+rpG'ڲ1]:?ykUxXF_y`ʄ/ɇiGX|YnjG>M3Mu:5;2V+}D2o҃'FSمO S)3b\[seW@a]- XsMcY~gwʊ/\:xǞCr35.v!*NXe]h?$~\`if5n82GQmR[?}+*le+˲1ɼKiRL68~KX+K3 ߟlڪ)]٘ġttù%<<9 B-4{ ?3/x+~/Mw~nMHaY|;,O?Y|"/1տȸIm?桱]MO Ov*I1?%M5e@lqTna4A^t^`^:|i6ifœ/rL{‘;2/kp7yٞC{3/?D&o1r(6]KEq;lr|ͩO:_[,d/ K||_]ܧ]ŀGڏæo?.Y4oɈk]p3הqd\Iʇ?є O햬g^Y_tǵ(|ʻ4k~a 4*tF+v+N6tf=?q jڛ18̷5^g>s xSd: sW}^tZ;e@Ng L\㿀1ڳɞ=\.no!1 =-zzϨsu[xuH_ U t3͍aQK=üc$:q oYyeywJqf~/NkXb;RvNhىg=xU3͙7t+ϓy%y(Z>w*>\0\Z |sxgoҿeGH>3TQVdYlexn۩tk V{nGwT~N,3iJ{_c?ma>6=Yo G~*fN\Nen)CMbd}Sn1;?od킾G-rOنۿLz xe0z'@;*뻆;4qCsn,s' VX.>eΐa{'./>u~gȼ-k?,YxD#|,Ӗg|Y]e?|w_WKa`Wq24Șt~sSଙEcn2gɼIp&/@.l3vqkOdZ6[28d/2/6N oKKybJˁ wf6q1/t<*;TOaeN}U_s[s($*χ|d&+SlySH(CC;(y9DUcvOpǼ8ϼ̥t1/:= My>4\?`k%TRiyO\[y51C?PΫ ?I?bՃGΥ+IJsҳcyF?y~>I/4ߔMToR%+GqP0?|N^8\{y)cSb՛;ݪKA:Can,hñ)y.3ykKޡ HOJ3X>[.K".0^ԓTqo*GƽGƽP?e& ]`eZj/<?اHd:䢇TgCk[8\[a|*?kV?ȗ; hVV^U 3;JI.`4!0Me?#ucw k"392B1cC|z:/46K\POυJZ1Ľ{ ^Hsc<391kw xa'XHw⹘ i}&x$^diKE-{= .i9à'4C,ֻwP,2|[Dwhe\hƸ1|1pMLe`3YfW&^x.ūc*/ ຌsn bNLbU|g/vخ =URIE7/67ir)J(ݐv`i&M%f,1G]llhlXl]b* fQ3+/wџi;mQL78Vzߦg$Np8( À'3)jL;,px1/z:5>v3<1 xb2ƑKc匓^83^eq qw?3Xpx*M7K`<֥4{Y V<KxopG; ۃ8oVط[kg;pvEpmf{=lX߻Ǒ]W:T7"jMsY?sZc9( Jw(B /Qxl~-CI@&n~g}=g$fUh zZFj=1JCPySX.\|/CzXAkӘ_eVz]qwүpr*+h}%;2#_x$Ow-%kp_$o`YHߘGdoG0@SaX$7znrEU3\H7;MQJ&± )||o,~NSB{:GY=Pߞpڕf+?c%mxlr5՛q/!VQj/XF6WK n'ؿǧ?bBܪ8f**Ww}Fj0) ~vbI~˽ rC 1mϽ^K_J&kVQ<|慶(#:Éߘx/By+o Cjqc;n35V X#6~jW>̯fr|<^SX`?4(+w?3c͗3 )7־aDZi:{y^WH YC?gwOrC Ê7K2 JUe7 RۯzЕak?ì߱-Lq7[ry.a^uth2o R3{I /yTg!LR _SfOX_|Y^pǭ?mU=d6߶T]{W۶C, tUX{cj2s+-l&P[BTiyٿ*:P_z0AG- $>k“^7&c81b<%^R;‘L0g+RXN:{eXGg+11˨}Ny"7,'87>o71sˡq fM_q?Do/=@a=wwg5̀'#;gw|Wܻݳ*k%KF߁\]øa?҄`|6;[v02eԸj =hl#:we~z_<{"S^|~z48 ֕wnqQ`N5D?3z|q){X-pϔnLz0Σ~9 aII9A=XGpxd䘳1.V_ᑀ_ʌWUq/>GmK.֤20'unT‰6ٌY )\Wwzz0oו1WaǴt[-ҧ!֯O?;7 _ /<fG$^iC+w[k9 c:ᝠhtC⨵$/H4*Zw^gz4sAZt >wG-Bw5wK57)NFPfq4ୠ׸z}<3pUZYa}mUZV%;M9ɲһ̺o]MߑˬgN,Vxcg<²wBUyx [)r\U1(a) g0SçԹi#>I?2x1sVxP{Gi}eEIxɼV[,x{CT8agt6.]^;:)oco4{kv4ʣduZ:)fVq\eGYƇ{wws9z\H9oY", ;*[ʵ ?,>|ۯ1}ӦV7qW1-܀rrS!:cB?7N?3jpGM^$&}0A٠S&;@Kz0Jʲ Y w=ݰ ݝ&.y<>d[Oo,_=e;Ay35>9g>eVTۤS#]$9w$ɥa9/c*4cf]dqi+,+US'u\CY.ʒ'碜uEn:%F9 iX> ȟPq}xiCNr E:)Y߷#Ӂl}q \>2ExB->v'~'`X3)=&᧶lH:UXi-!J0wv6` '>dw!α1ɞ=U:x{?g_MبZK(晌_m=lҫa3gj*N@ dldMZ{f֖ uU?7M/8lzo r#%0v x}-=oT=a3y,z  E GwZ/0ƣ,/vvf^ D^a>Z1=;t}^u Ҡ&ƛMo'mxXe^UՕ57טc>}&s9 {Nucނw/4ܔu6?Feu*ub}iQyx-|KGY(2WpFsA>9iSg1:$Qg\kUǴ.{]f΢lzיc}`^N4=we9f/eۢ~w0fQ\s+~3㌺1!|rVnsq[Fʯ wD}|"o||y w1Grƨ=XƸcl; 3,X~&&'!Yo6w:q oBY=0P>2; Jv&UW"6 aXvw-TNq8 H eC|E(ox'.z,G!{WAv&n@k8g=p|=C ,0bѕˡ޵ Ҭ?LX);06۩M~ >ݫ`|܇[wIq|2sҁh Ny 9OP Ns}a/O'=C=CsOlvZyҤ{L]q%º퐤 pqĴǶH:7Owۧ,d(^'cl3XЛ9Kwm|v=볳{6q)3C=Qǰ6 )'FhSF쭡Α<1_sޭ:s_;T>Es_p)sgRӦOw:ַ4elp|g p_Ƹ V].GRG溧)MJKNi1s|Кӡi}rz N>}i*hϬӦ̬=mY_{ ErG&54}f;G\ôwڌiD> /iTW4iqr=ÿ$P'2Fmu&3|)ę-1~ddPgOg} 5]۟>G v̅Gq|(M_43˜Brz5bsX^kָpM;q8Iq$c;_Ʋ _>My T9"\7dM.c_0 `,cfsD?~/ۢp^/ 7 [GOsmW0량 LϘ|i{Uvj+Vr>ceؙX_w9cIs>u=߿c,?CcߴRO$bٓug]FH/7)u 3%~)o't{L]|?AGMN5 WqOVs2Kҝ5w8<,q֬ST8kWa8v ϑ t8^?֗gͽbYG۲svVXE b1Rf,c}7` 5;'{2C-^3_\)pO9η7r^H9xzA=hϚyXif~cS,y[YY?wa5s6m#eM.'`ɞߢ8gʿΙu.L ;gz&0Ƶ9b}ղstm?oڿ_ms?lǼc}c>^ʯc}KƥyF*.9.Gw݋qv?o?x=pz>qŁf]mb{жc}{ضkV ju!oMc'lN)f*:mӈPw2m|Y)hZ޿f*cG;=2α6u_0O`|@kf;;. GZ2dM^;돟gRx0d]ʈ䍤I |6"y ;.О{ y tմ t ~`O1Nk[m2.z׊xTBe=_֒l> sFLl R?E#ax})\4UES.}ރNI>*r1[!|Z5_qJ$ Ns/~NktwpK2dd-U\1r8X.>-p;mODi4f0=E1?6];\pڈoTb~_QW~fE$,ڳYS Cye~+0{͙+FO+fؕX.+\`|Wu ن_1'~z|kΏT:KHakm~+L+Bkx?{?tBLL3Wׯ} \sYz^j]5jƐ&EƐ]5~v̗_5QWMK|m䫦Mu$-/YߧmĻj\iE_,QXG 償{I}R=oɁ[xN9gSZ\sd?]A:ƙU:ea噑]6Bww~k<@שAi*1Xc)aeS[hĵ\щw̓_76}$$: 5&)d[ľ4+rpz9L/:'ujOpu3ytEf?א.U%/͕^aJ%cErc{vv倛kM^3>9T9~Q-dqD 96yz)?_b/daOoH{3?bOO¯V;eJԾr%lRO p* 9Y,W&H8"'̏c~8c&wǹEԷhc*ܢI[4|Bp%e~Ew=MK}gѕ6=?Kmju؂[i-g<P7\y#m#;jͅ=nr}6׆!^ A9ed>Y{S7y|4yM^Uxb neߥ;{vӱߏ|n~.^ŻFcىy}\wzルo\oZNoƤs@ca 徘: _f-Xvs=3ruq3MaI;*2oHk> ԙG }@W8}n捷x/{3o"Bk#?g79:3o*(ݟ*<s2|YKfq8Mkt}؆YV%6ȹqyp>?T>La3oU$?Oս?KhMм?/V~hdp=\lYn s풦N|Cd}蟞+)jH:xo-ಏuLpkփ=1Ѫ_k)oJMSJUF՗biYg>wq:cxNj%K ۺ[\xiuyUj[Ry)_Ѹ> ,9f@k2U-{ʸ;E oo'Dq bǗ?e o,㽅gq;μsfN Or?;r&AOn)/oz}6&|v}/;E_}gʢ~Q0/ޔMkw~ޫ*$W6Hd50߽n_ Y!7R].?)ڰMsB$c byy 58N/689qq }~k" 0OK'^5e 8&1_+մI ׶^㷖pK-?=`~=+=<(5x"/YPqMJk UknKkT8.~Fu 8ܻ.K%) ݈(!E(( (s;܋>y:ogL|0ewxg^EYw:~X4a\ˣN?],e\,y"?/d umb = ƷR'\,-;?Gx0;?RP={SϪ8@57\оu O9y#UDS|t2$yycX΍IZ~¿?0g_$KgeIII?wߦ; O.fC~N?*N~q`͏RRZfq'ͱKEe*]TlnGs7;K9yK-+) RCS9~ϪtY11)6o7d>io7?k {Mj wRupDwfqK`kiܸ;Ҹs`g;N7͓mo4TnyoZ KK6T_&HKmsVb?7C}?>a7CL~Zy;2-u'3"-!gS[~anGs^FOGyVLܻh &C3L9IJLl| /,xg > )ٯʽ|q1{IcɭyC~S|*/ZRʙk탲g|,u 4a~?T/jEW\{泼q4g,;[W:O}G]WzلcB /kuY^=~xe1u]O(nd-[,xu Yc|md}'S?zLշ waZenNsڑl.b3_ЍYi>nVODg nc kN%gC gKmYƟd,%nsr ]{+ gyGVXȑwN, a2˻Zmɻ;O|Ӣ;s<):?;˜|S=|[H8WN5oȝ3rNp&˟!yvv d@QuG PU¢Qv,~#G8 ݸ1Id+vϩ{Co8ٔ XU1<9!s;:/o1iN)OEX> cӔ9c|r6$Y8T!HzØf9_*3 x6fY< 'e<7G֧b<;rIYe81X2UflL-Rdzj/Qr=,_j%rǍoa,l=2+t_XʒXrvrV_S{ʙk>K_c[h.w]]~Ekܴ>!75MerӚSC0TܴLl'5rԦܴ?w9}DA>8ћ@ڭr|jnn^ybs_\r/K8rjY?9*?Ccǜl-n;Bx' =K7rb*q sZ!ai@vy1V~v߹+槷ChlB\w62.'De^ uܽ:fm'}%l''8͙og>2d!#"ixz0˥M/Q2?OBdwR?JBY^H*QI^J/WR*3SqJpPB\pJ)9kce",/gGC OΰK_)saZg|0>W ? *K "4^K_ cK*_뱼hgy]|5KIlc7 :f>Ǽkd.Y{Jsaڅ} `No^LԱOx=R˼Akm ȻB+g;;ZJ}CD_e^ 5w(b$a# Wp0j3mƟ!bcؔ4v{AM9m-fŵ^WHǬ C,bf]8'V^/{-[qwŽkmw= {ʯ|{;<$ [U mH=kyD!C3;;t8͟R>.JIVwq]AY*d]ueA7Ӻ tj]DJ` w=}[şmm'yW }+]gy (?|A w=2OI`ova0W逸*?Y Tn#X:V:H}zԌuoH=Brًz w|Ǽk2 <"6)a< P_ٓ XΗpC3=C|fD%h<~NfRͤՊm&~c>UHRdn%Zgu w6+iϹ_rѿT_poD}ľ;+KRG}19̗Mnb3ss& [0oqfHuv5paK}D'zJkIC=aݔz:<ϝF=JO[O`O-d/k%f*,L¥OIKҿc#ER&D>J|׼[VDp9N=. U#r>2^ bju0~8C<  LLdь<=?Gb|w}rRxZy)ygעBfN΀c\׭f2esaM!|sš~6`;7;pܾ Ow@SeWZ~_c7,;ƾt8YwoE2N})_u]ŸԫPD ~V  o4kG- &v^e?Ye^3;0, a3W= d)+w*8VV#*-/ `C,dw.dwQʝ~]pS/>8z|˼4i{2ݳqݩ f^p,g%;Of<Dž{+ _ KG<>8cgsv*MG9?{Нؚ>Ud :~_m4Nan[ k|TVgwn;1=CWWxҾW5sXy-6}*{坕݉ -_Ϡ w>,ϨtXΧJVS4ְK;1 Xe@\cc^?Kc _``O~H+s /_l܍; 5PpTt;X |w) 5r#^&>CjM$L_AyV_&pUA8j )zrZ_qᨵ虪IRi0;ܜ;i0Gq<#+,?j7Bv3@}mWۢjN6:yXz/ 7ojn%'*mG]-!Nk¼7?[futSA߉oš>jQ۵ݽjoqce,2oZk _rAƤkQݔ7bjV :g3"mO.a:I,`^_o ~sr5߱)oQx,;@[sUtw=p:fꓫ滁ʀ~WIT ˷ܺ]aއVy\ CZCwv2oC,59ϼ6X:E4s]n)•wĪךכyǬQ5dץ}Y~X]/ԥʼ^!>}߇]|]q?(fYD2ep)ԙ{5vn=:R%c_a_|.׷G}^r*\AaIؼ>vf/f`s .tN6z1fO|/xh#?v{\{!u{>fMU~7sO26q~7 OA :p[yGS:<sf,~oijoP 8`33mcYyd|Ype{>eC 7~)Kqj(܄pdXI2?Yz|;BƐO7ND]Ć?f6_6|)")"Ao˳*9qodyv$ۏ.r,sRyԑϓ3?Zy}gL>H{ƿ|`2VXcr34Q<ݳ$/iɋ?KmkU^gyYUP,>2nʃw7鋦,kf^%m9-8 ۢ[`mWʎ|"Mcװk22y1O޺5׳?by>{7\5r 2 K͚] x^Z.%^̼6kJuj;D2O:.K`)sǾݖyylOxO5u./g*_Osvw|agr71o a{/|FYK:4wuQ#{ghs:G9 `N =y$9ZsqCcaa%_AΰƻF^A. k!""yvІ&.{P8K{9ҵyf\-X%c$vg[PՀހ87v2b࡯7gCMHO2" pMܰv|uCM]²|#**QOq*[bLWroSWV5,Tiװ)_{ׂ4õ[Xv8(ݪ|=[й~)f0c30!`⽅`=0K5e|&  br`bπY; s[`VU=Uݑڢz`V/50:~5& g`/0awqYz?`s2x 0yF`~ 3"D ټSۙ]Twrש3ᝩLTwrEg*wtr ]5BٻaBaBaBaBaBa&R)rfD sPbnG߁-It C&%ߒ7fAO~b 14>7R Мt:p;`Dwqc#hRƾ<pnvJl >c^{ qŰ urPGo~r/ǭo=XK&.T_ܳ_=L.goW [Κ7lAcăi]&x%' W# w̼6;EoU/[cޭ8@2 Wtt@c0;<0TGgg6F[̼?TaWŘOonvնIWy!6O^OnӾňbd(Iy4!AP3㌽P/NG՚Ae:$ `iR6m[ަ=+p-pF_VR-Ɛ1/U'(~ӘI a [tVnv w\xc woC}bomIj}LzO1yKg6 G}H.y%qg10Cn*l g/{o I @8MϯK^nפEwƙUx1 e\8AOd|Ϭ7GZ,kӏO?L95}<MArӹ?y?ޟ9Ł4ПwPtW#\8yER;pHZ j$ IkZ׌{m~Q.lc?ీk#Lj#=$/0)Xgt;;  p` pCDs q}iM?w<, ԣ'0NϢ),o8|MSPx}~-?:М=yxmФpv h<2}MWpTyG?H-ٚ%L6\.0=#6knO6Ҝ5d3 Gn7eaf?=YG}J 0PzP/=4OMq:5`[>ӗn{9ŽSg u!=>JGƧ!{ѝTAf jodp{>yd(bT`37AoR:H}mϼAtcdb }^e`ϐ{HF~G 9e z7)tc3o<{}#gC\v-ۖiG Ʀ}j[pϰd.Lg`+80NBYūoj]'8?>~'e)DO8~$ƗV)34I5ԤUcLCl`>W `? tD C{8/0Nӡ>3L30Eƨr`ZnPgD>M:S~X@)z < lE7~P6019#\3]oe1.xi#綥3#:V =uh_/ />% (;kzܯUzݧ =Py> iR0[kkECgbNlϝc! hK Sx.`WgsX9zIG7 QnuwnGZi^)oҶ_xw@>ͧί/?b|yu"30vB¾B훟uc9iMVOgMRx%7Ѽ~|{ᵟqqG\22r+xdW.=&q0B|r"s| 33xbyޫZ)Z& 7s8\Xe) 5W9LnN2 mv?I$&#]qtwyY3sU6*1CN~ngB[\U{)|Eˀ_0mtf}wSx‹ޢ GĭwVw0>72{ae`Za11Ώ`} \jO1O P{,.2G+?)F({8@|soâ<1}?cL{~9_Q@᪌?NFc͙-LLcͻrBso'LDVoI2epj,9 }e%Ӑ.mNGqgu}N(S6ҩtY@P pqQׄq8a8H-1ܟRn(ɷR/>ŪmKChTf,Vm=>L89K\2GPyvgD\;BLyO2QXxX^.?rf%K#*X I,M==ޤ7MZp mq@sbS;,ƼjCZ3O~φ~_؏],|S'PQ 1u~یqq6L8 8 VaP$Qݞ$'쥝g3kqnySsxF+= LW#;gWoK;HcJil|"Ntupu?ǧFg׀as'euZ}cf]Ƹ1qʘwD^MA/L 1?q=0da S1잊!5B8+WW/$s')G!N.&=c\WMd`=@Ih.[7·؋9g[CY!8_PY4ΕMx wp +Fܳԡt_oMηhI|]'ѺqoOy=9ݙT{r6Sr|Kg8v|pɴq6e=*^3!sG\h2΅'A+r;WpM<ٔaM3ٔM; -R|0a2c^QaRaSamTa6ٔsKտ3o[vX LO6}۱ɦ`yN.+L1gqhW1اpScݾN~NL{z3~hvYg"3~ht? ZϠfA3̞Ϝm!cLU3)|5PoMg 6 )YL}quѯ_5Bh 1T?FP +=Gu^f>pg*\> Ro©O7)f$>sx"㦎4~QH_}a-ރ n,d"A>1%.W-?T2~ Nc5n[q"/[Vr\j[7ϥ>r\#/̥>\#K}dyṬ><#ͣ><#'̣yGnG}GfQa>e   S Χ7vϧ7>Oo(ܘOo(3P_@o(T]@o(4[@o(Z@o(^@o(,]@o(P8PPP\Ho(^Ho(s<-0oJcbGKw<{%w}F,<[݅gRXΝ1}vZDn-"7E<ZD{Qo2~hM7<r=ۗ+/$<x WqF(o7F?I8b8޿XWP7ѿ^2}̚B q-#Ֆ}_Tx5L?hR;ǘ :A[`i2Fʇ8`'g_&^h[ey%dy6_6\gYp7~c0.Pqfw(R7fAe\y''SUxK$f/>#Ć;{-JvaEp.za qp?EeΐaOPvYpQb 8W]F>rq>F>DZVVme4'FZ{Vo8CGyQZe.pI+\L Kڅ sΩ~8!eHe_k ]O L`wJw,=~3+1Y@9 =)]Co9NˑYϨz_k2ۑoa~sfi>|TN3TEuh[_:%skn*+QpaاU9]Xa[UI;$m?TcmToiŏpW;s7 ئF9-zvTgN@yuu$/r9Ee_er0,߻W܋ﰜ.te+Q ?υc^\~/Zcci[Sd_3o'1_Y>9?nWoԙ\,nsSO?7xMV^ۘawd+ D>@` !V0o!6#`?M7m VEq`VXqy/yOSIA+uY~핦l\it?!%yxEЕ^JGu<,0'1oU gn1__0sV䩤a  U]+z,^R|;2s3/@3?3.w>;8 ޥWy6l}o*j汞{g䋎뙷xd72?7Oo? _迸]] ;]Z"Ґy. ?ܿ~@~N%ʭi햾fWSo)" U’撷TiQ.k_YmNA6c% ެ|O6e'_ח=;M9 _cpSr1khNz$;$$1u+{eps4Ag֎P YR`Umk1skb|mm֘טw1"a}bsvWƵdnZߢ5:{~ǹǺ DӚ䑃da㰪=|)ۣR_o3) hz(dmO4Mjc? S`i C'ipD kp4c\񪺛.O >ōӣ,:O?7{4#4Ep}/iF;*ʍc95~µA3ݮYǬ![FTJp֜{UL gG׉MA0اOAӵtN 륬+'ďNM?OR<s^muWQ* /cbK;,mkϪʜt눟֙Ku%m\VYc~?L`o<˗¿FKX^JT'R^G.ﲻBuo^awE,wvmw]^"- +aG?kFJ]i"i&:fyY.WI /**f^e8ؼ̫ 1', &]~U5P9koϷ]oϼz[[e@gyK9KT;z/k1V~Uk`M}78 8h8m+7<7  dɁ1û=)h̄{+˽~mCݿ`T\9ޠ4Llå~@]EϤ7DEfmn1'Y?_ߢf ۣFWˬ'UyB͓0Eѵ%f/ u^wՃy=^5a=7if .RW}߈G?`Aozaxi @ VQI̼*xO Op, SCm0cd.́#gJM~QJgݞՕuL` >?{}x i:;)~Lo4mYԽ!MHV:n4ʸjۧcѤթiJ¼g6o}DfM 1o>:n2i7rQ\o G4YD^^Sx!?:d`&>x-u{/ы!6Mf &si{^.I!3eY6kFsq<_Vh3ky䩚Fg0e̞EfMa>ʼ>I5Ul68 xs0mݏ6]`jOܢ7wbq{f?h1uH6o;-p+ev{One-Sj6obto/ľÛfn'iL vcoa&5`Ie00 ߀|j#˿ Ko7;,-,X?w|dPk v> ;?˿dY6U0Kr8p>o8ZTs?#k?żɖuS/gY~? v!Pv}oї>qmޥ]ャg§eeJ.Qk^k Rx+ޠzPu)Z%)R<נMIn(9}F^V_e1!c lx{ )yRz+*-R8ݻf'}ķ?U(8W,ͼ?=%\ᘻ5]3oğ^`y*lQμ4;Ix7?W胦icym-˔ ^]Qx. `ۘq|tQCdeSWgMe/B^Itsi3sn5j1>zXz[/怹]*f4ޡay2&mCD5Z9)X[ٗ_Ws̼)RmK.`y WF?2zΡl uZ1Vɜ|X`;yQ|Uk݋ﰼQx|p/)exMr;Md!#Z'yǮZI1j{qi]dkDwYʼ*˷Ӛzw2/ቾ-sy=ÿZon'޹FI U3mxOk{&ו{3m㶲=3Ɖi4aiw~o_4i{v:XmO_=D6C'KV[),j=$Y,W_}?:d-5Wwn W {,_6-~'{#H$ArI]$d#r/=Th m+^"w-J^Mg߳NAH]1gv2º\:c"Z]%Q|_gw%i30of;L|`5_c ;OnOƟ?v6g8GVzJ?Kئ+4R3Tinoð+G8Ώ-q!`7+BFS2N0rFN;^9[n7[)WtD StpLdOqN aWo[&/o)+rxR==<9f/;i^F/忄74~fp3%.ltd-Jst2/&1y; ~2NvTcv-הG,k>w{ mPo۠+03nʕ(Wm ©_"gXnmB(xO;Y(fؿԗ>J@x?0eݟ5 f zД/ϻIm}gt9 |޷<ɻ[!}6>?+z%4gQgeW/s$< _9 ֣*֓?>hڎCg>d8I CHvk ܾf Z2gٯ[7Y1)IvNfO<=c, Uo!}Si?$ПQ[X{\Xf)?P])Ttm)ػRk'Ȧ16S?kCPɣw;BondP˽G|g1:Z71/e(O*eKGۨx__Po|^0ǀYyIطogndt:jM}%o=Mv I11[}7vs8S7VI\79{'[:) Ta?bg< Ƴtwݓ>:=0T~X*<Gwܟץ01^ws|i5!gLC'*_c;]9.Ic|&'ƽP*(^rx_A1Fg9z[|BҲwؕ[HEwTtz` %~jʋW!`Kܱ z|?߽K{r`?} c|ZbB̼SjW*=?mOs(f] u^,n]4 ! pu9G}.vܗSX$qoˎuk6m?;?A'GP`woT *;.v:m`_t*_̿ )G!vS*sV]Wa`;(<ɗFбr'Mľ0P38mxewT[ܕgmvb׃n&r'173 G Ofnv# nc7?S8΃OMГv}Iz>$+=IoW:Io>Io79Iow:Io?Io'瞤W4qt;tt$I~$WK>/O/5$8rrunU8Odn`\|fϜv5`Wx ug 3+s1SsNQ0:*l7Ečo t^.nN7bNyO]!c}#߀ı'5Tt~g| s,c=w:n=v)pLNhjZxqՀ'KMoQ<q|t%` wN866ou,=}oi-=5H`>n7e??Mu.zf|1njvgG/xw'?q8#7pƫak[_kjBoxg=@G>.KM%Q(Ҙ_\.|)>q*!<򀫥ƀkk"r<کi/v6|p#C],?8͑8 +~<'E:,1pkmoO<p Ee.xm:Cm sSݙvۦL O|GMmTHǀ13V=Cm@Vw9g( =GGv6W9ϙhivqیs~}si:?CgCCC/CV9sX+)u .uwTަJ?ǟ[Kq~ݴp<)SY6a?7q_tZpbnnbx]oE j:c+װ[+qT&_8>Aֱ HM]r YTO>Ǭ }&}"p>iNe#uÚnssݘ 8/疩Se1p)óy|ȇtv#웎Ο&3.\Nk0/*tϙڌ%YTȥ?(ϣpF3YĵJlpJt|_.N\:"ՉN4HuEJӱi"߾Hc])Gc 'HUJgLE>_6{QU1 HpNŔ /̉%E.p1%_MUrM8wßy?} }=ş`+4@X?mzX9o sRFU) >A {ͫ?(3wS ʌSL?jlwU6?]S3HqDhhssp3_S'eK/pmG[C'ym{75c[15m{o{¬-88ӹZ{]dN·NJχ*_(s_;GsP**0Eސ彬<' M܅y}X| _Y1k;Sf*q,!c^\p[eF X>d K\Fp|$vʝp%>/0o ya˼_V8ad%roPn|)cإzdf22W)'p<+r0p?g7qWy#W%b[?ՋCXV~ 0^w/#뀏Nq2ཀ?B O>|D>|wtͅ3OGb@{f@Ս~7]KH{f}&e/|/y{fm:cL߳[feY,%&'^!CqYxxet^%&UViJ&i4bҤ&ů4 8Q{C5e $ :0NsVy^"S'^8 r&{?c69#^p? 18cHu!= %cD'-n",??)?v R; +쐰'aAM̌g<Հ0NܔxG~nK&8?J]_A |>@+BG Pc>5YN91h _ xw.nֺ復}qifT`yHc\ֽQ|T3SPt}z=5OZn:*>Nt٭o̕Iǘ'~\L5E./(O=·+'q17, :݄+㌇\q##9UnW׭|yCg*?tX~p>ݭ Y!̻ vS&n+|IoIۿNqͤek&*]3i䚉wk&.î+Gc^h皻'suڥ[h5:=k)=e;fʁ#2VŸbH`o*"?\4&O^Ϡ,$Qo6c_z}O^6s1m ;'>=UM|1WCm%Gg~b1Qu¸Gy=rY7п6c쿞ccD^-c+NJy|7h?Ҥh3wxzw wu>wcN{+AlOlWP+K8u>?@ߝp3Σoyc9G@y.?cUˠup Ϥ yq/W9Y:k<oft+TzJc~. X) Q1KYO*y97WHZ񺲼 Wŕv˼V/޾s_s}4ԏxH7H"܌,Y__U.'T{VI W\9_Gњqg*Μ|uEKY#vHH?RyG:pG:qMoAo_n_n_nɍ[F6qx$Lm#cXx5 pom\ncnW}M0uDڪ2}xSUiiͼ.FOf!OfOf][_M=}aVVx«;2gָAxr 2OⅇgqOO|''d ̠?؟j<~EMv?xT1h;~rM7ʜ);FX~j;?0 G##ቿ}7Nh/,v~6 ~6}=^o)x61ORc8VYy7JCwgw?=H2K fZc@_`l+ͅ/c̛kׂcWo*W?bLBQofu~3c! K܁;7)~l0Jj'Y.kߨp> oDo {$Aw;Ru 눤O,7r9;]o]mObt_-  ?^0L׾}1(_>WySxcמtNyPg}ۅ{Oq B%Ys? 8 UC?\ps1Y){S%{3tϩܯJִ v~%}'˧X}3V ԡ, W柁D;WKgMc6IuQ2O*n38q*|qLV\5K< NOL[<!ʿ,/ִsZoU\hJO ,3{-d5:_ͼ2`u';#wknͱL,v?EXu:}K9 p)Wœdy )'8;yHn]7յS4;׎K[IX)[Ymgkyn:v'(> ?! Ba~fNr4$xo ]rmFwno̔ 7Fo1T 2’v:mO1ԝ3YKyàӗCyk[r ߝz+wpewa1~?6L9X 3g8Cm?72?P`ğ o7L޽>z,壭" o^0,+/)|WGgW#_/zE # q^*BIe ~e{Othoq{B]ɀ3xU5 t11Id{g{63`/qQ\wخKc<{صUe{T5FTvawp?2 W0+ ̃Y>O U @q׍_~[,_ {Ɠw" QC7\d?_ʼ#}~{E`~Lcⳉ1/rp_޳~_jk!4֘K+T3o\alMZ،}H_mIsq7?nԷ07x3-7vp5__I S8gqxۂt>^A^yƷ&"S=a,̦<ǘw-XCaWcKx_i{Hfp1>eOXvo]2-| {7T^3=JO+i3~`8b܄9}ܯ|V=' CLg 1iZ,ĔŪ>Pkr4[2~qZcԟy@ȼ[p 7:= K8;{;΁?ۄިr1$jBi-jOwpPu<z\ݲ3gUO>"jڵnԊ ~1wWxc\ۚ'% ~/cyQMOg=%c9BM}TL6r?ݣ%KK~bCe 忰n^݌ qBYUd C{;̛,!;, ';̪j=vc!]r:, yɪVU1l6d}^/cj;3oX/ ´>UNaⰔ/fQH _z;fo)?K6o#Ř)H~U J Ҕ%),_Z7Y,Å4_ƼSLetˢ_EX)R?*^!^=u`|Y'~`wѝV}9T8"?>ѱ\jکn uێ{>Vy웥JdNd<*Y~ Sypܐ k&wyg7X~ u4"qSK0nWƃiLH?kʼniL1s5#G5͡u Oc~~Mű_cWd ̓X =iˡ7m\ O zew}׸߻/rHkQq=/3Zy7j2!Z0X=%)Ћ(ŭn ?I_קǯXi;C클TWcZg^tL:*>"R/m:Ntf!}I*+(a7d^l|Wܲ!~u~'@54]">: O'x4.yq߶{ Y3hm&1쮟>6#/e|Zoe|k˩t yc<3z1~o~5 wJO̪6pS22Vy{%º\6;' (:rjNu˼1;,/dU쎤޸Ng FM1_]1pw9;,lhjQi>DN+7 7W寤'2)`.w)7u8 Ψp (IA OVx›ޭi>̏M#=xQX^xX^ q ''M> |>;̷>wǷR,+">74;_waZ^̳ӗ&3UJ&*.m]R?uN<1`qY,ݜi]vYS:tF}FWܞ8"88$+{&Ӣ2|Yn-c9~4GĒr =Xs hʏ]ސ~v=np3e) 0MƖ)5TX-nc3r_\/].vpWy},=Ԅ@މqf45iG(KXjU'LGIG|jє2h.bs?Yѿf o9-CƘ?d4 {2:cb̾; (,f2n> 2qr[䷴Ƙ$8V K3%?,')@XUX3zr`HC̷ep|~6DX]-}8s?eا:7G> Sy1>!/< tc-t;ch7C>d ;-3r?ig(gWFlOcnGY{1{~OS?9(?{%:#!^^b3dlAV\dlN+}('*e<@_*[G9gaZذm6!8ïW6/١8 }uqYc)8CxSǚ>p~P6bf(W1ﳼךfgw1ґ1`Yב>c:tH5q=o-ֿ={˸K=A=NC\ n":ni9;ϸ4E|j/壇Pc]Y](tū<8vNCkoFu\y4e_Ъ#]q*3+ vDp'acy1d{2¼&~kE՚P@9=KQ zo<1wz|kWמ؛qft*_%>O龥pU^CW{I>8.q+GtisVAgjㄿ|ђumwvXp_T=uG4g0[μF{Kp5s8tMA6.˛<,rl۶9i\Voqܫ,ڪHtsg~{{:nc1/`msB~9c}oi73Pnw3F}"IF?W;Z#¹*o#? '%ci{ǰ~730&dmr-32"1qcsK;ȇu=?U<ΐXz }!?t}$g ],XkKߐ;Kߐ_tUne.M-7wyx6&,`w$N>'qJ0]%~'W/)Ottt[7ybuegrx+u,עp/4tx5OkLƟ0>a/n/?1F!!8/{Pp;^옰Ko+;j'б_srntl%#|`>E8F}h4Iq &'DwEz,$J8LAw|m8+Qp$0w֒405p,.Ju1I+'෪Ql.uDwW快5C]y{zdP)~O5YVc7j0k6O W>_I.Ô,>GX_PᎲƫ5c;d S*('e'fƉ$OxC'J\Iޗ#ևQy KW4ʿS睬ƈvӟfuRxй¯|YMSc[S[܅(ǻh.`Xz{)cVb}H~*o'w=v0-Tm'ɽ=Nx۝ ؞],i`.n[8r8hOH"*=͡ɱ^$Uohz308(q-Vk9#fkz Ýr~wR(\c?97ˠx ˆk*^=Cr{{ߺ^WEϭӁ?xn x?む1. ㊀?kFsk'ؾg"kl"GKzA<N }"S 1r8]AJdqsa:A 4N\q2຀`AL8c{W :2PeEscq(kF PV-s~1oϡo RД3|)|S9_? ҜBq^ 0tQp8/8'c\?!9ҌQ{"r=rMdm_܍u{Ƹ48ٌOx*_b:7^xi!Se=>?B&'@%%o:ZȜû\Ȝv(7tN߶H?_a-v7Uro`_L5ʇCv3_O!^Ro8%'T~U ? T qnpq'u6 'L*,hAo?ük1y[%@i/HI] !,ɸ!W x{q??cy]K=[؟y,Ep"fл]'8cs\"Iiz5'ݝN%_[ok c ¼=}"|͞IK{?b{\:x5o:SmS\g:|L|uJn a6oN1usܑo2fprPpYY4fNp!Ynfgq[?¯!@*kt[XG8%;?mzN'OTvvIY&)uw*j/0lf`f24͛{^-j(18Qϩ0)j*7?.j'" ?O1Ӈt:/XW-f3\(݅ySU::u:M2k:S?SKLw)g[lHI~s-8qW5gOpA*]>-foǸK41:L{vy)Sjb,N!q38߃}eGט}dd_EvQb]X,7xc#/z*yGfoeo}{^wb>tڇ=iه Jic}i)uN8McKP(AiZţe 7_^%h8Dnq:d%x:;φpH':&0?=k sCS];yZXaە܆ ֖0Qp|1 W} υqƺ{hӇƭw'+jS(Q vGz8>G`VUq:cۀcJv qc)W]y[}[妟cK)-P5 Q^ʿT{<x) JmRc[5w15&sn&ˏ(9%7y{YQ|F,?{xZ<ǵ>4<;KS]*1e.ǝ KӛxyiZWj{isWmsT:w<"mπ3J{SXs߽yKJSIV3Erܳ%o=b<[2r} ϖl+ 7k8yKLJsĔV{&\ S69/Y)˼tk1e󶌙_U)cNMʘ;zm39C,뀉8C昻ԫU]k܆QU|d:&c4cڡ;dM+M,|#L˻n*c۽2Oֽj}TYdbNwǼ+YִnƸ:IieMg_YSuZs mۉr .qj*(<u`^X{^O"2'd=a>󣀿I Vrk;_yiS/]XQXʢ3,DG)<ǻ埌.'˓1/ W Ԟd@\0/*ip'(qd{W0zR6 ǣW[%idc"hoPѿaHʀʯi `k;I(|W sz g^ }Š̯gy*Le]$̫J&m/T2q>g@VpW6%BaE㽄kC92oƴ{VvPOVWκ:4O3^c̝B3^r2̉>e'c7&Bx~8〈L9i^#[>-3L[6~Ls[ z}cY9{8Ypx8ޯinge_Bs*4Bu/Gu@wǭBX ΔntF4Bt7bN46Ym;)-7j}n|$U{Z Oܻm5]~*N U){\r6ax~DW4܏f@AS^.qYZe`lC_@YV _x7y.>Fv3RU]7&H|y/Js^tU=| VU<cMO!U)/|j{ќ, k۬#,QJ.8KnøuWHS {tO';^rѽ_^wuq{jf<߮I1A X]$i e:>YZ6T4X~*1gk15'+T78Zqc|'YƸޙ^x;@hbV+=d˷8)+U؛Ci+퍜meI?Z>H^cnNnn2o?=e 7l`3M nG_6N~7ewpmiκaGTi }J">qΛ΋lNfvR͢g-g xGi&Pv柳0;^vw*ߢ, c;~ݖFoYX]sIr TY(=-T2Fϼ[ֺ͓Oewt]Z\ fErlg4s{7_vW1[nAj8w}5cP\=bS$=~22>1M3H`MQfM3[Ӽ3gg=z%5#ԚCVgB2Z{G1E݌OSyquLdt~1F7<>98Gu#p c|c^kd]M^)ﲜ3CT~xN 0oV`:W\]neKk8gN]s'Wԍ˜w~ʗDlҹ |_8XeG={ ֓1 l{s{,ԜC\.z,rOK}krvY 7i{q m]UyR]n~wB+}Y]ޠhpr(A8g`N* uM*cxw0;+jB' 8L~ Dowca=zlD=;h=6O=Z-SizۢvG/ԣh=vq=_Gգأh=B=ZQcG뱩NHeYtgy1gw,޳pި3 ħ~޻q b&~C(`V3P`sx= |O0[f0?JY0,?`{:0w˴+V:R`v4 f0ǃG@ Cdg|L~0{)Y xO0`|:Uш\L^L_hf] 00k9_0뀹̺` f=0[*ij߀{`6‚L +XJ&`ij σl,0Yw^%+0[o0`zgc+,}W6l,?`Ý f"K M>T1t:_[`8=S̾`f0rڃ9́`s0\0!`s( G\Y28]h56DXnsF?qGą8r@،+". =;?9 Ӛ W_rNY̼`U·Э%\HS-[t+eנDg ϾRݽD55i`\'(b7(d{?0̸of+1ƽf8C~LeVHK 쟖o-9rd9ϙI7x&es&W3iY^*xp_QFA3Njn\W(z٩Q/(7G*-}f叔gRYo֜t֯w޴sѯW8naڳ-< ҞLyȧ2ε㻩+7y3ge^-mZ, fy$aﰖ5-c}-cP*~ ů ZB<t;a?q8N3`C`:9xuۘ7.ۗɾKq\۲T"5< k_y! yLyKz sK?<Qw xZ:Պ/M+OVF(\ԧ(&H*h<A}kax5{g5fMkSFs+*8'rˌ[_m3/ޟTXwmZ+SW1Y¸^ zoy?1~  OO7p?Mք}f!{kcZ5S¼q3Q>e&wϦuRg&i3\0cTe:ӊq#FduT&2)2 Kɬ ,\>7ǻ>DGqܭk0Zv6yqktI|cqhWՊm*DZ7ԍ?m Ӽ(c{­ۘ8tuLV|"|Uwtzrwqxq-im:W]w9G|O,]k`ߟ>0жieNʁxd\{o^pkd= v? amf<ݐNzs 8>֓iA lN3qBFru{?۟𿕬ΙZXHe㾡-d%.a!o_?t^~qx52LۺB-m7b}όBQ%T[Q\bk~ aFڂY {3#U\*oMMSr7d~M^b)ȫ"g WxvyNSR8i/28Oq*,q@3зLӞM6#h-yy xoCƆ?p3_ө yul֪u8/qTV 9m-mS=E{u;˻;8b<һ")Y3JNm@;cLc̕ym`w}Էˌ>όeJe^#ɇ`?ѤWY©~Rϛ 6dHGq'cye(K<~ <~x"p\5O8Ouyi܊e!s2/';^ojF)FCoh,]~g2 WQr2n-Vx[pn,{2o'$LpLR;RSo1-aNշF)}ֱ|5ƒ7tM$,mQ+R>-k~;gS?-XW`>1 jpfƧzRqDgvO+Is3q:j|'?T zgCvwJk3on{Ўy'8?!ϳO]8*Cwv|CNŸ|Vs1^Ք]M=efg.B;ke(l[𽗆}OWj71f 8M'4-o6 m_Ljt3mSn n]ByMaI'4e,˯YX ܶaCG8Ɛ;a=֨̾ӝ"=h̵Y3)3n*8{IaeIaUIa5IucO:59|xoP%_fp\I/9yC o9]~YS׬mqפ3dYz\x/3ƭ8%'賀3gPY2!G-LuΛ)Y2)ZO2 !xxuqE3;1w(="샎?u?W-B:!:Iy)JYg6o`2gD/c/a(n_Qe}S^fP qs2p<;s1~L] ͱR6gycz{Sߙ Ur9y3PGp+W췿!>g?337輅TK}DwEr_,OEo=K]޴^%5=Kx|n|zipϴ o7TD_RO}c}m3k7wn{_snJƁ VW5uH:VQµpFg8/\Mm,4LH!'m,~˝/wY?:W3w ČiEcL-uq78QvܝaJ_r42lq^~7ݵ )QGvot+ oؽ?aww0e., ǾSut5#[)fy/6k U~ot0˳5^7Oh̼xFPҴb;8`wWM`rGs?$H9x7x2!i\93p@pX~3|, VXa cOd\ >Y,\4/}iQ WT;u =P`pEƟ}l|Nϖ#T{1>c<;N&=bbpI%{*c0e=܏ij5~WT2͹fݧLJkd 7<$ $z5iZX6eA[81D )m@⁦}_ieJ3sJNq ]3Y#U3lUf;κ%i:a{8vCpX4}KM94x|'UZ}9Нy#?QDe Zg5֙Zu>hi Zg9]twj'_-{\oA?^oyw݋r˯D~vliY ?o1"@q M=qGݢԸmO+~MWw l!gRuu Txœn9܅B;5!3yG n+m.㔕|q|%oM 3aE %'V_cVX_{蠰ACώs7vw 9ǹM뇑^f6k? O~R}ȣu+]]ex9>aɚL'O\{B,wxv3~U q]Ͳɸ[Xg_(S7M]2?35Ne-O?Ry]{lsP.y.ŀ'7 ir|oUns#K O`.fuOs ;H$Fe$ՅǠ—4MawEANa[~B]5~I*(SBuypݕ HG"we8&dzؕe v-I8=W)Όg,ÙW즍3\7L}?TɎ }Q?~~;lE{ 2e?;qn+8>q'GQ崐5w}тꑾ=t4kv3uTKH f u{)qjn}nƑO WƻLz&\V]QMkXa>I#Cʆ )R,+m6Qbch i#ۿZ~.pzF(ڬ /6ygySQ'm1?5g3Q+&t,>W|=ʛ8TC+c}cI9cIXNyLK L)̅\cݲ7>;觸E?e 6ϤLB0sƒgyǙs1ricRu4/oa'd~9GqݟĴSiiE'鼔KITdYv9%̿vojߞ̿qшwt #,$40+Rvp]ew̤'.?[|M7?Yڪ",^^߫7c()*?i;Ti~6h2ڡ6vdIr{Fo]P!.9h];vX:Sh,r ov Ms"k9PğM1߽ƽ*\ _1'1?8E2t1uJJ ʺL%]ޞJ ^(r?Rsj. q'ӹ M?HΓNYws Uh¦ۨn{miN8_I Kl?zod.T2(ͪM4;Ҭ4s7s2ws_cii?n.]vݤNyi:Itt:GT}:O ӛ-S|ck쀿p.8S9ӡ>f!-n@s.(#"I.9k9trVMn=M cWT Uw}esM{q?[aa @2.tYL^9µ( ił38.•n4Ìz*<|iw)5~QL\=ZӴ^g[1/tR_K5Y4ASIn]?7%ٗw <Ʃx&z?~ 8^<⏭d?gq`k1/~ aÁ'kݕ' =$ }U?4~}O!b*/n)fp({X!gy-˫,11'o@`S_v,S׎)|I_砦͗2~N֙-䮁*?ipO#Ue)=k g$_ri}f>ܶ~lz%;gSlj?MԿU9Ӏ _ J < 9wC>x톬\ ece< ΜXf|~p9tvPDN;i?'wt6pWDŽ'~_/MIl)Z xz yqXߙ7Pzz@8}!ӓ~)i3as7Pw]$_]Kt 8ml; tʗ+ID{A*<1΀v p\32Nѹ 9ު=yfr ̞oOG獬Yiz{^ 7Uw{e1yfUrS}EKy#wjY3PLw0'E/`8K_WaxG՝О܏c\[0?I/yr/&o2cܯX;NNON7mCp=OkTqG{K [!$gs|p*?"oȚҞ-?5[ oTX-{m(ϼU5"_yxhwCmONJ,fwk4qU> %{7؟5*_%>\ɅTݭSui0 ෷܆9OϢah%'~I:Lf3_OiW2 { yǼ-Aʠ`6Uuy ,28 c{?MI8ř a{kmȸGfpo*},߫ҏ,f~U>t♝Pzw3#j#_̼CARbO'"W}d { Or3>xp 4|*t“ϱ`{/+QὌ_U1t.(-\BKY9\ϙpu;Hu^BqxV2-y?nb,ḊY=-1i7tp7'KUיc6By?YEx28ߌyx*~Qj˹l;d\=JI,S8P|N=M(r'qw#kNV}Y)zj)q!Kˤ]G)̋?vu0w ˣ9h{ 8};= t --0H H"("C"")H(H H3tH|kEx~X vw)SD3WtphLFO|u2'F+٘L}SDou{9C2ƪ8ƵHvEejsYc,K<Ȧcٔ ;x&ў0k闁àYS$3;p,Y>1MVq_M`Ө4JpZց*̕ ,-mFIgAx->k&S\K{ҥ@?Jzt`8oa]iz1/IAlKه)ilQ>7ye~oc~s? N2﹝\<c"$1t;ɡ"{^AN2{봿)'XXv9( 2y+lʁ&ڣqSTj'ܡS.N๙ ;ҥ#ľۗqؿT>X,k `vo ܛTu)FV=&Ӹ3w)?әWu{ۏ̫x:r@3r`ׁsnCnO6ms}SE1 x~kN^)5kS ̰t$f%e ocIþq8U~Ü;*3'!ϛb=hY+>ba|, Fc=.$f@pdzONT{SqMn&j_Y:~NV'(lV[$ :Go;%wBQػveOt9сݸp= tHx-Y]l߆T8z6a9 OOvGy?>`?~9 Mko)-np[npkG圉y)NC9禤n/b"Kr#}G»  4ߚ8ք;kdG(yJv=dCb  wP#),i˳$^˳,7_yvsp>aln䟖q_M. /?ni|x_vo }Gf01O7Ԋ=Kv:q~8N[~y06n'Xے1;dSk:@aq>d°wyi|//ਃz^?qɧd-NtZ1cvbr8q.le&ZC,V&}>knϰz2j6;s~Vm*W(ԑX0|a3(+\$m21o#Lq|0/Q' _ң:mqԟDؼ}yyb;;T+qb=g?͆wp~9U@F;ڃ$VO3̞6 |WJ9߰жc@ {${@ͤDqm84vL֝g] F ~s SB{]volL3*4-uf8ĘONSwY ]VW/Cj2{$MMcgQGހg`8;TiI'5&3~Ə|Y҇z"f XJHsY>l ftHӰy?w>UF>_Ty)+eo8 {Gƙ[>Õ5$n.I떹>{ح,w r_x` Pᴳ?53q}YzITg}?ohTu2 z;cxw51{aqQܞ²yq sC=5}`f~G쥭,`m6r!iwsfywfY"}l[{Oxj{˓uSݑ9^BlꮖC P_i@l' 9hx:G'ch;CfroL?OGax:{= 2JU]zX3,LIsf&..W:q ysٻEYR{?A[h9ӭs3'2nwmEos߯ϡR6 O"'ofJðUaY2{˚չΣpz9Q9T enwWQdTJ?d^]~0Ef q 0@z>hf'`K@CsqWow}/︒~šs 3pE*xm5qn` ~ڹ1t>פCṦ'{ =Fي*4o8O^Z3҂qr3Q98҈B3:8c;339-E%cS)s80z(),)?]{|?C;Mq,& ]nր=f  wo>_co?<)#UcUg1cZVy/aGY}ywyk_Nb!JS~C'iXeۏPG?:K.j}vq̟ Lp&:0O?vkr?`KY>۫"2ѓU{(N)WO([),/0yQ#K޳**,+cRf;˼_'(GWB^G9%QFc]]6J }?+.rU̜V3YGHۀCF? x-2e7-Z}͓/_dΖ[dNnZ8E4JPf]ly5IJ>ʩH[AO^bg6'}ϊ}@gJ%Ψ;Mx2?gX>],t臇ȫż~x8D^[] ?>g ?6o6ú̻gGΆʼ;~x Fٺq}%'oy]GPo,1g#rN~! WUMnpg%;*>,;NACa.rW?d(7_Y,r~2mUߤߦAoe~o-bqGf]+~y]Mͼhp.<_ZBf1]=;ٝpIc҃{Ֆ=’NRQ^Ǟ #7K7Oa_yQvW3/;ے(T$ǙK͓]?2?\9/%~>R8YQߞL,=uӾg ,_XjҪ㝤.ؾ87vIoz ej.K;˗=8μ$=d^y=elYx%*TX.3q8oIOb.?Ua~ ?WyǻWO+/9=޻rYËkK!>Hڧc寖^*raY_WfYM%~OaY ", J*/f^b㬹 ;˧mȫN9(?1?M˧/~E8^|uyԽm2zpx 3&rW(p|L)ڛP9F'+Pglm܏jlv(M&l`^)I I/Ti.ˎ7ݗc g^AMJRnwR}8oe wXa)E/wOz]ˊa(˼~O ;7;ijżrI}Y<#}ڑԭi y|35파>mK%{Uch.j OFds}Mg޳)yTa`ցk̸5)W*_8a]Awiq i |LОTLo<}Vl%^jt&:]r5r7g^ uGוyoo,"#w(o j?n(xꐆ,19m\X>.iyA"A\yw=3=xSq·:$˙_Xg Rgmᦷ3_)GB~<DK]WìYtj{:ʛx"c>a_'_3t;U[gNLsIoL+tocH޺<o i80cl53o7s~Lrcր*(wgqr#T{֚yYpcno*|L+ ?SXl0ASga Wa&#=gw/U%.+) LLÔ!< =eE{AcH!Σ=o̡;9}.F=˾-J٠%·e?*+//>2sRx'bҝwͽosv)h}W?R/BBoN3|N^꙽4aEln)tSd~9l0v% Kx+m^{ey)au6IOw R/|#nq9Ҧǀ}IAl0y] _M!#~ߠ[g9< SH|sdz"[lOt7|ƌK~؞cf=ĿOFZ`FslK1zI Qcn*ޥr]{LqJP9~yv0+EeC}1GY['g:3?L808l_̸eo&g<z@ -o^A^- m:a.*#w)|B~p}gƄeph}T,x&  !ߤ|k /źVnC@6cU:cәp+[񌫘s]U8UQ 7R&g} x/}^b3sCt~>>͓h|xd3zW{= TJ[wGWi1wO9h3yPn+|iN8p.Wݔ*#ۏVZ/~\dϲϞGwQݏ!7H/x.7n8O3)C-Hs¸2fL?igG(<]e T;lpK+\ ĻqI3وDpdi0{ŪI[䜑{y6>Oc:L~?L牮u=Sf:BՖ8B=Bv8B |3w|w|q嶣~K| f#Gi/ZۮR$1h61+g>F Ky1ۂk1څῌe;3on}&WFF̴wvqߊ!7kvLkƇld>>3ߣpI{Q`OSVʏYn cMq_8qc곞2|ɜNb1ipa}yOM¼SWҪb!j~bfR0ﶳYeϽg8va*psUX-;X 7KT泔Ve~Nޘg:oK)sFG~ʼ& 03a^8O1GWx_sLf`:ۍz=zc7xKryͭ.Z3&E6 2ҳN:ppVka[#qp(jgk=.S(~,䇫Fa?KE0-0ƸqO%orOhW*~>^ {yݙ(gGrOZud3Ef ]1 4s]"^wUd0JZvd^o}J85{I570[~pP/ϫ>Wy*}5A64²A _Re>W^ WLƀ߭zy&w c~vy_Biy_N|ecd)ӬcPn)/oc.Hɏ2S}xUWH'sba]|)6he>Y&Ɓ,’:1?pɼ; gc_^fw^?jc+ TJ>P4/ӎ+ټ,g1yǔ}e. *[(~K^w%p}CswR5wK=ޕ8;{E1yy.KϕXW7;>{^!Kt,WOuh> e`_E7~a:vJ7eKv;+?>Vx cts/W '+]ŗZ*)}gT8%t4Pp?Rq(K|ۙ xr(eAmzl#q#(C s>{dw\{!r}T<1|RGD0µeP2Հy:1owңF1o ~]T.2 >IJ/>0moC<|]47U1%~|KUǂ}=mp/1' bzw&>{CGL}Wq+{;|rQMk}n{!NAG#CQqmaDxd)͵ȜUz^?̏̑S&9 x*z*n3ˏg3ͦf>#ڿ]? ô\16'R=6w>tӼwz0[/㱹_}(؜MkYQ̜x46dQ83߁_SqÙY΁Zj}K8xUz[Rٶ9*3l ės)LYF\ܛ0M;WNL۟g#9C7C)N0)3`Vx'1`r] eߜby9W#u8n?GCu̓sR)rFʂ1q0'߲7zO<؝-NН,/ _0oC;7Ч?;)٨'\4~`:+'+ۏS2v¶T[?~#|Lvgwr ¡U=>(7Go,χ >7\`Lmjo?a_F23R'=Aߟ8p8G9Ų2,{ cE]Q Cv'Y* 9/[q O)/Hqpp3<3:tQ¡U>Я(˪nZ^@_U\C:>`yao>}u9@Z~v7U<+U f7P(Ndz*{|gl{z%]1ߕe=Q G\}ep/O՘ n%<7q{_ ORxcc<1aAyK{nt"Arן;:'_ѳVq&{n߰~u3mgtDy{)6FT[z 鶨Cx-u~BJ+短,__ ]c p?G3B>3'wbl}9LM{^gb2)^™D8Nü)c..%i}g3y2a/|1G~v3hS紏uwn qL*߂ߵq|nrY%8VοKs ,eyο`*Uƨ{g( z:7W5ܪco[Րo*/%.جJQxǢKyG ApdNa"'g`W86`|lƾ杳0΀f6979ՌW\_}f˔]-"Tjc3} bڜI¬;+?+(ھ_e:3w)u@x"e|[@[%MS%vW| /^Z/Ic-Sp*>{% Pi% sQ{Wb\,'yքOc>KK\HrQ[T8_8rזYD_ ?KvڹL݉W3ф37DFb=? rIm*:J.B9m">W89[LVi~`MKŸ5mGv,˴U*uUzWWo(޷1 lak|ς\&{L^aێ=`^kUo5Z}?7Sq ~wkt2y,s9\qbۑdvM& _xݙ}G(_}7Gp oz( Oż@?0ne] ۾j*kpJ /ìkEG&(U!>CߩKjkHJjR8n5$L1A7oƻ/c<} v'p%cf bԳ_QwSUYۺF/.l>2 0Ag J>T$/%/Q:`)>?  ~!󚋞j?;nl]Vyw4G}<D:Zzl٭9zpPNC/} +c[sI^V2`W0@_az"8s]:lZ-a;I*OZ_T?3NE̼E`ӧ 6}JeSApkk!)s#n?ڭNeJN&g HX '}7I{k} n\Q!&˼"UڋZy :iB"~ !?eOdKUU5Ĥ73oV0u!>X-)3*&;7яNsI?G IxCp\(`3>AR-.+S:e=宫$@hzȳA}79eBIC mלY-~.s=2_m8|yzI4T7xϮ)h7I wIAy|A Lvy'vvJ`:I<0QLoKq~n曋- 0'3w`dC\ni0G} E\KAs~cZ"1ʀj;_/\HLP.&BAzc4"O۷"b=s2`ۈv/j`U;s0o?(mfwp1a E/ُn0`Y4(9u'*f} xS"N,_$K9Hz37hd[ r"uRu:}ˮj?L]ދac_n+d _U7U-ax+ t jwo0z9G^qt.;<7Ɍm0:qHJoXWnw[ XfLa~ p?wJJ<1`)\IEL/1gε`^ȴg/G„k>bsw4:y`rGw?Feޑ֧w3/K ww CH$񲿄qKǼg) a۔ysz/$J]v*5`^\uWka,6q8 4c;#z0c~a/SxNep6uIݒUb+9+ {C/ 1zb}ɻ%SЙK 2z[>R*",Ej5R}[soy~߭ҰRGSSp95{75(?5|QeQvdp&|q,ʎNQGٺ5cO/89^;~2s1ʞgGQSVFƐMYv3<,Kn֏%7ߏ%7'ĒlXcKY p #<9B=F{ Su ki Nee~͌5?cc={f4VkKƠCed<p>4fE'gVl=xxUNڜP~:1&$ƨ}8x& 45;D‚zCk3Gx4#`<4= )Č]4xc4zf_~]Ls_Q}2]i 'g8pfƉs|WE>}IknlI\~[w ڸZo8K q:nNHngw\0p76S]f aό{Nb >ƽe>)>#pf}dp9cܕ'S3q"u`G|PpYH|xƣx Fnx?s1_τrszޒځd'ڠ4 \|#=/znbG"8x'.x3Jte4 p;3``+hm= Ɲ_l36=clog0@ 3oǶp}^8B۽9 fJ8P;!g빣H*KPehȿd1zY~6u wXj#uz̸3RnzM*9yNOil73f6qYZ*G ܀}jF3k3 n9 SVnlﷃDPg )؍)uVn7`x!cAW2sYIgE}Գ,ʢ/,?n|\0]jv~?Jy"SleuQF7l4jtG#pAcylV:;ĺ_-.Αjt {ҁuaF 3D8~}MvH ~3g\ʸJϸe\]j}&` `LhM>=0>f3F֕h:/O[t=x1'yU}}'5w)51f,UX.>g^7[!_O!QwtpG©ۑ|W% 8c(wdsgcZ); {D=ȇE_ ,oa*u%C]FtMv8;٩_uYvBsP.ksjۡ\P\n8 %ݞ1=c6W<3"%P?aڅBHylu{cۿ]'rPn9&)rR\䤸VIq|;'q}sf;G^jy}#U| ]9i^8 JY|a)$_)/{-Q7N.q:tE.WWTnUV8Jv[R䰬mNeߙ 碵񭚋Xr=up,Ԝrny6N~ knlp;3~jмp@{\Tl2<8~7G9&Iw?ʼrG&y̼#s7_¼5`V1(<[0o#"ir fsΛMiy'o|\Oe\G1Gxd#1C,O{y{3@ f~󼸯U0?fГƅɸsr^gæ>ymrٲ\t 73/Fry]-10=O˄ԗl#-^oJay`wx{z:.2'}4~ȼ;ݼWU>2ᆪ-xkʳCun i>Z7ȼnI8 G2q;Lbw09J~gLt^;| SO#!˔hx\h4U~޿M,ߤ[oڧgtu vQ&̋p;V=-𝗟ti~H g[sL=lu*?X?y&鱇yiw:i7C QU ծ9}z_;އ<ϖc ZΛx@h\4o"ʝ)i۫Wmq ? y<gƸy+c|R᧌qrꂄ'ճqq%1}=woe8 'c3>YS麬 ^w f ; ,8 + *1= )hs8?&ʳ{n5#M_o˳wpo֫..:O4R܉u.}eyA*SeȪ0`xp_{ո`.VB.6\1ßm?jˮ_JkE 3:8ʎ|){tGlP!SgsfJ/D¶Vmb=w`ajz4 jis 8,DxEUs{˓T<7~z ;@9w}2͕\8ThœُWK7Q>xK#Gl0VC 㐇u?fx1jrTo)rUZh9)¿8 _0ón= aHߏ.1QMH4Y/ [ָw y@bNgyӋ|زbtfy}>R[f?R{-hρ_.O߉NζK`ޡ\y273yGeɓ>7m%h)c޲g(6eH6M> ^u:;.;.8Ga’Nd$buT˘y,n? c\4ϲLǪ2Fw.[}h+}6GWYᨵ~8.y臣~8ny⇣?ɥ8Vno_.'eb=wA{;ws;wMÃX{¼X&sퟌ{&Oo7C~1_rN5pq':s.-k0;lsq  1v~Gv׮mcrvjR|ѽ6:!!3}Mwa ƴ=-Z#֢L菋eϽ 2?.syrN x>gV֜;Ohy_a>np>6̸3:aq;イ\{\Pc}r4_c+GhoMrT5{0YY,r4t2-|Q/^U]s~ xyswe$U{aS],㥫@Y+_9+7eᛷB\8w\݆AV6 pEXGk1N һ;ix|ঽTqPZW{.3ϒWL YDc10}lQ 8}l9}P=I,eYgǾEbcܣ ж)ضIm;%mCQ6#SLwltr"eհ,O+pv~Ĺ>[֔s|9k/rx7u,WЏ?"'/l<`xYW//н?W܏⮄K*w9_ϙWۂU'v_㝋3wc~9ŏ*318wzl_17#G:jΧܨPgo.sSrLKsxܫMm2ϳ݉5ЪUS&+zy*,{?~ ?_c::l_?`w#>hIõwY_Ҳ.&Yʛ 3=N#o1~Iahg5\⻝xo^[C>lYY/Q>K{}ϙ]fU"^kU/#'8(&c܇Ё1=3=\*Qy/~~|fe^2^/,=qe0> /R/?0# 78¯)EQ Qx~iS<{iYXeδ[ԇ9˜!KN42xߜE[bh_ҿ܃T P̎SXNwx џ m">cGa?c ')MQ+-1vɯq{YtBj橯*P͸[{>+ZF5v3Ƶ`ڝ1!dVxc^|Ƹq/~SS煼Fr,:Vq}zsyzǼpP)[8wCKJ\:[ddI}ZpUmI&m:*l8O!}𧚁}2o';!#6#(kg^ׁDQnbM>>Ʋ@SH֤mkRX Y˒!5x^:VmNvnGqxמ_yGg=iOa {WOޏ 5L-᦯u9 w*~ލ4~giE sfMk4֫5$xQq xd<֝x1=8?ѯ&ߩjy+ռDcƹ^[?qyʕ=&@Yvc뗍p Cmܤ T5ͺHiAvdc(Y.\VOGaf8mrK#jzuu*w(eAʵe^ysX$nǻOSn{nV'2`WLζ@d1\ީĿL>f0^ȗe` )evYkr/ǽbn ډX?0>n1JWX>7)W J6Ƽ2FoV旔ydc^1[Vӕ(|NG 5fn_ Uׄo`-Tt.˸dm+*X PxcRw P pzȖSuyk*FRX*c$es=>xQ2),i}}^6>Uy,gF/̱?|E];҅yCUbY>bp oFobWneKLfqFU74zSXxSeuCy")X0CO;D]9>mHߑF}ʼ4Os# [ 2o*QX̄l?6qN>٧9۳buf%N[E7]Јt-V9{F$U&0<췒C[H#s)ؘyyi6!z4ȼwMVY09. nl)qjE# #YB  B#5lTa:Yv\CY, 7&4C\ׄiB{0N5}C7ȏ-+)hJU\MȜMM-Mc={ehSڋtK疕~?ڌ]̭ތ٠U3rS3/eP3s?3e<]ϲ[x߻x j7*p90?if|f: %a4VN3^М08pNx/gqx/gUx/gx/'Sd|Мϙ8${g<'1 㙀0uvsIuR 8eC/x Wx܌W.x b.xʌxoY}?n[Gf2 %o~;CO'*|^\Yesp.8><ҏ[P;pɡc l9yWlZ ~迋Zj>f,#~)wxZ6mid4nPcy#&n®`sM@%]ueG^ֶ>`}؏hI{ oՏ+Bs֏Z*W=ɖ:yc?"wAy|˅sSf0GuѿpCO7_^gf^d^WʏGΚK oH| j̋}A66&&o*7q/7,qJ=xߧ~ x'NCoZdccs2 $O\&0;dwDӇ 2pKgU&b6e[Z]m7H

=.#}ƃG_|'p47ߋ & -xy_8iwDzvR}; 4ؖŻXj%UJ: I0}eCCӷ5ⷽFy&yyוjKu=6`sMNm͜isCemK}WCjKUl?㦄S>ϼюnyLMw]f7>jKq Ў:4RDzg* t|ÿ?d0 8b_=`f;s.Ko'~TZَx0ୌ.Ce<ƠںN3o2р0zx,`. B{Vq2F}2;c' 7wܹR17kCWƃwoY_fnsD{i']GYEkG{sa|$G~{z~(^Ft sH{k5(KhqMpv.(|%[Nx~M psCD p/|#\' ^*|ZE〧w.v2!߁ߚ \wnK쌫ϸk5uܔ3L=nh厖I=ִavy/1{d_U׮3/;?'s,kmqOq_]\?gxy#I||G>w^Yxx%W aƷcErWᜧ;'*j2y(,郃5}0d|e`U_`AQ_:@Od(sNb=jɼ5_9^\ӛ|WKwzotTsL;K'}9>t 'KH2uMYrS%~^`k_^.QWA>f){<㓀k($dόdpoc2?}3w2e;ʼ2̧+2#:\yeḢ(i|2U_QA]y.*ʼ2+]L]ߦ.>_Q vX>3m;e Jw= ]=V^l]\`ήf.8z]; ]^^y0&2Fv8C6N\D@>\`+q;ϸc)1| 0ߠYf`* u+ߛѲ8Ortwbp.a>6~pU6p w`>;θ3I5Άw||tG2xq/211+ChmpQR(I}xdC$ Q[@M~n<|-H{mp֕)eξ)P 3NdmPлwvz[vs*nXٲ*p?߂hJv'!OBXirBvwse+ם8˞G\̫c 8 $u,WPwnv9}w\)?AK' i/iErm8u>+b8N9pl>s!"wߏ{wJGWu|5/w'(}6% /jCmE;t{D88#s1qYzn53>gYјU*1c4Ni%=%ѝ\*}rCWѼeu{ը~T~Pq's UZT+_/ی g[pNӐ:%p|cx p.v/yvq \A\cl΅X]^7c*9TlnNߨ7*]>{^Afqi~8zKQ60 RN7#l8'cx,:DQH]߄O2?nkORXFqp4nݓ=͛gړ_ YӼ۽(tg)TgV嫜#OsyyV?^4wu={|9r:t8p>E7E.T/sMUo~3i8&zkzj.J,{Yp^[eicoڋ0Eò~kgC[n"s|-c|Cc|5cB OZ&U;U (F|N`I>s͐!{ {3p:o"ڌBF~]#Hwi]QkH=oWw:"p} 75go`tW~f9UѾI4<G_svyIx4Yo]h\×[;_+/ ']| o4џLmx3iOWy?VQ^W]f^Է%XwHT }z^bpn<_sq \c81^8gEvumakMQwkޱ}U7;˜VW@9pi/JxT{ܷbc;dbsP]8#7g܌qfgܓ1 ]2U5qgt`CAvr@TlW.0DuUb Y<;¸)Avgc\I?Av!\p:0pf\p Ư̸ຌKn vƸٌ_1\Wx1k13 oհ~H)kN˸&g2~ c0+qm]܏q]Ce\𷌡~d `Ÿ1ೌɸ)2nؗps)a\ұyKxonSн%31}uOհsp̓x\AsOp $²!OP_u㘃uvB_Oa=`ö91Q/KcB;וۓ﹭nƒK}i6g턡Y!Z'HDӪc\.-|0 OOGG">/:P_%{)xS8\a[3kvyIe7o1{ta &l?Os̭f0G/f{c d|2uAGd?.}0_seoull lXf岴??{&?ywY+$-D:W`* 4OAüI~],tOQ8?nq·L侫Ns;G^ٴo? f;+߃}%^/0Gnil4:/Cz|sQE̟?e#[~R! Sng:Fn˰?yk7]»>Cc>11/pY]n$爛I8eIv{Ϡ|p/_;1``Zaq[\q%Zo#nYTdD䎲~?y~ vטwQ~BsA8k/x;?*^kq{ɇL~BӢ1cpqg<2Oߴ=Ry遗wvc>s}6V\e^vjޣ.{ՎՙMFx#Gg|Sr*4c~!ܜ4¼6fM3Q1]oo0öŲ4ܭqw^X60F}ao0lNM"9ʡ؞a|qSG7oHϽ4#=U?7b8's&BZx%#wImv nXpK(;~hmVy i=AL+c^3{9i^=oWUSV V8E.p*\Pr P#/9OT )~ >-~wA 0ц_Va.w'8mQ<~O!50'(Eʟ-KNzE9XS[UKS(Ԟ;L1 ~?#\VocԙˎMCe6ϼw^惗vEwu^1&-&0~-cT1ޚx 1G -`:~mwz7˽6V'cXSd /cO}MwcNkd0SW`^⡿T0o0*Qci)Ed܋?i8@M+;|c =z~9u&=\'#eco£2x?eko#$O `wyn>̇fP83ps+t맼 p([]']q6KڗRpq26iXgo orWD+P֔qqnL~7Z~ٯ+4/U)kS-]:Ų0.iySa,1_Qdʜ 󕙧,,9X+U0 [JV~SYsV{xц?<)/;9 K=yPd&ZT=` xxmj?qϚs*s;5.t{3/ȏ{[ڤnA'/x&Q43gJY̙˜K4e"< ,0Rb=32W1_q1Nn+kS4ʏ|su1[A܍,q+Njq;)T4M紌)]~ۧq<7͙~<7o8XqlSzt*ŽOl/##b~B&v KW~ t3r[:U:s%+c\w DܯMĵ޷9V~MFq𷝱\~#7_SrOr}"'RD?Hs&D1Q'݉3KL:hm$Z[6֢Dk&sd)5#'ӚbɴXm2)LkO5iMqdZS4OO5Ek )fBkEX)} %BkZ)x?Yo;0TJlS) KO4|c*a۩}3TevT:{;~*:*؟TgYsW%W^{%g(=zѣgUxm.a>Ƈ*;cWaNe{0h+V} R‘3 ~<3yy)T,7OT\ W8Y gp (\Yߙpg(rQ+l"s1yY-eϱ|΀yk ]2kAfyovy[ʛ';)yEF h sؿ}'7 ٘Y|/Em:U=oYxRcӡ@xmy9&)['ˍ.ߛ所_v&K!֋gu24rvQưH8H)~6m26{cv0c.ƚU<+p&K1659V y,0Kݒ s_ Ua]y^q8|]yɎ>RxG1Xal̻Wq.+ѾSٔ}{Ƹy:#Қl/dT}&9&3 Ɵ'MRJfe{~Cx<Xvp{:\Nvr39ꂴӢ9US'lu;6gzwy}03ݙp xGC?>8z.Rk{ħ\+ͥq?/>I += 3 jŲD޻;1c,ZFAs#o˘1<)xE^(ˡª;e;7tF ng~G:b{u(;/:F!5s~00Y.PWWX)n5c;c1߬ }<߬ o֓o֓7wM85bYڪܣ<<ܼ<ߞSk^ b{q2g\q [~uv f[*;(kpO|rAm9 h^\9vyh'c _%)Ppɤ64Pd4 YH83 =z7=FtE'Bb|-1M,zCDB/ZYwBS6x)-<}0]˾G(us,o e# w.T)hvp\BCfe&Թ `m c̻xWeBT/ea }sbs7tʟb=}GLߑL߇J?I+TP^?Qyk, :?4#2#O`EUҟfXbgKIIL*}t*FzɼR%YNM Ͼ~ "%Κ$ko/9Ax@w`<'DymD*7Sb(8t c)K>nۿ=C ò!\M %oryen[bppxT..1us#?ɋȥf*<T~  WNqK 灼 )W16 gT@:^JIccSQKͽ5ޤ7)qc5p| p=N{)M|8/5]Rs״s {quA3p%Syw}9VY{+~?[kd)\ 7hXIlU9ޣQ f F3+X/qSacz=.}$c@ea>^~_Sӧ{~“?L_%q]r@nTF'wLub~xx6e/C}Dc~o]2_O-k톤ݡe${T?alnu'VRy"xq'irwv; ;ۄL̟#od,Ub{Z0de8Ig}~ܝ=n^¼__pd~g?v+CYͼ}^yXd3o]aSzÖיp\[Ayqsqrxc&3F]ܟnb?+˒~ G,zCziNE+?_q?3F;tEMC+{%wE7f_;99kAZAXk?4//ۮO LC7\cg.~V =dbu0O4\Pnt %8ku J>eJz8X8n> E#)Mǰ%Mi7<pՎ)lO$2dAJZG;q=%WhA| ?xA ȇ ty,GV\$;VH8pU|7@$;Ж_oBX WQ$nx;`S{7 x~S}g@W|yC#19a7 .S%Q2}Q/9x>W|amEv+u_e Xs"YI`Y80$w%To# n &0 qY)M\UE۲y}U4R8CWx.׀TW>c=%H CErʿ; 9E =#K,8w4dg:176 &(|2? Yc31p}֘ȧy_cʽq7)yNtuyUnܫ<} m^ƌ- yV,!3ZÖX<\cۭ{9sf}.o]#'k>3YC/%kny2_ 'Irvui9z+XϻQ~28ZE=wwZjy#D.Di":,;:=tjL{ʹ7 1evZJkJtdZ:Zm|dmM8_p~uv(_p~m5;M:yu:J׵pNo"}AݴWLf:ڣ[ u<2Sl>h>7 :w\1;7gvǃ_ pPs1{~s# ;z>]LY/plyj_Y<(nsn 06СEU}rHP_G^"TKi\B.8vJtSU /UrrIv 88q?#AǼn~)>#B `APX2zDG9\_@9fA5xw{}7ah*לr} /cWwz}{_.'w8]e<W4fɲxj%㮄<ёy#TV?ۏR0Ѳ;wO,rwN0~U¡;M`Vc<юqPϝ~¼~d<㹌 ̛'?J,MUiuxװb%eoqzGQ;e 0L8.b17n-/l*:^T v:d>)F`?,g=lĹ7ٝ~ysz-4S)qN/H~iߑbpcUfsq,-U K>`ew2}]/ˬOeC4Ilj~ۯ&_?njgL{s7ʯ4ƯٯNCB/Tc+W)~+{1/ݻiiɖ_ w=#7g2efQk˓N>>aLG OTxodnʻű?(oF kp:ƸqE=VSx^O%1Ot0ݶۖ9u[{Mְ;.G|q,ч;2v]:`-5N?|þ˿eun2.p 3)Wwݞzv7P/FXx] MH2)>H₿ {h ̲4i[_栉Wv`Zwk‰NIxs s?a'siW/-U0 ư Gaۏ@o`BIP3^^op>8 Ϩ;&_ƒaϚ0$P"ax_qA{h% uR6ǰfm׽f4߁Yz<5T ?ug98 B6q»4 EaDc}U_ 7iV 6fw;WsAX6 ö\fo1 >:Fﱖ[1'wg|9_b\{̩,ՙ1s|9ӟͫ ix c bm a|;T0nGsi&nAcL x%73^ x/$'(:МÆ4c |1Azy{iSNCw/̑+| lN/# f~7g]<8VewPG *_>  PR/) 8>8-[ uf<ȘGlC–ܸ 7ϡ7Qk÷9(n ~v 78ݙ/̼Yx/¾dQ5`,[A{gy`]+k?U]">LkA>@V4~06h|$| a>ajG|wu3p{SlM'~ _t;l1H1,=Bթ oU8Qf}a1M8RʯRo DNYTd%%!Gu]GIʒsG,o`?Z*~[=Wٝr/q2΁%;g8}8c)fY{YzĴ5㏘z"ct8=O`],=z=eQ.axc{ߪ»N.zrG=-9ϩ]C]󈙷v>?bmama@+a+_S捕y?L;sq'20;ܔ?Ȳ?^oк/λShY1ȝr8f-[)Oi20WCs?]~gy}py9!?=Kr+;_D G}ߣx8Wguy*/<ܩs>X jxԜw-z3䷀G(u:Xm>Αb/ rYzcM⯸9tٹ ]MXblz~\+MCi^9MCO4)zȧ3ŋOǾvcu6/a) F8vڔ;M~SRf0_1݋}?U N76=C;Rx~) WX/uglnf`R2nIx~a붡f_oRyciC>Lɻqȼρ?%̟'S'1Kۚ~b0ۮʟ Q)IB g\( Gp2{'gx/N{>3b)3y Jϙ>9ǜe\ ?f5ƲYI]]o`Îu0-`{c-p) GUxz/'{%k!9۟n ێ .L u1O`g:9cD嬐#p9n9watݸk(seEx5s©zOx/:Wċ-u E1t`|x|*2/=ed^.]x߿\yqtST=Bǫ\uϭx+VԻ@1% s^3/'UXdHa[nμ|Qvb3n? zNml_Xeo^Hf~Q#a\Ej?q' K2?_8˕2ifujPu[ 2,'OhFU55kn38VuyU_)\`G:bvWL[i14 w洙^j-G{Yvg|=kC|c||eb~-G}~U]?ubWbzyo Y컲}#Gy{cl;M?ϕt鎌=~PZfMG +;`yo/r(۷L؛0^i庲y{f]vޮp9r{;yMdgy3кusb/( wY=x'.$b^`_1mU)<^ oTƟYxx@;8SN@X3_7nVJRxu:'o_>y(:r!\WOap +x(wSOR:+J6&כƾ'?#+oR[&:ʨei^edmt̛ǽboۑy߾71o}v7x8O33!+~^Ѷ/̛ s7 6c C2e*\Sq$ ;ţ_ԇNٖݙ\y8z- Svg1_헄Elqne9}ȍbxȭE[JXTq$˗o95n ηM0HɌqOreLߡ%(Ww xZ1;^ ښME]~c!}{Ǽ#Nq탿qv <pn'eEׇ'`m={ٟc玾§Eb9W1z-( C?w8u6\,ŤJc͉e~3FAΚ^ў{Sh}nʾ {8Ib2NOxZ&Ik꺄w) rS#k(zA+E]OF= i Ϙx]qszo*2I:#^&vHUlPL!'gާ2'yx$~ϼ%P15f7k{;e9IOoޒ&2881~=a㒽9R-&ܹT9ˡpvU(~ʉ̵HXs,?U;g~ܓ}g%_ N<0_:q\F{ Bi?tC9^u'&-ݫyRM]QW ai "9së9qfqh/q_b'>(6^raaP.Se©<]*>3$@{r<12+X s"n>BgF2aA/ί~&*k=ؾz7V13׳Wᓌq_eƷYcoуXy+[r)پ˞,/]?=?!('Q]f_ߋ.! 7{q8L8adMӏi.6M$i`HR.n=y,|_~T!謏s6[no1~p{=xw{#/J{ F0χ( a"#꿖w'vO8vݼżo,:c$R?d 2 qf\8꠰eedVkoᇅ,D{M_(7*M2Q11敕[ʼ?awNĽ-/|l g \|gủ!$'n&Ύe~2XXaXQ=νRCh?SזcZ?Fw;ݮoKøLy/ʚۤ- / o"OyWܪ'',h|2?jC۰nͿ}jfۥyJv9]֫OO7ʎO)ڜت؞49y ~FeFF}cgXgXgXgfgŽY|?E' qs0u X]hQX:抴 x2(Kf4c,dn} _ \kAe:dnL2Q$+dJLmNI܉8!}~L^¿G0?2's*~)<`.s\C;SK'>M:ޥ.ѣ"\gCvΥ3;윀/3's8<6;늚9İ4V6:i< 0Ic &*DrZ /|#;/ ﷂL?2 "NL^'6N`j9vo_3w tH܃1 D8~84pP 汛8161wA 3i3MƷC[lqWkwM b@cf}}ѿ5JiR9syv 7RxOw7?BLu>wwbr9`jp6+(Rឡ}L }?37iJf›n9uSۘc<<dC^aT 2?jL-Ӕy¾+2ef>m4o§/aw3o" -萟*Szȹz,7Ə->bOnPIKK p1f,)xBnq4'+=t yT0KPnUr)|P{J6,a:Py~-;M;[2mYGAz_z˳P{g}n櫶i[m\_FiƩ8L0:37{^9Nyk[HiW Լ3eOC[Fw0Dw*qW1*o8{aܖ1Gtdk'_i<d|%OIs7XܖD^Sw1ˢgT+ɛ)MW꺾E?"xy窑&?Tw1/bJşJn|=)yvmD8̍4q[i;Ҕ8ҔԩcҖUIa6ٯ`YT; Nųnٍߕ=Q:B^G?qlS#C/~;h?d#6r 'o9ok*jgṕcop-c"OCl4 y"z}ezNL?3W`j3^9-ej38 3 '_%Nc{n\ܖ1;"Ma^2o/*>hebؖp6~Sz4_ؒԍs5){$%v%(?eEqteP\q>MZy+6m'uIC{l"Gx)6 <ͮ?2,wHOLC ˹aI+ˤ'E* a%/˓ҥ8)Kx2N,_P8i?wq[1%e_V K~\iMZavЫrϝ ӃYc%?#l3ĴF?^IJ=n5iY;r;iHtNgo\:ve}Ef{̃,ۆyM9W"\Ƹ^b Τ3^>$JO7a)diyY׏{ߺ˕) N[+a"OؾZV!PJv1;6~W c7arW7']u ~(WN+,cr?V٩`A8_s@rXGǝoZ֎_c2&0}-C~Kg+GwWUlsJI H:%zH" %4T)+WXū("**ذ`H^kYgg柙5}MO`r~{\g#czjPt<ە s&/wV˻fPU@k- p'7Fݩ$M}7xO^=xx5;`+wWN I=h5_G>8ȀcXz LXA~ BxZ&3/Ѽgs;ӡ\h+ӡ*cLt_x6Yl֍jjoaR[YSgFI U4KNNc| 6/6qiގ6/N iy ,dYV*6Ȯ>7%w^t.[c :&EhW)x/#]tCAQn J54xhخȶf5:W',︶u 35֑)ٺ*K~ TÌN6l]>I"Lc<[ԉ"qUW+ap/I|2DaVY`+ܗN`7WʏӌV8$>cDT!q 1ch-.1ԟM1w>\ | 2P1DOˍd~OYL%1ۨ3'|nֲ M;Uz=X&m{Xlul?Y܎<']{~Ia";6vy;'ee=oki?SRVq]O;fc0ɉu)y?=֣?@]Sv Zdڊ]v.<nk<y̏[L} Wq{O|QJ|]uoko4?+@>\|Rv] Rc/*\UsI;a8GyЃt^`?Z9ǿ^#ߕHE`\G)[ \#?q0d~DZc?N;ǯ䩜hyN_%#m#l9[&ڏ{F ":IO(¿*Cs U¿S]ɣt(_??/ e 5(ѷt=h1k(00&௭o?o&~üD?i %IҀupUk+ dݐ[8-~hS1;3iO3fi~ϧmˈ뇙xk.M!y(|Z ._UyM@U0#݅'C×4Z?Lc~o?|)~' /wJ M7423 knCϳ ?)N*b:6252<8u?Wغ: wTXg~b>UXQw̿.ͭLg2:k4&d?|gh~2ћӁ?^ ?0:PZ 0Wmnlgelii}f_5,,-ZDcny;qc]hLg WS28ý69p[ Wdp E|whC e~TpJ|qh)zbֵŇw+<*"[.g/'D?q?>WeyV(yxBHGq-1|x9g5yW /Tx_DbزW%sb Uz6CĒDTr`7/bwQey 3߁m=M~0 Z㷬߈߯9t{ u hbNU:6'IM9R4mW6{TJO`޶k su!n0o*Q2Kq\)}~m gۆk-g?Gw2m_fCGwo}۞pMLPWa(\/*~L3o KaUO{=r!>;X֧~qqca;2yn |C?ߞ`kݛ@{M;|]/k薉4DQ`~gi\"q vwl9Ʒ:кHwZ5&:Iག!jMWe N<&U?*nJ?k16|>U^C(!NNM0$3)̹s LJXًhN4{ћ' [VQ$[YcS8L+qq|%KJ*Gq'[}l]N$S&Òn&αšk_:VO_#ۿJ*kT4z&X$2n>}E{I'oo4xoʕadbߧ:#^gծusQ_Rإ8—O'PIm,G9e?*o;Dk۶ciSoTh.od&,`,Y.>p[J0e^3)݈~{)j,%)4{,KQiej(N;Y3xiߠ.ƸCƲ4RN`~#Wx2C;6|Cݡ:mk7p7].A=c^gG{b u,?Luu1%l WQўld~Yx1/x(,nt:KɫNTuSͼF2J-RƎw!?=63vߟ7l?3v),,~!Uʔۖu[y9]9yw:ʚOu8sa4]F⅛+iٜx3i' l?[<榼LR~`47ua"{P7x]Ӗׯ_MKьyxbٖCk{0}Q ^t#WK{7[Щ^?wީA e 7mi=8TxmK3)l>jRݭvWKvTx6VgRgq uh_V `.VV28΢HϽVxio2kO+%Sd_:+e|:^ rװ۱tmȼM+6C:p oH㜛N2oZw_̜O[E42=΄K't҉^i?t{k}nK)O}U}U=c%:}U_\֚tEőȓ3˭fżݚikm7/K;|񞠠6u1jN;L#_VHfmh<QNmMr;$}%o{_= Osᦱxς_e!3ҳ _AʿZuBոVeTlf>d%6|f2cig*f&dH^P$L9 ߊey?݊M KD|vQZVdMH@F;јf5AOEwnw[SmJe.ʞpCXZ23fP mG 綠NGgne6p0S,k2~h 7q1d'쿧6ayEdAt؞SږWa\8Mn:P'{ս-ݧs[[K4Z xK/-ď0 [c9}w`6q匋? x?3Ce |?c##o  [ѽ1+ֵǯq]~5I4XI 9F(}𒕴v;ýt7y{ o{l;f/8HC=iqH{{yi< ~zC|^ex_;_Fz;Ey>.lGw5άnG},n|g%0gče΍F- ~IQ/WWv#ZyEqoM|*IZN:uvRLa xjY;sӌ??9l?5gg+s\֕0P{~}SH牤++9-1;-;Ib_k!dԭO+Cm W?jw_idK=:O ..מ60g w'?\ƨ;M ^'}er{q7C,ƣǫw)n vW>ٴIaXÑw*}]%N9ؾ W`Že-7oL7p>%}ueܷd{qߞcU>cYƷUPzsWũeיU$.PAWCtv>v_/`JxZ7w^,RI8wco4ķӟa5όfy˛ *v_x7 PN|&FVFu$w qye$G} ۧ({ GNl9i1Փ c:ÿFZ/:@q^tS-uD{+}^KV8tߝe={y#ᙎ捄f+Mu)6~?7wNJ{?v4iQ'c;o aUD_#,?CY,Q~O6BTzpipbf7trQžwܯw(|O'z=jY Wegq)zV2@_ ;u`ἢ8,C( >(M6uoἢ8UF3FsGz$s؆ ݆_*Nbt6wM3Oɳv_ds(?k.3ɗ@/y&MbfJLDzX\nW=^.܋+pZmITw7}un= yH᥀/e%is,J Fq0D*͍촌eD):m.PxrӦ,Rxch chc{nٞuKsT/Ǒ9]9~,c9WS!.il{+c;"m]2|1=+糌/ k ;%]$mb vew1b*n2U>#qaei-밻m-f2P#L$mqѲ .p711˜I6@ )a޻_Qnv1^eRB]ņH]? !n6XOTZʼ.s)*42P- 7Γ,|V8.òA,nj{3\9CwG.c=As n4pQwߛ%F{W;iNwWx7}&߃l3܏aˎvnSu@'gٶ~{>tʦ5wTz3\/66fRv xae3m5+H޲ɶٷ؞mo94ϡ01EI o8@!hs+IOyP[i%~OJ~ jC䐼CY^I?L`V!99fmt7X{i9k|)kr^[K_ʡ1?19}c≯q^ˬk}rP'9НV.aSnB  e \p/> N w*|}HovLŠVg.NV e,q-_kw3n$frRK+x-TᏗYVe<4m%+Yږ!K%M,S|1ڭSN_tCLrMqO.eTWR=\kր=ý2O/7w\wy(DO#rqf%O[)>St?8u}#ƀnN=u"g K3.'=uwCk$}Գt#%wO<COnk9ńƲފ ӡ(T qs '糀x~O^[mY3>HL-e'=Ixtnƙ{NLfG9ys4ovkmw~(ݢp714iWΕؾ r܀y9#\KEv T2hQGɅn|> f3c A ۷יWfojs.Ah̓vst𚤐ǙWTۿ?7qSjW~52*AW,ӗ[-72kK{wQ02G/j˴k ֊Mm `Y?+Ӌ?#*>.ī5e':tJu$kFOBǚI.ΫxTǪ[ ̟ifޤsz,`~򶫧M9쒕zº`[wgCmȼear?gغ,=@oҝ01?:!~4po;?u~{/۬QVkqY5s -_8c>}8?>jMNq*Ώ}L;9 "p`Y]k} ?i5wTLkť}M|_.nk޳= >)͌sf9?B}cc7>6{g -O9q~fmo c\ۻO~+<)tz?0.s |ю~OK+'s]7qΌ3*z˞{il}*|R ?89_Đ(ǛdV~)Q?H˧0SC)ƒ|: b}˓Oa}Oa}OaʧPXՇPX PXC([PXyC(qC(C(LJйs;C!t6x(͝sCmtvP:w;s(]ڬTcT32$t K%sK;N5v۲YdodwJyџ4O/o6C{.{6z<ΑI?XPu/P||R~DR;G{{ˠ6(>(|[8$'}oF;"Ƙ3׏1clm^;23zj|=|?n_pkL*j/14sL#9]GwҗY{E-{ ֤#ޙ bT7Оa|o?{r&[.]L}R&p@UK̼E kχmé87SFÈc][g(\Y G~g+;VF52{#%?3/ܟc\nNf)?xi&~aŶL0/GeSʋ쾕*ew]kpbUf,ƌ۫vZry,mZ~f]0Rƥ|? ,p7]|?ƙogoJ`;qKq;z8-Ɠ\p3FWZx~uZ]M?]?G_Mw]s.Q=(vY[k`Ӧ2K˚¼!sVy-gaeM%/TtgNl?Bc\e^C~a/;?\',SgYHP}oe3uB2M  W@zH c͉fM$vh^Ɗ炉ꉼOu"4<3:l>̝D~ΘD~(H/ywQw%h#d'ɰN;8b;scNi;t#+헱<﹏eC |op|d3GnD2}Wyi~pp-c$݌{[+cd 9{vd*w:n yNI5wndw!xߟޘ~0\7Ҟk<|-V'јf5#_gR)r{ӄ?D1'pKT9X0gM5jXj}$DZ^ov_t'zc*՟E9p7twQ}{i=xĢh '!Ǣi~n/ev;PNLxȒlMS#?h>Jc\[;4QEqi+禚8`2SU·O3)?+<ߏߒ~~^omޗӌ4M7qӍy#eӕy'e+e>NS+ޠ&L72+0g3L[v~'0y8OaI> Amgg&vE“%u"?"g *܌1;d{fxәF_8)e[̏)|Zqgf8+R,{+;uxcdzKߝXTɥ-.imrADۻ}$v*}) ]~M?  {+YN wl/2o[j?ʬF3-#F-s , C!\VC2[͡xGAԟqNu(s:k Or79:&a RtLg}3L9<<[̫)cs#(1#,z&lx3sGnp?q^";/|>?ҝb'T<.8i5_D>r /ᄯDFvb9.YNgUOVEsTDͷ4H+,e&Oq%:3}t6wM-oӽQ|kه[{y?|+֭!m&2s=c,r1sVt6Ewx Sg1x)O!YW/pn2.s5ct8*0c܋Bg1=OcW1Nr%,o:Bٳ^:1U8;JƹNH}}S=@޼ t? W8S>pl*iǩ#ݟɶe~>.|,2Xיn w0 vs";g袰ċ߻OQ gqxyՆ>"#1V`2ք ۹kͰ%L+-ӖE4דENF^'tQ1ΰXdϰ5ھq"nAȞaȞacϰ3,mwU=Òcʱg2F[̵/ m-N˱n~ɫ3D{NGxm;lye$fIcG 謲*;OF ݹo38Wqۼ0T:8O7&I ùīՀ3r\=.rY0Ɓg d@ j2Fc\?<)Υ7ªcՙv%0E-_6\-K=_W [֌\4;<1!?N| 4iKx<_F;@W#W_k2@18t[;Mwcrq!w&+ߘ< } g$ؖ QWIӒqM"UL۔vƖ ;{APy{Mӆ4fıyۍY34 c-bki133'h Q<ˀ1/C\ZaQobc32Zy̧0.ngz#Ea?>8>1xy/Agh/>̛0F 7c m\}p"c7zqc{aluҮVӺ.in>6RvtH@\X>|+l}kk}{x[lKoN<ګ dzl.c*Cb7w$!ML*c'Y?!O[#L ƒwy A"@Cu׼㣵9\C}`ksq} |;bc[x0ѝGw>kGDcJ*wyn51$o<cvΣ< ҫOo-{n[#*;MM}lsHzCkNyg+{8i78x{P+|K=XK6/%F֤G#j }Hoy2n>?W'r|~SKHwa>~9~/guZQ`_6MG|wKZ?t {:^`筧ĒD8γ/+I 7G{T&y:}{=1 Y?ܳ 6ἷh1*BZZ,6#{e&j Y1 M7ZNEc?Aߣ_bN("y<7kY{Pϋ`\]q^_dWd_~oߑL_|_ bqLt3"Ke Wٳg[-"tɶ,&G1Yl$K+0l+0هi2LܣC/̫~Ƿ1d,i۶O1F5tRcg+_~<ǝg&I6w6!שw?a[ʺ2f/m}%3*YmֿXCEܷun<ʸ,}/gzxR]QuuuiS׻xL+ʢF&H]f*A;4&--rAŗFx8L's- 24cqخ*A:dgA3E͸s6q\U\jᕁ]T}eT)Wٌ?"D*[%%l?VP{!J{!x|l/ w yoC vxmui㥲J1~'m..E{'%lc^ꪁ1sho^Il`W(}q׃\X%ւ;:uan$݆ãn= {; c'nK71,#nI'roU'ť+ߝ}MiN}'=nĉqJcLpUν)q~̈́(aVm s4~pQ|n7)%w,ՌQc|RkRʶ7qsYMe{Ŷ9x/|SVjm{Kmoot='0ۖmO qoaH~~aߝp, J8f0: . 'RGtå}]J_Ji|hLmMt|:@ot<ڻS;#*@g7X9;ǃcx.@y<  +<`?1n$<=C2+B)Q_3P1zeN8uꠔXFufeTʨϔQ}7*N(ZN*N-ZNd9 Yp :RƬqW=z9 ;9ƻB[7xKJ%k)X,=\9K|R9?;0z*W'q^ˆ{ | o ưߖ,y ~~#_RNvDTƌڏ~/u.x)Xm]\샨/J]&Vޙ%`1-}tmv|g ` ֕ۮH[T@Uz-M]1&K?ZAz$?G ſ¶ilbmMC@j^7䬼QjNӚ>%'t W0W7)$>Xߩ'+=S/Q{rߢT'&&&^uɫmhmIfjepnlޛf{z5ɏ gJo ѽ咯B׎0F;šV7wBmRAA9^wx z\kƺԄ|mdݪ.uwOOҕn:;UpU{>yД48GML7cϘ8НF1&r-ѝ kF9#Mk~ڻܿ|ޏ׆d$(eML7gS7J{7@4d&s?Pоc'9Jzwzދ Ao%dM%o+yo*ڔ*ɖGzB=+ ;J-jk0@W։s&۱NnV&Ͳvd,_?⿪]{wp0EUY=_EzD*#FU#$*0iﮡJa$K)׊(z*|7UZ*k {~Փ1N}Wژi$%#n#;SKZbsF4cc; y8b}yhmbNӯeM+ct7s%wf}k`@$AB.~',u BpׯVtef;y;'>TUL<~ 2wֿhl+T_BcfK;t~C%n|o5=8_ޓ.,w{/Q؆swwQ_rߨz/߂%z`ǁk[2wGі|uS`9+1c^ebMvÑfb9)<ϝI_}X*Nʽp{]g^ 3\qfw\B(e.uP/6ۚ鯐ޑg=cZis68DO ?EӔ?#&?![1GǘQx}~WŪv?1 pP lrW/O ?U8ru)r[GOr~OKT8+ۓ1tK->A)af ܤm‹UƸ?m{-:dWyw?˾+۬*=uWꧼd\;+=8{11Oz-ywG;0UqͼÀ`<)u4<1pܓ1inNa;=`ӎxgZFy?Zͼ<5w> JG[]v䷤WwxWe'i}cBRX# n'w }0'9Oθ2$16 y8)ǼǪmļwڔŦa "Y#>RMעԆudKU>&zkl[XJec3%07=һOq꼵3*#q,.g-|Z5a{Z)w$42HiKGe$k$L^p^nI:O^ʔ ̉-oe^J Te"[K* V5Ffoۣ-Ȧ]mds6!vk֫.b<svc~‘a^RFR&~-kדWo{|uuJO̘c¿իiNql vVVxrPjs-tɋ0sZ{7SIxsE4W .kgIèS~xwL`+e0ZC\|ٵMdy)ekL>\mƏ,[DŻE]{ r܈[X?lD:>[g!b}pJѾ|Rݵ9I0nx. {JhINdKcN4 dl&^mƵdcMiˉl;ch ^ҳo Rʳas䬾WʢOl6Оe߬?p.q_V {$[E{ɞmY7-릴 emi @a {z={lq ;$RmtEjhlE:J;m5M>3`,Ch kv~6`GvY#07p söNixw 2/ugud5A[K#ޢuto, *k$m~Ȝ f_ui'z/V;iX ҖWG{R>KўRΜSuP_9zM#8GԷg܍M}˧{4lZl^}>p6Q}kg3|UD;TVx'5Q_w_OW7 ]Eu85y=_>J8:xcz#XN˜0FcwuG~cN}&ܷ*m SjS.+?8fMF UXo~qWRuTn1Q[{q^G{(!}rYT[~A aPT4T-{W_ ycB305 %ګ)al}Ӹոj*k>DOR?+Tq(O~Sʟ[Sz (n{;o\h7>kRJT CzYxYiJ{u4ens_o3un6StD G6m %J&N ioge~K?Tam7|ʽrܛ)ʽr;-ҎXӀIiM >Şm]ƼlxmPڻ|+r|8Mû]w;Hܲ0#+68&+~#IƠC90+1[7IWlg\tZoN C9ym} ovM,LZ 2z(\Sq$d}*99wx5һͻ\,/'*~ [R1t/'1?W),𷔵SO3[Ї?+9L3#2o?>~{)e)V̝ v^|wüyd5Яw3+{\#.o %֓%k[]V-= yof-{k/ߎu|Gpe=g@q/5Iz,:02g$nx5ۍԗ`牛Eq{{ڟ5}R.b3|O3J1Q_\u\Nl}GvW&i鿏ǬGcv#=y6~cgOw̔OwtOw||7r(l^9"́b1ł|ˌu8;f޶>UkE+[;d^MaCWvtcބ?Gc wi r)m猢&clV6gl>CuID  "Gڥ,.6'is15ژW2 oȇU] h33?USaO:TV3TǻŐ'ԒG-VAX),S;]3CV·~ M"tҿH9L]\DT=v0?w1oGxr֚y7znna~ ։4AZ[̿~'?{v Uν@7tۑG'XyKdwԗ..kY{*/aw,-ѹ nߗ.c%/APE[U/!i,8~;>:%q^̼Pߡy(@%0/A'q t&D 1V(sy JfE}}{&91hi1=ae=~ἴa3o ô p$?V2IS%ƨp8\󉿔3)䪜S{SNwt`%JOt1ʲa*X!Gh|ReV|$_n^p>=t:#f=״JsGRQG{&c-_O; _o.:]tv2^whW,w+2ƺwe#ʴª\W-ClV]2t&ݰY$`>\ Ir4UT;Iqְ I(% ܥɸ,mT.)3 LT;NWEyxza5oF) G|`|e˜,s7{ 8qkr?>zoZTt64KIᄌ^-{h5Ẕ ܛq]\XP.~|s͖n Z.dRc x=a@PݓwY1*9c1|g~۽%<1-wV )n45h쳄Ÿ9’V8e̘ dgl133qS*1)D+NMxcwŶ]J uvǽ NϞ-z%3VR}hqe^2>Y3M b7k[w|>7! p#}j,;7:- n2=dg {N'Aw]x(]%p:.iwYO{q?me?ؾྶV&`[߉`c[-*cMC3 O9xf}3oe깛gm`D\MU)'鬁}p6!y(yVySWq?-/)F'if`{}Ҥ{ c<3:Y 4c i=潓曚NwyAh-zJhi e6P;8Eh#Y\3ҝqcs[8¬_9i<s[?q|JOS$a .aMYfo4wwȬ@z,]%]Sue^UrLap֔鼳׿w8~b3Ca-+q;?k2׹cMy]tN 2 K\  K@x3Wfx D#qNh瘫;Xݔ_;Q̳ns u?]>y;M֞K)sTۇL_fϖJDV27oFHxA?1l6GͷH[RF|uxy-]wP\=OB>so|E̓\e]tg7]q=#,&%ǙY͘'Ԛ i|yOZ#7ry 7͟TA+"\7T`^hYo[^Z'cMyNC?1Oi'3/c²IxyvOA_qp'vݥtZyT`=pſIg_][qcRZڧ?=ta,ʿ$W`چ6]'%UZ2/xHmc^p;鯟_/l@_qږUY##W##se4i. G wĿ|G\yGp}-R oeߚ7ycёӞ_yWio1Sa2Qu25,=V"攥}} HXT<,Eew\m!|ϔ;r.q_~"k alm+c[9cW1tnG_\x&?mn;_/vhg[?=PD|ZdG??yTSU83_OJ~cvg91 gG]7a;g%'f[pmdc5US2 cr_pT2= *\O6x[9$Ek@?8nq͡[޺e:˽M@kZq1[_{>m.Igʙj;?Sy[𿦎^U"sܾh3+>$.SZM'My6~oџ#m~v۴K8s?x>-α[ۀZa_λKK  !\ i Ugc\NJ=eGÿ:c_;)=k@-`&eƝmsǔ@g1mj~XsǴ-:=%^6M`w:=ݿ*g;0? 1 wMZo29Wq1pDr&Ѳ'qY3R_.swϻކc[ݽyc[Y.E1|w0k'vk )A" [v (f\)l D{{߇əCqURO g=_Qҙ-̫qxBu؇_]c#Ւ&Z v>d,ג=WQ9理^z0C~c7*.xH_cF7} ~vЅODrwLq9@pSOMZ7h]ڼ,Gڧ2+Gwu~? GQm?}dև^y_{UZ9*u0V'rvRy</b߲g]e'ae'ؿ8<\\~>Ѕ}HGpK{Υp9C}Cw׭t'/[=4t~hďBirtX(!lM(eIa)9lAOy+8l̩0{_#џe +@qns.׍ -=d,7SΜUWYEz'O<&xL*}OU۫`#+?>^Q:yTgd"?!/eYg:ſ%ޏ`\´Mf9W=2c]bb2*KKc~i.p][(E'*>&O9O/bY0PѲ1e}x4l}'f,iv(O0}S~j܋<+3:)]ݞ[<^~j}A >38eFXoMwai2G>3mg&?c?oG22ο|)@>k|+> i59͵V=7zX9G9P6򋤞9kpˣ'ዔoF0:͉cTHxx&hK{lg!rzIv{%×4xEsԯگ,Τaඓͫ5c]_zƠ1ڧ`du}eʌk'22W2w2:J3(WN36гO$HpU!_;(o<{sNŶtQ&]^g=k\f8uYC~1։i81F+wP;N[e=  /YLt'\ yػc10F{*|1Yy0Go 4o21UIM[$G=Nkو@Oځ3oځ+ηTcvU"+L;~L^Ԋ'XqҚt+kѨ<]ggzII;J+dOw}Xg`1/g܋1S;c\ۭm{ʲx>afRa\8ZZ{-a~~Nܹ?ah~=G+n,_ Z;Nb~a[p|8} ΢p6s1J[Ow;=Mv's G{̅?yCr`ctK{#r?"DqܟMa??| p]z]kgС#.S *1<ړ 't1G5BudI 7q&PBM<36R$y3@6ƫY^i[F? d)a uH0-kc yez &,zg EZ~6~˶8xRq:+pូȟ0G}-$֍R _tiO_VqA;4swѯ: n PZ2:Tyavh'<,wS!s*eGk̿njՇ+WeTY;_*^C)m'4}_ۄ"};}{W? <|3 iu(tQ} &Ta^2WH^/ɻ i^%&i?pGxg>H9%ea #{^dxKx]L x+`֑ow+G̝m dq};+D *P^֦b?8q_c0WGS==6?$8Ե!~A?n+=svsN|{mƃ=xWE 6"ҳ7`82c>Lm}fy~e]; ʚ`RyMXLe)Ou0ҝ]٧eRSaK$}*nOeJ_P/$՘M8޶^ġ+ܙi|$e~1G:W{pr%at8¼ш7iJ{},_-އ5+ ' !yBW,]Ɇ%>AmQ:iQNl_Ҋ$8T^g=YO|y$>XMؿ:v WW1nym%,WW K m̯h4vۂ̋,CNwPs;y>$R8 ~wa9:&(p>)ڢ{'$̑_lf:,XݶO 3' +"2}6"I~bMB YRL4 II6{R-d'%IIfRo$õ~=<-3ቯu=쭾S$yz>xb;յ{ + &']ŧq^~7 wwsF[5" {;} o89 IkI #O䦏N\Y4oawO9B?Sx<><-߫?‘x#I,+9e7FaSqzH7SukI;M"v^y3yj,|sYOwe?<%mM4 L1a6 -'HOcPODG#?]a߾"Zsv)L6}ެ ݑ)YM?}4["ҳEϊ?o \|m>Gϕ}kyOQ8V፪~T ;75ԔoWp)֧4[(\!/,ĹaK_v*Fߝ(?9,&?'*ټY;[=䘓\߅oK9X/'E$"j0m҆Td8cڑׄy;|{x@8c㞑ٌj*qxAx-+ɛhweېI3쟐+~?IeMÙ9hZ}Z=Za?8sIAv3OӤ2e4SIK5U==6] _RVjZI9%zNls69"YKq{:K* P&lM~Hԍ{cԦO'Lx*c|o+c9ocN_T_gkGi;Vils+\JZiLf\ʸ8ާ̸$Ì ~p\RẌKn8p J3xjO0v~Azssy/ijdZOܼ(aYo\K Kk)ݡi#=㥱iͼ.ul̈́Ӛ9ij c Qo5ctfΌ4"oQcoW^(29' QXYou9b9UIhDMjd)[osV3/x㞒S", mc**J.9CxVa^57Xk8d=\.{̫'ﳆ1#_$>yɼz~xf5EkL6 %ѝǙx+KF~ƼO4#>F>gF_;jH MY[|N >/wVa~Ko_I0  bޗ eu8_i tncyKQq!Dxm4qS^& v[WH iX+G1~Eʱ3Dk(u(Z7MEinDѺi(Z7-EQnZ3(MDѺi(Z7mEݢ(=htpu Q^?  Ow Oܹ}#[Ľ['&C>Qr8c_=7 JPqPϕ{}r9-v}+ 3Gd628m+V;B {.,Ex/ wۙ718 wT'f1>YMޤg\gk1>c 6hx~AwOx4鴬4ғ!1^g-NgeVj{e>V6wݲQ7)y}[`Yu1 9lMy_oo R=@E86軟~,<tRq[8Gzb !.BRdO?I]l=(ƌnS{OG;oGǨr%- *88p_CQGÜW{,]~Qt~8Ar8}OiwMJjm)cHOfY,sA#O-4yֆ tSm t}~#P961F}xV9:^^:19L{7q>ȋ)w2p~ 'y&%y%$氞 &KR/w2ocLwܛ,Pm|s5]{&7wx9 *=wbI@VֶImw99] 9i~7'mNëσly8*x ?w޸|O9湚Lɜuؖg>U9 9_BsQ~EQ rQz?Eg\7'Gx N'8빨 p)$ 妰s2(ʠ\n*ڹ)N-sSMsi7979޺dûrLnJ4{xyh*r9yhxqCsyh> S@a -/LCj3Q=CӸ.d=&.>_B]yAx~cیP?n7y#I!TrLI ' 8`E byi>V[>;"s龬ڴ _\!76kڴ6/vXa.}WAh1Mg|Ҷa.k]HGK`:cFc?q/_A Ե .Uk G["mu4Ԗ4e+H>N{h$>~ufVz58@*z wJՌrY{7/JaR~j˥}hû"=kcd{>bp9.vQ U PaC^a;@)ᅩMUab3Ķ9p (?ٲE`6l_bRoGem+|ttîTj}.!8vrW 7,(-|%`'&ȇx'G}ҙ e;?qNps$G*?c#c9pa8$˕NYЁV41?Z;2C1 r {Wz> **7?@uچrU/ ԩą_Ofa~Gcމb,WNmpϧ[4`~\>Pj/ևy)Q%}›μO/a,Q9"#~;iNr%/Ux+^e^+G k?i^} & ^ `uuu$;(3 |U>ʼ>kkGOx:#Eo 0pHs-m6k/ۇ͡ xct9\~}ƟvS꠴$%wf,鹯/?vp`s/bisV{·ElJtxq%W \!E Wq iWws#68ߊfs6a Bl2m ˴!}ҮL: GN.,ݧ`pXoc3Kл܆|m<Đ)J{'K&U^a< XKs+eqr(/Iqn-ie{.郒v_iEŒf/S\aV_#IIs˒A=/<x1hVяsY-O˽l~lY컝^|`倧pm(vtQx/7+67 e;<1ʣ o2'q28gɩ,Ff 7 d|pNрs0n `,%[a n8pc?/%\q¶ mA(N8uA˽{M}L[ Z#:p0#р3?d34ؤz?eh>bG=/`U: xಐW毗}g.qв#cÝ,)(v x.+p\־7d|*oY)Ƣw,KWpo {-O3 L{h/fn\!"pc x +GɾvY%?{[f]'Ucj=6rO3L QXBy?X1 ,Gu j)k}-~9ޝSmP֗fO78ͺ sܤBVs7Ỏc=#8h̑E{3{&1yl8_1FvQ9|u=Q|u e~z)8UޠF5N4̀ouax<ݪ_v5 Sy%!|\|<ý9@.rphMϸ%oZUG-L?޹`~F 4%Y0)F=1r0W"NjS D{F| ̵M.-/qvdCqk9'a~!i̼]ɻ%{±Vrn jO㸟xxeޖV8A~707`yY h:pSI*RYbU4si-&2F=>{O3ViZ ~EUS%]J.swl+p]*l 'x= @w1JE3>=BE`zwj[V4{_Y²+){V4eeۈWѾOq~Hk)%qy5cۛ{,+rfN^: 8=cdKe5p]g4zEsSB9X >XǟM2{n^E:uK?NZ.^]`_kEV rYo{rj}r?%z}GaŰ8W]lYwTCöELaxO%zҝꛂn\V&T.+3!e@R~b,䐗pK6ˇ'/ryۆY0O?e\Sb'u|hy73/#~;  2#qϡ]^c ȑ~TtT!~_bYƔyXOQG/$\q?e_a[xx翿 y6ϣ-˗r؎+sp[,ZO|.G)o*y}8WS/`^Iz0YO%?zƕ ;KTq_G*t?zPUޓT U5ҫ5v&w_hV_y퐓pr8Rruy$x WYO}U(z&~@'J }'?;s;E*|OɜM}:?Ee!,?0qS|`?s ¼ϾiC+,e a0m?¡GԷ=3FUJvqN[<|i+3~Bujn1/UxUz'vmGe{czO~>ѝy4Aу@~e=˾@*a.<#Rއ-WbuBƧO53?\񱞖5q}wiGp}4q2>T3sXniֳ󀿋qJuYcl,79hq YkAmy$3F٠Wcstq>bnEvyKR{m7c˜dۊnL"|Zs7"}9K_-rRiY9ܳ)roygC|9 UI m6a#[vq+ȴdc,kaL]N8љ2'S~+ind33Im*-ϕm2z. ?):D), {1r<[sjm{mZ8^޽]M֦wux:N.W%h>.O_Tٯܥ8·G{NdyJn`=Gp?ϭ7|}X{} ~(ϟѦI863^|O-Q{k]^:b>#uݽyW4,V#-_.OZ>7;cS5{dzfK̑Jc,:GtȹK| =R=As@y V!r Gx)\-S\W7|g]R|(lL] P.,T(U~zFegƢa>$iy|K#9)x2~3ﰎutyƮ1$;acY'r zQGaq̏2YT)|Y)gayOS3IIYp?N 3ed Xtff=7gwQl^Ys9~h+}?w:y{}2s'̇s~k'y)\)o1kU+NkS#8ud$ێ(M\(ĥsN Ǿjzc+p^eO*xol@&<;i+=fzNn5S>RvH9> ӑ>I}]S=DToH}O֧>F}_֧:]IR HwS}=A/<c q"? "ϝ#I4y#gޙ*{egV>Ro:䜈] W\v)⚳wФٝ#;xGy#ﰭ3_x(` | W7'd0.Ol$}g=m!mӼv$}.1$;[,g՜s{+ ]ϸ׏ !a|xOx༌g\p Ƶ¸ךc^EU|~eQ!̫ * kU4\TX5nWҤ·8_.Exf?!x>X9\}3:7pMZs]45zp-7r;ךe\?W( <żOo\Cj;5q~gV)kؿ9v3.}ӻ*}EיOp/p0x=f^k>2y$@]]*~L*\U ޶?|w&|e(|7xcsMf] ?ac~?1AiZۮSsc5"~o'a{21 w2 ܧ!y-c7ٟqP9sۀc3& {\lpym)'y笯2f%S_'4vjeg ߣwO:00?2*ݛ>O8WZR?e?}>%9C`|31OWXl׻5@))|JqES|궊%/8͕ z^9̙,wV Xl-fq~ g\ogMVOM>m F\NMwז8i4hfa3M32V8~H1{ moꈗk xND4,<6'w, 65M#\wk`Zw% Bk3B뭛A6Aݑ zR]f[61pozDJ!Uh+ `+Y]1)mQۊ?~bPUL> V7j &zAP?\;D [UL=py&m?xI _$ym0hDSU ^<׌[Mx 9(s,M;L|7ӽ3TnK*=hi;j Ӫ2w3!rU9g>y|[hKSM@Eܾ]S.:B/ejj|pS-Ƣn4vYK8)fDظ;ypp3ܥݓ5Z8tQqژ@~ɿ=obz蹪;]:^K\m" 26s`Gr_VuƸ6g %>JiFP}aJci·PN9h22ibfT[5ؽ0d>5&> jFdEϯZg gg=`uR<8N cy6 T#UIv/Pg09{[7(yO~s| )-wūx'kl.d]mͻ ;SZx/ Sx8{Sar,WQyNmߵS\S)"=άp$:? u[PGczKƱxhr2c^C՞IH9cqD=[8"sqэ{u.Hk0[TM+Cadf!#p r?gΑ\ձ6"Ys?Ƹp1js?Dј?3k㨓NYg<,wڜQ[G#wVş/(wwa^*y?o0Cm[3ٿғ2ko2mg޷q./?Ղx}T;E=L <2] <,Ϗ-H58O*o[Pgyd`?.dP`arXQfSdJ/~t$y#\ENߏ^ω7F#KO/2dgq~#rŘ7A9r?eu'.0`nat?m҈V潒iL9i$Gy:fLl:@S1o0'?} E"wKl<>jw~r9l?3+K0oCx@{rKm n#GԗX[/BŸض3"V:B-]n[}bj-G4mnf?q}cz?uYxי{yje9EƖDZ2!v2,@vص)` we̵?>uݷAe=WA,y=ن-@qd~/oaMPYeO?-/L 9+ܻN`C?O/}kOace'}mMumOߞMԁ=}{6lٺ۳-቟  OBִwI8xf ͌uYyY1:F@|>#{)w+ԑTGʻ:)ZvѼ,{1ZgRܩ|ˠ?7so*[~dX@:ƶLex|nъy{A^/P4Qt=M:ڙ0lⓞ1i);γ+T[ʭ.&*^2>,WkI;q:3oN$~i~ }d Hkcuf~ 븋-ym7ሏYDgs[ c =y d" }m*t1坍1F)(K pXW3*\p( c|oR1E}r ~~IWϴıvlVW6t~lWgu5 Rf E7e}L0cusЬ"'s0%%/k7'SهSq3һ#{Ӑdi湓<c9`(cg=opv{/8lesUjq).jo,Q)pPo=bn&x͜?aq9gE6ܱl-l7qS;zXvqfڔhL4o T2ed/Jc/7NiWZr|a[?!n|, 8srd?h99{:<{~MST;)*#=;Z3xvnp <nӑz=߬ɟm:=.N N2_I vZR>v[OֳY>KpwIg|p'{O?ndl$?oVʭTsiؓtmIfYHnI}Q5|ܟ*|Hq6)] Qx>pCc ~kb_]/3,UzR[٨fƽᯕN}ko)c-ߢo[ޝd#F|Gw精LmwfdwwwK|G&u/ge39U@O?8 G~s] z80a<~4O²n .qöEF[<ըߐ0i|k_/M!,j8 D=DՌ'|ۜ$~?ۂòVQnՓS`_ؘ^ te'ct=/(ԛ0]%{yLWe5M/ܕqA<7<7[4w;\ߛ 1]uUޡߦu'Wp=HU<ڛ#,;m08;HFp<&x8 Pv3r{vMV͜ ce6(}j 6rj2j߇ڳ}=߇b^j6> m9C]岇20GΐX8֌ =-ׅ}w%:P8R\G^`kֱ}X~+,Ry1>2XM_LcwQn_9sQK٣Y^U.Kk hx~.Ek/˶ zڀ ]0Y6 4/ʌqsee9XrT~FM?c"7NCc`x!88^5ģI$*U}]˨6ae1q+cV!ƬUk)_'C<f"f1d̩sl/bO|_MYJuVe{Ddsc[S]ʤsSx*NAۖpYU<':buyPgMi헩ϧ̝a)_ |;мw|krCM`?h# '}YVMq= +n6(h5bwl?a[2wh֩V:HpXֲe7T:/nj}n@3yv?fF wA4Ft{6l {Dcc9p [p;,s7+aN 2ko]"F;nZ#KbX5`0aJc;X^_'sSbúS}'8 8KaeBMS`̂9U<J iRդG?lʂV3j)w|Ȼ7,Nո֏Zn6E &i8 :0،%feqgȉ2y,["$02~Ÿ#nrx+xZ8z[qTcY(|^s tqm,m\׽[ɮWG*p“ͻf=/zeWC"-|g^OųSDfO]BDzF>қQg:vQtW(\^͊\¥>~_q(Ӏ 1"8${;ļ oW7a<*p\R[/;| ?~!f\G_AVGpk ̛ꈿOWXWEDa9H\º}H-/'齩'/L?_A>UKO^3Gq2̟_v'O)9 A7١;ȡ{?yGV%e%—Y>_lF|ȼŎp8OH{̆uyf}"'ˡB×2Pz(=+TR`>R~J8kuɗ\-[mo >?xG4a,OTE?nYK c/;1Sn]*xjW^d. 3X'D=x;N^V^I=; T:N].MSdgJ*ާQ;ƭ7 X4É_ ʙI9r@rM .[:{䚱?K%`~߁;*ma>Ϸ[gi,wv>G;O¾dTwYوk&ይ=cZ7S0˽f?~&]ZՍa]g]hh*$QtÂQ4_ry6ñp,O?Aǀ(L XBF!_g8`eXy߯]NloD.u+eewWIj+Qa` _^aI#U? {sZ6cM`F{q-cه9eg,{[7G^yRci}[^?|}6υyЮ=ts*Wqf IJ1/3M0՗3uZSo@%0?2'r[n|7:!c'QiA/*< (n` Vca60Kd,w4K?tw}2jĜ{r7/ג9xrW'zj}ۮqJFk)ϟ~8z/gz5! ǙCEz V;A=}pVvUg?.ngI~3֚:/?`X'>1dИF r9#!sN|#h-;y[U7ddG~vpbr3Mo}p(=8+潎.pdqՋ#w|cptw'm`ʡw ̏wv4`W ad4It/ PIyOBm7g퉀n nfEC'^*k\q]<})n+OOҹʛ80]Ns{yȻl]ַT|Ol`Kx?p7ɑ }i0;[\Q'oC U*[qtw?%#OzU+3vԞOVe9c?0Җ!HJp¿,r'fy K}*ga* WPɻQ7ڑ_qWg[o7oX6R:+\Sh)GɫS RO]ݕ{ } ceZX%[pye^Xt9SxNX)GF n ( Q%cqל9Q/@sSa|e]c|- xsN6bs?9A2p;=%neX ޤs _ x#zE^ދ~zQe>ڇVWoh"?TzڋjԗeǼj9&=2cLb}6^8Y1ɒ80~^*r{kMZג61 +7_IYeUe5Vؗ,ia{'Pw4d Յ ;㳅:y![8gqy>- _=!z2?od]SmJ$O?[Ҏd_vic(+J^v=QK|w۶ϮOJvr&g dNy/P'n m2)^z-ӈESd-84?`~6?e&2ϡ ^Іs9#k ~z1/O }wߦw֖|>ߧLَż~§[%')C\?yǼbݐ浘N6!rm_CƟJ\)G%vznՇ% Zx|;:M"5>pSXYon5kKo. vYp&yCx/ s&G]x|ɼm:8EOUDgs,Ïpڡj_JLy_Bl,`cX#Ǚ~\I~Ec?r!s;֛+y!JKS3o\r{g_1X1`樂Q= GOWÙ7;C, yv)o/Hkz 90N4#V2ȶm٦Lm=]gN~?aLG?)ɼY*ݣZ?O06*#DZs?KbYE_]>&wkO!xõ9/26{EF1o^F.[[8N/V(|Ua _iz X%:g0/Sj/8c-Ntod_W lYeV`u*jO:OO>~@2ojWQ&pSiƵHϷ_/zCKRv1o|}R{}ަ_&e /c.Xyiߕtaalo63y sK9,d- 0gO\)='Óף6b$u=?3_xwO~ż~xSN‘̻g.mÚA>|ωrEX~6"yϓt$u/7XȽ)~Fl=?0k#KVq7t:[uʙ֑oבHǪu)i0s7zKjg^d'Xfbk&@ܨ -d>@2LWa(~@y~ T>W6bXwFҝr#εtWHm$7z΃&3~F7-g}XUm"})7odDo"]U7O6gϜnŜ(( xDyuMۿ7Q{xFl2f+3mfof+knW~.v31Y7S_q3;7f흋ͷwlJ>cq~BƱ/y9 /k,1+Q~"V\|O8s[ -]8,CG{c?9gWmבz,FƙJݚGq8@s~qޜĚt:I'))5{(a["i5w'L;1f>>4->yn-|ސ&?_HO5b7kK [1!(;bIgNd9veZY欌5&dsp*O~8*/Qg{5OBK zYD\w:coU f#I-46 CrZܑ}*Gwx)<"$z?QiwTYe?^fUUX]]l.Z+Co RK窳h_(ր'n1h2O㙠݌ `< f=f T5Œ:xV}[X=',')ODnkUM7+he=o.›㙑#*M wob|O kP_+ owrߎzdk|Vқj+o?V89(ߤJ{¸' [EN#w@Vr]rBj;3rQk;tuasY.pme܇$v}|FbkuY.s~H{x'e5 e߀f|OɼpQqʹ iqs2/9qש4<ޫ'i-6Af~r?|gwwѯJ%;HgN4p8n^N K r{i1iNb^+a_vguA։۹9|.TGΜX̎g?Aan΃sٿ]]1 7LqdOa%2;ɿL󪨼|ħ'|c'Cb AJ ^۹r]binZڙfinZڸv߯ion nZ例?~OZ+|o]1e=쎿ǔ=1eaY۳Ǭcn1zKyri^d{)O3<ͻ^{)OoU ? Iˌx:}'7[4ks#{3]~\eﵾoSνfnKO/Nriv{{͚TM>uf{Zou7*Jic*ϩuGd G]\i x~Vӟm@~5g-9`^>ʸgx, WPAaB}3)+7чM?T5y,wx ˌq 㚀$\pFu9h8G·I]npFK#r]r/˺~\8Ͼ̛j^;wǺANߚlZf^Ob:?sN zn:k<_ep!ł|CF&!OE7ϑN^U\$>3Wud\-K ,JEøeZ8YZ4KYn- ?TXl!gH{M Q'9Lr+uXsGu~OީG'ZlEֳ.:҂vP =l#R$/eM]%/6Wf*wr0W#$GM|rR6zz~p^f~#^cG1oݞs/;"cʼS[7me9yg`9>=cޟټ .>F4%+C?󨣦(xe_FYr>3]FwI:;<$GrGiik{Փ:3X?# f/iﱝ[:bTۗ쵽_8l r:lABǼ*7/< M;*'c;w$]~%:CW3~=CW3~59CW3~=C3~&y~_fz-1V6ZQdE g=8co{H!R 8ރ|xݔ2şÐ߭ b8O9x?=l !'i5osm*}77}`<U0 $q4!+¬8f<ܻ!lhy;w2/_mDn4:% Si S '_nNk%p?9ߠ kL#o z p/yGSO;xU̗tayI r<@yx)|`Vt\p ._|vjIvF.u'oO}x0윌tvb >;~ 5֎hW.Wnp;iuG(F,|w}N=w1j4Ϲb1:ڴ>8c蓆֦6X_gG.?16~;^[%xC}꣫WݮR^j=]%,seLkŮa&RkT_gey 2ܘ8Y 7 뻸q !0.gⰑۮ"_12ў*{|5gVNd5xwek ao-ңD}X;Ǟ>^ J{ gl  os@W|ǡq x' .?1s&0:99Rj'sk@m1S,u=q*gýaAz:O2E5 :< ߈/qq=x_4)j}0ິ`#S.8 Mao.AeFUiW-,b[gĎQ +o|p$g;Mz'Q7.Ka\0orI~\Mk Cl*6:qxCuJ|ĆD;mlp^ Q^:*R r(FU VxK^".p|[߱rr@^*q!xV!?bRoD[q8ȶ֜3?ggɟüPI>d'mIṁu<p۬?B[?%'Q?7'SAwiӝ*oeښR1}kO敉cWN1yxl)WWj_ky_>/ o\Tݦ2mJaV\>\O{j*?w G;w-ൠs1zO$r= z1oGz;nӼd]#'AZv5ݓCq'Cg;io {{Zw#7Gu9vG4mt#-_-[Fyň¿* ızuh,<P@:'kppD6HcU]U7V6aK/ 0pWyTxqyl.n}ȼ81 OZ~Cck]b3:{^x9vw̫Dw ۘl9~euZey.?zYz<>i ~i !%p v[ ctOh3h@+)V{g0}bA#:q< _=G0YL|nYgo= %Kl%˚)-Q?oF.qZUqu ܂qM܋qm8&s@罌a6n߸OCq io8sE)Pqzvp 1ԿB~'p:@}~0.3]'{AT]}1/S}Z½%1J Jp̯͉+)'}`QnՌQ78.;*+*Gl_ Cj65\5,mX)Z(Q޺^- q-~J7 !]Ƹ#a3!ީZ: ?wVq+^|(/C{<$[ :h`&x#,+Qؒ'ܣ\19%osuȍ oT|'SDp[ދEY[ yУO?0֥fy4Q&8t$]ڈ'V9~ A}SU9J~p.TǓwNT?:qܺyqy'p9rq wq {_ ~dާ%{yc\yq} [VDY\6/a>w0#a#ne|8`'Xq9X'W?d=վr@q.'uwXs_S>Y֚'a{>(}n/ߜ>q9I/ͩtʣvLҗ^W< x# u:^Wĭ yִǝ>yIJSN&ח7̞_|L6'Er?=^z3k n]œ߿=?Ȝ iq寰̓j:d{B OHgOHo'O(+K~zBs_Sҟ)/Լs(76xa[0&|$,„5?]nW*=u#k2$YG #e]3q@U_) }C/ O"6l΄ɧjws8nefTbOJ{ʺ2+]Y_A8tdS:Б|Жb}>_YuU({W_L%SЇcc+Ǹ[‡ݽ:}(GXJWxeL%S6[-iDexX1C-Vx6݋l%%[GL%Ӕq5%_݇͊| SD׬WG|2zG*9)>[tP2&H؇mcʠ%lv  6up>le}J&-VOaFG978blµnp_;w<s*n7ԙנM.bJ􉌯6sqN CMĸ )v^~lxJ!oC5@OrC.Wo74Kb7e)eDg'W(+}-QX<<,u2HVZB.4a|F}6|qC0.SK^$2iJpDͪYy[ߣvJ (D?$3N;븧t܏GPx# C>`'JiY{PYg{fK>[ +nN O^o+(?`D-iSk[~{u?SN[wZGʫd+~%*sNu.7:(g)TX!z~ ?Vpw).ŖBQy'j?AmY>q]{|> WMb8wEs1*{ǂZ=-Ț QeUR/bY]<.y:u^S$_QN%/qwMG~Y>]N̐k*~zw~)hw_1޵:' P‰]J PSr\A1.c8^|;\ɯ]n%@z++O܄?ٳ4l?ˀP ۤak&+8ad6#HLLs;}Tҗ,S(~LJ6@-;%?lԞ/zb|vIE]zҞ$GwF EirHc_3#M{=;'<[KU6w9-GX~,ȧLep!(½ʻuA~X^]xx23o'ɋyNr'n?Z}f8QԺ},%>Vs0v>ψԏ~kH}vQ6U`~3OX;;g#,ao3һ~bJxҧ-g}l_=ܿUvS,wՏpr<[웄nƹ͘_ ~ȼ_ayaKWcoX>ȏPX#ʸh&-{°]=.w/b' ?_c#,O.U6w_|`z}-,^]Y Qؗ.Ncy7?TؗƟWmX>ȏ! !mˇ,R8i)\Zt es, p|~-[Ϗ/O|:_%|F3i*qC^u2 |w ق _y'z$b/]>™I,_d4=8[/E2g,_D|wcgdRx|%[gPeSyeA̗>ļ)3ye-Wx?3 =( 2 MmEXqYhu^U}%zK(H/"R J/"MzZREXBQQl ͜;w7{>O8/r\S+,-4 80yk3~B[jB?U8?WFyRk HhcA\c^[+.x'z!/r.ߋyHʻ2q ǃ::db {y(vD]_w"w-+q/RԸSup\`۱ pRQ1Nw},Y^Vsn,xC,&62L;VUg{ k٣FޜζHlcyuoc!>[ߋ.93o+WْZGZ<x5SZfuZKsZ}mwN,xo7I*͟S ,a8ˋQ&Xm-wyOCy";Ź4Ժ48sraLxnsnx_'!EgJ"/y;΀?IVy6<\g6T竲>G׭rCz'ZjqoH_8:PNeV6o޴ڈiK xN~`J8}ٛ*S ӝ<8ɫOP{:?2;ɸ/s0g\щs`,_6cz=V|,%n/adJ*w`SsY9*FJo[g*7&*rMg(ip%7t˸1v%M .i䵒&O(K/S ~Us55UVʽrx(F V+ 38B WR*MD0`:\'}[(_B{6u (|q E /0D^ tGhy╲xgv> ~Wpve,wWa,b2g^EVx c\ǟ2c'qBV ^Ӿc~uegCCr}=^hg/*?RxaS6u)NITq9R ULo?Ϳ3KEOO<ץbI{dJVo=y Wp]U΅U}. $łl鴩H}t$]N4Xީ]L'mg7 /HnH]3x47r$FUt܋&G>x陵Zv&/aKXX.ĵN~ַ}ɌX’QC=2oP}?yC _XrbWYl^'8 > E.KeYt<nbW oLn̛nqW6ۊ9< 1z^dk4[΢dǵ2Mg:]Ƶ`Id)qgh87ΗfxU'6]3.|^ܓ| ]\Ng_j:S+v:EyCYr3G/̗1K]x]9 ;vSJ#U\Ne[ʳ8I_3V00yq$[XWR* w6fk޵O5US u4"w޾g;H+ U~9š{1f|աp;S>,?zUx*ԯ9VkY?st\$<»pQc<dG[{UҮ b1J8PʛJx-a>P9Y#l+RUw\̞|HO$)*1$szLbIBS_GqǗb Ƽ\Å%)Ţ&[yM^'xM:$PڨV:)-(|LU#}h;VU3)^jeq^-p%3C^]ں'6d_:Yֆa'X5se]\ ~9;J|DZma[<> N߀gT|ϦҷH#NlWKo0yN_N6K- gO=_f~t0c:_0|}? 뽌K-KR8}yҴCضU&qSv~gg,N׌^QGw~ i#jDF2$*Z#p?Ώ|&N^N䰼&\γ;طegĹ1_(-gB wĽ7Y۹S%3ʃ{&CcW iwzvau8E0szֶw0/bO'Qٓ왥={=8cs4j> ^;4/!Pun*}H;R쓋ӪX'C?r^3X8?8OMO aټhNjae jEʥol ~-S>ΜIٗM|fgFxٜZz-?ɡݗ:v1u 1Rrzy+UW ;jQ=O= +c "6`$6b4&nBvtسdחViŶ;ʶ*}TlUj!#-{PdVض=/'؃sR=8r˪MMwM|5dNIg<7HmwteR☤efm:9Ag ϯl jz=G P8gl 2G.ǰLSvĦߏ)뒒gi˺D{ܻBX35Lx&{e"lm_ 3/{7Qjq޿u~K-?:t=YL]7>}™|J={o3р{Q#r%ɖp^`]խp{*2ZW#_:q#m X;6se9Fi,⛿ߞE 7g`8!,oOg4.0=d;c96=,35%s_Zq!.2O=/I!B܀Ǧjn8_C3c ]]\]mgؽ) NRuj4M!}qrgr6uᾬJe8<9]bKo6upǫp'҆I;$q?邏?s6J G?ϛg`)Q W8^pg;)p=*\G ?p#*@ WQ9ΡpvS gSi Ϊܳ(p>*G 2m2nl:?DPT-o*|GI UQm7)Q WxަV(Y /PxK^y?(k½|FI[Y?R(sfҸEP1z.KȾz}Ǿ E:CUsPS?P[iÉzp0 RX*L7e3󯫴 ė7W ֙e=Iod=?)?2wbm̝d޴~Kΰ|Ɩ'? c{g=>cOW=lb՛E $2hmk-N_X.S:$r7%&Kj,m|=ڸ R֧ڲ\qܱ:Iޫ  K eP[r.Sm> ?({B\ J3#\3#ޭd?S43R8^˝]{B勖|¼;p5+쥕/X\|wG~p˥,W PxسF Yɶ[|'AW R/0Ns=^I̯ 5{h|ה9 b:ia~=L|>glݪ@uK maThaĴ0z0z0z0zn0yenp/'v;o;oIzc^$v>T:0};DxupjI|F1ylTvd\&6`~7=hz>:c~T9ڟȼA>7O{9iĎ<''{|{Z'w R~b/Ulc1Z?Lv CZi '|?ٻ*{Ueje{(Yۦӽ妀͌8Υm{>7=H]ߎ3E+uD s'5X~V𤎣ُajg7߁v&*r=bαFFXZg3yD*|NOx)SMV Qx»M)h{ra]x䟖f R*/X~wr{(_}^O~ub|_(6d ^USMpSK(:.; rg;%}_v=v仩;^G;w5;N܋{c>%?Gn)ҷ Ǔ}2K\EN9_C3Ƙ#7i]kȷaϲ}'zn 9Me:m{6^;^zWӳp߃蝏y}t Dka*Nw5N{|a\:|,{׺0޿胶/_ۻԖQh8 eoûw'u<Ƕ3AvLM̿Z_+t6npo]\!̳$}ki)|||rn&SþΑd2'#"=2A I`8ȶMn7{]ZA'FF8?'lm\'PXpܻ̻xhpo@B_vRx_~a>(UvE/D@@^|,$+3>:Ń\k%OvK}1 5?Oa75/й [X.!H}6"kXYthpOc>i,yQEXm"^_s1'9)|#sB{ĩރ$py`J w}U5L?*%u3xBfĆKhȿQ& _xa[L`:2N`J/'}Y9e q|UߎQl\f0xc<pc}Y 8ǘU>CLH\qQrCxa^[p1fX'g/Od׭cAX@#fInfX[82/wwuW5_aB, &|$9U(M>2㢔OuY/`&G6qأ]z|^%o/PF?fwepYxW>#27\K´Mur */ F~( &̛UۢʿC8Lit6ulS:!ߙJ,ֱS@ulC-u%p˺>k*4RlRy}z+0%pcUH_9>vc[z|y:e4<KbnXM%|OL,pGt*הGVt.UYQX:Uh7p} ߰]r߫~w+IܭF2eꀷ2c}Mc|‘vHqmsxxg%!?LW3^~KK^c9Ys &忴. 3~ G< hӶJs ^4֘K][cXo|9t7#y?̢&7{/wضcq-mZL~`Ҿ;`H:#@:fV_l۸%7=3! 0zi 9}K>ĶGx=Gև0/cl๔o_ߜPh̰|Nhӻ,Y=eiю?Ƈ.w\ֿX\Jye.`57_D^x!` 5{\ r)r:{n;N_ =cp l- ̐H&MG\d" :rRonx" ?p!Yulbcߋ6^jkA7C*2}% ~+7yOᭂgg3)n)ge'Jۓ9N)x~_Bƻ r)\Îƹ`#-WײQc*YWe(7xoݺ@NrOMgNb~e7Kx!k??,?pE0wd͕{ l}݆Pʙ}Yk 9v*a*/C⬸ o=Wᆩ;?~ w?;Z _H23Xi6 Gd~d[am8{;<~ѡƶCڊt)_pwsJ]%~UN?dD[Ɵ63_޾c-^7 a})hmSEXRH+6DPZwLEiǼ98;{ei̛i$&e7*{y%yI0/ͼ*L_yU{,71̟m>Я+Du?1㆛}i1^RοMq],,םB;}j68>K]}V313 ߰TGw[xb'Vg8NC{RoŚ.8fP~j2Y:A?OHؙvzuvYd~Y[{ ⳃ}gK]o4  c_])?wHJ}V]WrqDK,W TxȼֿyGyZ i#d?Qc;d|$ļwq22XHdw?J)BwS}z I?Gx2i0*v;rxy_+uc#ss9QBlIi.IcSDRĔzyȗ̾5_ ~4hs 8ϓ/f?-8:$+Om_k6sO9N·C6{ڏ9ڬa].궥o q$EOpRQJsv¿qTg~.*Ζ%Yֶ|qI&J{p1:w qrokk.w~xy1UPŸ*Ÿ(|(XTpN6cxGuxG]Ƹ}%wqusio$Mr%Z?*ya%d^}O?AH~ ҞT֞ЏedC_w4 OW{#H9prT8әg8mXy'Y_辬w_!m1#~f5O蝹?RuOɷ7K\*u=t-)}S! ch&=r[mϥcu^?n7!o<݂k cc?L_dVbn2+92:߃?_؊g+Lx qy_&~+\KVyE^(_ M_-˗K/i,SҲE (w?xbyxò҅s4 {<`R'{ G:8S{h/ڷI4F\3j_Cf$㞱X<<A2s&zq Gl[Z_ഌC"}g|ϡo^`aR[U10FcO>M])hUc^߃;l9l´FQK]$fn 3qn{by=i=lXG0mLk8퀃?ڔxC'L+Lq.R7yW':]{#p4^oA>[/bl>pSǛ5;@ZfJt@vxLgH'{rxlseC8u-~nU9fJGkYoo-7;7e;Z&-[2St上yY.q|O=+|j= Jg{굾n*햲c[T"gsOŋQ9D8TE)oeN]>ΐ. Zz}?Mc$_V8x~W& [7ެqw3l_IZ9s@.wL16xZFY9a=7Vxa'g$˶98Gmn}¶~'=sOKrgH^wuWʲ䟸Q>4@}/?@=J`%r<7U2VOޟ<rSC(Pᘦ/*ܱ({<&J;"eGYxG&xlD^¯)V| t69tc$<gcnD^ֿ$|d׼ D?7]R?J؛*{!_l:ļVE UzxŬVDJ)Hi^n"kN1~4i;>'gDLHcEyD$ aYcY_隷mwضl[Id[Id[Id+A9; 2O%<<;ɍVIV$ k9;88{VddS~N&-&fMpy7K8d;!'bG8;9~͒=ή8{ c cۓ팧ྂ| a 2d5*Ud.DC}3܌!d5N=Qy5;d\"aW=vN6^Ws=Fr/Qee=U?2uS_;z>\{Cng8nӘ%/YnZJx=Y }WaݸK{HF2>H#ӷc /3Y2ˇkOo|k}ٙjH/\ppK+c<±]꿐/zbx \fGfn_y q cE^ǖ`µ#mG+ e%c,JLGY?G'ϖ}<{/d/:<+~LOw׊~\'tZr,ZZ׈wZ[yRf%S“QYX?V!zEN)|=|!vH]jzmizH|6MXϫϨ-岄?sYu lg^wyYˡ.v~\@/Vzߙx*B,ג- #0SAt.Iu~19>JU+rmV8^/FLhiw0&%6Zq1y/OKx3.-\R+ Q’g(R 煸ߟfa Ƹv;}[X xAg ׍7cw2{dY D|ծ|~X3jO e^#eWX;WYj?qʜ{*}m|DžW[lf<7 ?O2B] 5?N[y+P+`a,(NN*kGAionHZ¼۬Quǽ(?gcE{SzM;:ʾoonVY_UE.Lig3~3)_VY}Sg0<7(ijpxHZVK"}=3(ۑ۩ }<כz@ 㾸5*v*|J,VJٶSi,u"7:^NO8i5jw?K^w}G9xQDAm= U?{VPOcyIFG8?BUyc0/ɚo O {MJd=+zDiߞIYj8ddk|= 897,7JFvztyS߲4!㈻̟ Y@dDb̟m`i&rUYnSKݲ}2>z櫴uDx,"ȎeVXdbZ- R%3Mgr+-&I̳*k|l`js0]46A =mW:F3R~il<({Qj?08e6{&bSX T\=v;w+ݚ9TΓkf C̡IsМ 3ɞ9S4pyeA֕ZuWshܟMIkV?nײ%OL7? ?t.Qx |uŹQܝs)?Υ4c\{ϝKVϥb;{ss)]n=s=gy1Sx_ҝ߷HD֑:.Qу1Ł?xwfZd2G[ʿQy)xn>oJ̿l/bG_(Joeg9Am_wd7,?߁߬X6 Ϧx!()Bg,vQ{}TE17H6wkMmys1=w/lB(0(ij?T ŵ6TƲ q;\NaIϔlgF=վi1{0NKxtF?p3,tf/?&V%n`9WT.{e#s2YZaǡ0yjގk;:\|w W ju_M}w${QhOG s)f>'*.%+u$SF X"1qK:QOUMQ'Q~(y"vwVT3~ۧn>s§1k iW+)}jVS`.e`ՅyVhUo >,G`E 7t]e2 js]F_32?_`JVg|:&z\oL3Vj2Z蝯~G.:+jn?/dc &Dηe}oC8ڤR7WWv\FH\OqƱ>Q |UۯQhόk/< G3 *kLp3™|O8KXC^yHخŌsaVq496R)^wqю[!7><`3 ZasxGu<»֍?ݷn!7-:nz^Ρo;W="w2Q9δ1-R`q̙]2a,n lH;58V]] І a}|;*N>$?6r1ҹaaYw)9EhmnnF F6Rx}#H{1JA3{k{(fwT~PBz} -dc4GU㈲;P0o1w Za> xm4R#^S}J>u~ѬUe)?kU5vi^ Yttf-WC3p(`kٌm p{ʝ?T9r U"p'8 N4gmV88Ρwqǎd\oxҎ _emouqycS.>Cg6nqn y,7QD;/\pN>5vq/8}g _QB~wig m(lGX3B7xY:eeY^&׻˻]98as=sQY?~3/҆yJ/>QFP&:2?q+y oP y n=r+G֜ޠ-k5eOqOx/XdX04')B?9wkGhײ\9+̅l\0B*PΗ̏*l6]tL9<a 7Le5mU t3j)0axMzHGU~ǀ|{P~g5 b=7yOo9 Ǿ syMAk%<[i Sl+ajo1L0mê4ط᛭.VZ̹~ho[mm6:mm[h~x}Bv:߰v?vwS;w;;;ܼ;<;L,?QEYOI;(۱JowP=aRE6N-dwlt>ל$y|/$%aye%;5ye,{e>mezګtګrz0WS{50WS{u0WR*BUjuBqBq]Bq=BqBq. )cNUjyt:B!] X#JoPk;ÏG;(|Qҽ(N9j:u8FJ#]O#]]]KHv1nY=?`nW8q'ݏ:'2IIf$33d$mr 'Npڜ N$g J'(mone]XO+IU$jzd{$]SOR;IyxC۵ϓ;ۻ~ ʺKwI$3Yns:Ez+"mO#OKON_rkT_=M54&i3T_K 3T_瞡 F9Kqtܳg>K2[Y=dswPɶ:GΑ4cCK ,7{0-c]T/0fqͣe8EM)27s 3qf̷ֳ~zsYkֳoo9> V}jow&k.;Y>-/<+pS=;Hځ<>'ޕt`Ю5+&0bq"`~In0NGF8.ܲd7W|IF +i5[$d.4OaֆY_-ݾD!0ote^eYntn` fAL1k~AOO1봯׵./}]o}x^;^Bmr-Yqۘ~rWf' k|Cx q4!3)ԧ}RÝ{O[ߪ}9²UP:66ms#L7cߘ}t WoOJ}T RONmߴK#޾iFҌ}04FTѸNnt}c:5.'}E9nXqBnz#mm-7N{V9F6߲;-X>4t* ,oU?XB~_L.>XzknM/k6c]2c򵜝G9>y,o5et!~(q\DyVpyT\R_&/&:w~vX0MfYۍ4JC}Am sne `2S-uߑAr<'yTG Ew׌ƧEge8cؿzuGmM G w@OnF= x<G~g~%y 1ZSomA)u4NyO.Ta#L5~Gd3q>uf-sqJcF4ui;950]]}:G$Ia|5Gf~窫Zwu>Av0{L:ɒגwM̒>6 諻R݈ˤo-Jeo|~F] /o*mhY918ՙf߀Of_* EiEٓ=\ЏȾ݆gC`ydi; <3'oBLe\o"N9csSw&?9yRCėA qϊ18Mm~`zd'G_ ^9s8] A1{)ctnE~Y!^Gax&B|s%O|+ñ߯>anGM{vp|w4a,9Ғu%^' a9=E.V2h̸͙߫mfK[e|^Qǐ]6D kPr|?nM T؇?݅^fưZvrOr0M"?\ CdfCx}A;t>âkwAn<Շq3֧w(¨'ȷ\E+q[q_F"SG 0i:cFw}0}^f7 9M)4n&0J`k c(tUzwzo?erY?%{T-!/ =!VMqIfo1aWEx_ UCsy9Ҳ͸V(q8秆0~'v6Քjg r-ϡv}Nog9˘vfH7]Ka7.3rI[4*|S1o-v &Dyg% |l8khVwYoN3똞ӏ Or w;^W gYVj^[>q>'%h!+c A.3^pw3W !Ed \Fl7Hpr%'%轂#h?_dVobuKڗExUVDmSLm[1C3y__r:=&te&_n*6Y8[!|G촎jVt+}LЈ:xKKi. ^oDK"s4b,1L~ WKxS u*-i3fOyǖ}|aNAg9y 711]~,yt!@T t9?PiJ<_wC ޫ܃F 7Oo#p[8(o)o7iC'5u! Ivޕn^-L}}&"X'}dRk=K .:H)[+$9wDo\2RW<_v., R'Uv ce8])E2 AC+3v(3A2Rt=wxc{7x mIۣV{4ggg4:8[D'=NF1ni\3vMyc=|]r=ݑCR?C`Â7wqÁsѵO{$1MF~&J~Gw4}ΌJ,4}Nb{A7ZmtksWc^l;==G>?4G-Pvo_Y^<=Rq *w]pYs{UfUQY\Em&U+VV/t 7'wyeY<]_og& }NM{kte#Ǭ>/3A& Z$w"q%'Mϓk mz :3}/ j=d')쓹 6Yzj}'HGZ6A9<ҡӃNbmSmz]3Dz?^|_8`~U*Ub/;H[c כrzx!]"?Zآe!yrYmS2}hGUpC1@W=0EgK=uj_z" 6uR֍r >ؔ~o ®W'"Qd@W]}'.qZq>IAGAoϼuS/wB+f,|}_VKtc=~6ymg1zw=]W{ۑ(ǘ}L7ag|Hٕ̅6g/JG?1𗝥q}*)Ao.Obv= ^vΖ7/hGDl,zx!~(:ܲmRP$p+z3{R71=w)9Da6]m_zl1-pwUJ ˣzl팍 mbqb~ךn9? ]șrz\HͿw/%Z9giaT9z7/9Gu稽u9547u M=]CsSjhn`=Ѭ@/G֙wqui~03O_.i}~}9'hq+=aZߕ#=_\PSV|Z编Z>wo=O|bLP:.}#;x滁qc8inev<㟂ir>Cy޿#Y6ñ4K^ԯ.~~b?bA=Ux?RO{j=ݳ Os/h_i{7 <|l E/C«^7@eoP>˿CTcN8c E8s7(6xh = sO˄;x3xVm3'/U ;bF1nj1B y3rOD`_MGbY/p_q1Hf2I$!'0$srIHeA",&\.?SRcxE?&G=yn{ |7p#6]j}EwSL0MHp=0a]\vY|wb.߇KD9W@c_IOۿeZeXg+R@27rm\w0̲Gk͏$F]`_\XM>4K]4` {:6#]}d*kN8M})Gg\Q˷ҩ>qlB UM4|2Eb !{껫15hL;CsKwp9׫NUƞ'CUvkƵXmqqWl9sф?ȋ>C wLߺ[/ǹ@ԗ_uhjâ;.S uN7y'U0\%!Җ;Qlfo!lpux4~rpa-]^jI\kߌtօ񧚨CgZ?h{<08s0E[9F_-Wt}Vx̟DGSD/1>𛙖==iKyws|s%;\?1'*]s^zS)~'mCwwfqoY:A9m˘0胱x<\|>n"];c*L>byi5}6ơٰv@=Ռܘ_{[3cȖx&-@|{?rc(#x\s%^cmFpMW}/峧y1[og+Nf >%e^~}*#,͇d%ʹ[8BCb->w~8y}%kl'Ν[c}X*T8*C^[8GrY`&-v :Rqའ q mioq_Ǒ"I-Iyv9=_X ͢_g5l5S1ugPl]*%;Қ"g/hG~% qgzm_Rh\SwHnz]tחrwkvVFJ.h:_U^tn*o{`_{~"N%5n,VtGESt _]֣Ʈ[o̍oR.H܈wAQIщ͉;lo=79xnve+ƙL4d,m?~聊Ŵ|KXYRxJ{¸mń649CͩМVj[g&Pߺ(Ʀ1 46I'Kg"M#ilz Ʀ546IɓDcSvMUI46H>_l'~JOk!>) 5t|M=?E_hI?YkǏQu}Xl" %[CWxރ4oeYg|USoDGA~ T tr Ӟ[=Qn/ v1'Eoߦ[ϸ qw2.!,~oA{,-)v%$7uKJnGs|>ڗq:֖"g pYeu=%wSYg3 zv^$O`('$t>qJ#2xiU{o*/_=jw'oB/xB}٭n}*Q4jog (L_ tQ݀0(rNq)t锇~sk:`̳yN|'+M,v5P{}B˂XuǬ}1Q/ů/}z __E5㎄ b?JOd{8/d1C/(Oa/J_|%;cۓvYO00/1?NAQJmF'œt?ZՐ_$ԟU+zŷ5)hZ=ʞNGBC9EBۊ:|+dL?Ce91m2_*O >9gq0_B $| Ĕ s[Uӽ X ¥QE\Z1%2a;O@2|=䵙A\+< &#f7xtKMC0T[)G84a[u1AT4t^nuߛ䯇5= Gޚu8?&z1|} 4Ƭ:Wn :KN3%|6zAF |UݽYM`|c0L2Mf-3MiLe61Ӵ2m`7ϊ ۫ ~$'3M"&*">RS`;U;(r& :s1 Fqj|bg˹~m}8җtpLx-jVf5LXۅU7e ˷u2 vݏ˺sȔw/5kn er_ŁzSҹ&w ;mٍv{ shbLjj5JC 3ԗC}pWC\WERE[לik`JKr=zX3p7OM"^S Y9:C*o.rri_*\9&dN%ssI\̳y17Ox)~GqO:Q~ߺOm~ߚ~Z?ϵuO]?i+*Rh+'~Wh<*yT7:~;c/''|O}7OOw2\]O~9y+82cqHFF;QT#Ϩv3y0Rlc DOZ]6OAt- S]@<2O]*߂jK+ -m*jK' -EE!Y\HiYHi^\HiS;3 9 ==^hK*"LraSYDz"*5ETޛET޹ETE7\DUD=ZD}'Nz1NWz2^^su1Ŕ')OU[b!~ظfP+K^%si ,!@9tV?= +]E%K]$\ྡ܍n AnQ>w)+Ʀ\ׇgy 74gTLH;j;1gFyxoWtC}&14-xQ@)1s%fjoVm W }ƻV9չ*r,>?㼅< rS2dV)UH[E'v z@u ]2r9~c] `-5~2UFyW|YU@Qfe4 -2O22.3󊾃BޝFx _C23$坸 ^@=a w6Yiroe9@ m* -ޝ˗r$:x.DʍzދyK`EWwms/i<29u.糲Ti唗rd7-'iP9MnPn$s>\2_dX^a?wync.ދ'@m+=ЇfC')'߈إ֯N_/3f$ɿV76}>rܘ!W?dܢ|0@ι^:Y)=Î(#'lϛP;0;_0_6Q{ޯwڋkfKO{ҁ?UUyS[;۷c'cd|M)ڲ"vd,|X/O=kCy߫);Q|y)V _+r+zwPtT3* ޮߘsADC zW ~asSt}F}_̗\zl$"g*syX?->p\r}f#rk7Y~Z@~S`;jڲt8ݯi/i'ѕ$/Cܞuh]I*jͺ%_%r~!_*y,W:Zc}Geh{vqoCfp|r`Nk]׆_'*X2tޑiJ,s^^~н{WwkdoLI%ݹ g]S4Strv&"3aܖ9>J:/yh9?:ΪϽL%fnfncki $%)E@E:k{C<9M|QҏT\ē7JD&EnE#b2ǤJ\f,zDQbe}V |(WFEX߬#i1ґ(]1-KpK(\\ QE.dʥݯ{e2:3,gpuպoޒyp 8|1,WQ|-?kbf 5uW[VEz5c =ع9hgrO\xMd('Ei |XBM6rW!}}F,N9u(ݗ e}˺.+폾{+>˲d-\Zxv;͸7 ]u2;7iU?~]7yzP /aXGƍǕu;#L޳!PŷAo_0 k§1ljYݹ^knk}/K-PyPIlx~1V/w)XnKb-X+ݟz%)eKt5a%~}-]7Q٭ѹ6Ld"O,I$,;Y٭/,bۭYTe}Xts1ris(i"ɗ:HVu%WjO׬CJw"Kwrb-`hL㄃3^. [em1 Y4륷`A(?3nuIxnUҗoE:X_Tvţr6M\gcA8nUOX; ?1@ݢ<S;ԝ,bO9zko9Dq״|]_Wao4Qv'2z~Uy=X1:c(y;axU^EovZ6։i>]t7Q?ߝ2z6q90O:|W| :ЏRXcv?oי&?o/˘вfF"ƨ'YYsƛ u:7Ac*gBe)Kv9mw$F|tg\-M^] (L˚=~d4Rt?f[8.mYcښ6qLYX_r26.Gɜ3bu^cYYNo jkgp[SDvvYec ʟ ښr$k'EBo>_݃>&b#l|sa˛6۶.=o@:?9S5{uG9p3sYzW]Qz_}ooG+ 8݊gI%)UO( 5ĭW|WcK:3nmښ蔵_3-p(H.9~rr;=e'D~cu/K>h+cڋz F *'.30#_(G8^TQzG˝7#7x+xdĞX70݋;1o/&;K91o*q"|I's>x3OX~C+Ѻĕ4 kJD.k<ř7&< U_A ։,Tyf)>,2ǿ2H /u2 gnfrG<;yQ "_W6W6oM7_:qnߥFg!3oޏo.ܘL|P'X^ _VF}>O{V T1U_*&xǪsƭC`]|aq_VyZ|.}eKgS2=cqϕNJ$G'U8|X UxUM8Vo$h/RaT-v &~~b|զ1K="8ߪz3Ok:)AMS'i5MXi*^54[oWY>p늹YۑyΜq£YgJM|87xymLnQ3{pv0sÚvɮszfGBqqY?Zڢ5:Q8Nxh_McO>e&3io{k/;̄ƐAe !N]}o'} R9*{S3mw0^߿t!k?%a,Ȉso?9] pGqg_E6P-~ߣ2C?ۗ%TsS?6 5eڸQKS=VnvK ayf̓ӑ[K_LT[UMX5:˪:mk-[ԕ]Vfn0j^p񧧰#8j~]D….)ʿ8uZE_KP+(}-wNT{來(pz1ol ^ƱppvԾk|Il5:Ljk5Ǐi ߶7{.w{1>Q>N c!)m̘՞wS~}bNwաo'9 SPƞo7:%R~l wCLT`W];ڇ0MK#Z04x+hnsov-x~\ߌQ50e'1?cƨ c_9CsYg=]Üi :qò x%p;W~oq$Uf]\N%Γb<y&* ?n`8/&dp0r[QJ{88r:7~g潻z*\ wP=uC{=xucn`ÌqƸF/N374'`A 7Ŷ-g}SmY., B?A@ŝ/ٝi팿Qӆw߶-SV#{框d`}3=D& 2j;g)[$rj(3/ȡW»^<+g=sI?:_\Sqz ē߈,d=αәC마$vf%*%OaB\hhbAce*,/!vƿ_=xEַJLSKYϼO5O7U#!3$0r2N_ݷ^wOAWԝa5 ȴc&r*I/(ַOx93N]|%w4mcs}c`#}_T6y̲[+Kp5& Ϧm+Ua?n,k0No|p}!Z2ilEC8 \8iugqgKcʋ{X;UXixi0X dc=TP>hy|߄xqJML{]TMLݒ1lgk625WSq8IV9f+1ԧMhӔKҘM#|Ź*ؔ$hS~3z߫6%2ٔ;|˂i߶`鹥_ܤ3^Seep3tZV<Wps'H5s*7{1>]ߟ;|z^/)i.e&Qjﲊ1?K-OzA0t0vlr}ۘD\VRp/m?= ڿ}vKr%,_wư\6>'nK$uK rw |f޻B1\YxsK79(d Z4b_vRz(=㔞J"8!zp@R7IJ ;Sנg;8M{r\sf!N:񍲨1-]_C)Yw\i0۱|qGv-}w0dӏrAnĶSIGq#(~]+u5> 'A+ӀlzSUk(܆QV_|4;^ߏf*,%/Ƕ,_KŃ=o7>SgW]? 2w1qU߷9ka! ߔCZY`ʭG,Tf|>ae w)|/kQx+} rouEK ,ko{kjlld^Y-1 u56E̹:όRi ژ96|1sx]g4o; Q禲T6_DZw p6mc n[8$~Kz²ZBm"zrgz P{QOL(~&xD=0W\/~Cz{eMz8ƒq`xMg[rѬgpR'c&FS|;>{Rbh#5;8>lcx;y sLϟiYnîfg< 0]b=ƓB^rT3^ʂ]-߿W?kG~4Eirc+>J1ocRI%uz>t{uA} :W,7ߡ0^9y  3q0Q|˞,$ )|Z_~'zaߤ垶g5'S[3[_KgIm0!G{E@ n 1OtfL ә~pnmLg30q&[[̭m᝗ٜy1I鹭QcCZ~5яD??E?7p&-Lg.4b;2ƄMnit7{80=Ìq KV&=3`CTq&w`5` #s`| =/r^ك|C׷{P9|݃JٓbY'!iΰ&$~d}ٓ#FWzRO<1\;r\^qk0<1 ϏEݱ,ZDZ3)#{2r)#h_O2r $.h{E1 ,,/˛'x݋=7M^7{{SwM>7{SM;B{dfoU/I-ޮ[M7呠>ה}ȯ}"2фۧ93}E즌c vÝWs8.!UsnjOf>v di;'c.h#?CCE9lXW20 2{5Oa~%W1nO՗pAG9Gy5?X6Gj~e~-]&v}F|kż:^ ]^^7<1*§ph?ϼ/k{{D17HI tv3/x*70~~^aV SE]tdUԟf``nBKY]~0OdbY)XnrWj㊔*n{|G9Ϲ%~aD]T<20 2>\Q2o9|26+|pxl{ V s%d hK4λ2]oJ@{‹糁yҙxeoMi/'- 󶛰zRWqYm"/|~Y}Xx٬uuP^q,.glly1`:x;_g!FL߻4`ڋ#ke綊nlʃM`ӷx; 4§a e?TXdqsRvF<*=Oϼ;'t^ļgy/Boƣ#+G2M=\oxz\-g1?J9B=w7Uʷ=Y>ChfdQR;=tՉIGмf$M\UQOV`^.G}M໴o<26M!N|+2w̻\w;sY}-=/}9nŀ?2<_7qk+f"7ɍ:'칩n#g xǷw#i. re=dM;IqoyjL~>΅GR5wcGǀ^<*i[#7φ8w7fVl0:=@حro8Nq}Hs/N8$,n$(^ⳋN}w~ȽFjo8#w~O SQ R)A$ E8g5l5gysAZQyki2lᶐ>LR ?Boqĸ!Hf<>z E75]GdlOtmUnb/1}1ƽ𡱄}G2~.<3DXke55nu5~5V`q˨{.˙ףod^/PϫobMݝb?Ƽc;.zmuMå. [qv577w[Šma C;ʡm)J;[(ӻ.{\rˍhlXݖ:bW9YmF3܏_ZCn}u5P |}se~ȹWE1>u G qiL̸dwNnH:~w]La?9azʃP્34`TYWqv *]7Czfz!}rkhm=.#y˙/]c}O e|\߃p 5)1_b\up{z\[w9H%:X׀)e\ W)Я^t~Lmº}?Țs=d)OvXp!2 ^o&%>vƳÄǚz5Q&8ˌ3{ 9}G~=pqq|N:xk'5`yrk[GϹ'خ{\J[)\8K9ѷNsg^p'y A?ğD~J>>Ʉ0㉎y~8%%d^W[  C|Γ(!<f;s0b{ݑ1ܗo`=;cz:q.5l$dBEОT{N܃t)>-l_873/;]6O+W7+<ΟTf2ьqdɌ,m8U}p.EaycTO]LL}l6I~\䬯"?a;eDUДtáQC?UQDZ̤;֣rxs?SkZ2?cV›1!rI_h{pvԋ3z[2Omj1ƹV}O긬 :P.e_0o gP bKg[:`\Z|.6<2z{tιT?Mh'5lǟ*3q>a)s_8}1ke,n|wyT<vh|}:67W&O՚'9^x{!R&]^@k_6 C?TY`A-W/ 'owiy'LuV21YzuQzo_*'+ L1IIt5I_ n{xZ;:4-h!|څVοBS\h {}sBS.Vx|Ա: 2cts+#SϹN 5 > e?2G]n09n ϓ}*rc7e}y9Ad~JS* :EgJ&taܻpE(ȴ)Rz\]Iq,W܏^dڴn|]~̑*2ŷe~qгOo)=;Ydc=J,d?.1ɕò\ش]Q+x_Tp<&y͙_o?@kPdCoN:n8k&>llY]:=_~lꚣ =6/m$ ,s$|VJiFߵ0w;Lc9.bfnXyg) .{or نNVK8~tgNUyAٛ-s&քhɍs/ f~'2طN u!KKY=m-`t-;#Q<)ϢLݲ ^%.x[1r7-Z/nI#־%4wf I%4.t)f[J㢅Ҹh/pҸhߥ4.:n).\J㢛qQK=K~*E@1n)GRcW-5k m2^GaX0]nk|1?i]Tl Ӵ`}d0kC=;4ۙ1ޏW7^}ly˨#]imz:+w7n/OWv >O򄘼y3;kwj_{7??qAPwp}]B֩P4Џq~K[tj`j?'aKCarAr__x17Dw,ƃBd 0:@&tIn rvk߫Im39EzcaӒZ$4y{cfA_-r#n1ƹktTX~'zLoJ,SW{)V"3cO9˪pO'+,il+8~Ӌ|AG?fS*_rgTRq+r?s/i.q?(}K,w!$6U)EPf%l# ƻJ~am)('$'K|Z~k/89|x7¸p oyo)w}E#NQ [x*-6N= YNl+c`~ze]d,wy8l*R8Cۑbb#_}Rkg%PZs P[:X.dضp^lk^F9~kyטn*5^c/9EͣCkqA̱_q Klkbӽ0N7,{ȶo>btzoJ+^VldL?/'h݅=k#{SwhdOyRO0ȧ_k:OtY.$8x=7 bW] x݌߅Lt$̓>v̳6k]{ [K]]w֚qRk:wMJ#+Sew q|☫nuXąYOܡ ۞>pg &BļHɬRx]8>eS^q 6/\.u~r6gsOŬMxüwd J2^+g}xg݁fnJ=~ƾ@nUn9Oΰg _1N"W?Ov.:yN۠N7<7N9á:&3[Gu]?30_G-G|)N?lXԯ{wqE_s*_g?%{e@[Uga{=/pQla\oM^h_oº͘`[8؟1/~ތ!.PuKS|9쬥0;_Nn?qF]f{z[=?f=GHvG>| ifc~cU_j]|]\ؾe\A~=8;D6Ku1 Y_5ߵmok_; q?Gû Jvz?)qe`Ci{7 sȢ]j򬧓r'r ߯;܌[LK$.^1'D.9.pyk+,T.g4cpƮ*S A[A|OF];Y꺣}]bwNlF&㙥m4 /1_$yB)sOVp),?Uŋ`!y̛(5|:>?ױy;7xG7Q6OVbcwRh$Q*3k0Ob?3}|Xb(_Yx >hֻD-?3}QG,ڀgH }Z\?Giύ8po xegPuBXg=OǙ*>|fE8-0kM1DQظE I0],ccǼ̽;?s0-74͎|Fӌ1]}f֞zCY YKYAfσ}Ds(8b_oυNOξ@Dcv_L"LLVd&*&l4}my(m"X /&*'WMƦt% I?tO13G#cJ76=DƵx[߶ٌGf,cy˛U٫lXn;ٌWQ}5gZ"3+_A>ku keBnf}Nş@N]pMv_f~-xle锕3ΐ ,o7(ϵe$ ?ȡ9O,?[1?1;t62ned\Rzj&D 39({ѓ_us±~7[PW}gT8)?zF gUiO)ųr3s2?R⵫o>/i0eb^G PY TGWe^aY5 3{:GrY0+k:3xWnj/z d5t/yϒ ]/<8||9t7=_P˗Uż2/JZI̫[$ޥv=?^pRl'*ɤ%: \u6o?Cj MYvW߶Hx{]Gxwg;f089K"?|C^ҬOS<=fоHu=(fq* +8sBBl[癔M,DRe <;5^|qi]QL"0Z_ xT%apjz]Rk{^g9SǺ28Jbhaى2qs'r+RQo2vDv

"/kN03?3E6K\Cj8s}SE*9beIPeʷ .?{|M2|ҟ/\q7%|TR=7qg 'P}Wk~ K+@q%WtowX{뽽<ֶKq^?}~ؽ4&?{/ǯKc<}H>ͺog;>ҝf.tG# H}>K0랴t/Gw#'Ym֭eްL8)G2H_53HS9g0gXv?q%)Sӹa)`OlVWpYx)Qݱ8Ipn4_u prc\0> xA [OOr&gΟsd;A7q,1n6{{e|5 /I&X=OeLG\GI0krOw~wla5gR856y 8ك1Yg,4m_jϜr8q Փ ,X~NX,1 $c_sǀS7ݽrEt:Ow.:'wywFō>*ʲkgJpg\;6#\ڃײ|5/θWfƴ> [4/4/qJ{4k7q\HM rz9`"ZzmM |ǜ̹̜[&xB~M>9!*yUyuyy(=PBs!/"~0bۤ?LY0hqt& ~PTFqa~`:UL7Ya߻4Gx80$HKFRyFa: 3?lΧƬ{ j0kVkS~C:<@x:̀i3<sF3ol7؏'x {o 7ĭ-q[KܑwvIv|KqŞxAUn3Oߚ.#d¹fl'=e>G{ϳD]Cx߲w3sPws)rČ<4}VC0X>oͺΟ al=N;]s7SvD [+ p#fA-IÈ6`nn.acĊ8:\ccuƵt3Ӌ;>=b=vٍ(nf=5N;|_+qim7eCPk*Unvb6JGG|LV|k)~C+RcxyÔL})WG9j2ƵtϜYOApXnI%/_ӳˏ:D8eMV`Bb*~j7`$c\~"d 6o7_}OYn& s=s 9ZA.'}q6 ̟%~|;.ŏ-?'98Ìs09E"[Ǵp _lM_w&O2ܗrԡ?Y>@%{.,ӹ*y0+a9<_~Ey5f=]Q Ov ~bOL62<d{1Up/4|c|.1ow sRK| 0W/1^{,o26ܘ{U!O>7)kmh,8kዼsڱHf=sKwش'6o+11|/Gb?p.a,wюb܍w?SPV;nlız'{qg]нG yzKX|h܍kπJP9zVZG$伬}^c?ci3Z߅yo[_:`F, /8grí4ؿz OG;eA4j!߰ 0mó?o_O8&;Nm|IN}qSA'h=c'z(L [q,9GBָa:ͤsuЙuf,/ N${:Һ>`LL LƟvZjEU4Y<7AӉ-k"/m.Jdz> >UR0>cyqoIg4Ď,wn1>SݴezpS$OgquT^jq\9fxWw`G#q+x|N?vq8IyߛFgZլ`^Yq$,7wSe),<1ݷ;j%n{9qs$=0gcɽK5}ɺSVc؞8|&ompԬ3DpW&ǣ8pF*Nxw郻~$뉒+qO>;9t;hVG? a[)-=W8P]Nh+QO>A]ƒ,x/d 2V;g yL R6C H˧L;e畄ip԰.+4u/y~Q“v,+t7k~=^Qr_MݖO -Obl)~r0Fw6U^GnrI]r_J։~u,WKvYGO񣋌G40}|~K%4׉P ]9ghޥvF9g&`L/Upy.P2Ԣm}X7Ӂ;֍hxNrI/FxL{1»);NiIA$E}|iǙ}4w'qgU,`$3ߍg4,C8|\㮧;}h uќ1)V7m_o 18]5>iD /H5"hb-J^D|ԳU٩H0{(Pn U:뱙zP'+T^ chߓ<؍{pz%c#x.!⒱0ueq@[ʼnd__2ss d|E3?%xW*=bs`}s}#9}c@.,=?3+e)$ 1 ;oXbxW1ޙS bm{Z(~7Ƹvk2c\> fuqi {_SNF1qXr"_߰?C.}^ _z¶w?;ҌEgK>Ǽ_//v5rOlc.q̙XJM_2.s!Wx˦oC:Sj}?5y:cMx_wle横Wz;c<}㮘zcO-,f)x]1˅+񨛌1l1tVi$힬 yGZY#ϮݽY'껨Ugjwfs sh\.Lrxnͨ^8h''p0W[<Ş=YY=KSP/Wګ ,w!ڎ}GkkY֘.~ ǗEae]UYo33R_j>P +u5ePxW[,H>ouYC;f^© {Ϟ\>L5$@š~1- ggwۙn +r7|u׫}(XWcg¿ 2o|%$8gPשՁ~$іߤ3do[4>kxv)0"=.3WPM)01np^&zh gOJkp'>0,oplǫ_vSkU'ar' m)T!?e~:y(L =Lt[OkqXZې3)a`Y\Y]YpOܪKj˦N609^\/޽eʼ\=L=g]9/0|3YOyfGD>|>wlMs]7])roȫ"!3//e]fE.;uUq/;Qa xba~I史?9qd,WZĿC9(Uyy^\0ްQʟfrB{R-R[/|X>~81~!1՝oҶ'#2,ms51~_1} Yw%uba&fS8Mw0=48#[uuߘn..m,jK eܠyFϙT<ǩtvt5؃3٧  V~94gיj0 2㓀w0m)?d| JHnPl'N<)gdaXO 'd[hs [F K#n޻f})unc *;~io!El SC~51կ ՔRBd=ƌ]o0660/Z]r1mr9A3 96%7ˍw,7a{"㹬h&=?i`-|{959fT'_ʼnMcm岱S8^0bl)G7or)Go-S2nAn[4?[ޛh-7M}oS_m.L?"U'| mMX1ޅmm7Soq!fsnGxboá4G1^g]oYm<FMu,/motNU)h`r"ۗT}o跜%_PAjAxjr;|8W7Zkjps8NRo<'*0~03~3YΜϘ9v[Vv#d2eVsg cM:\V~t06?x!?^w(lv _L,o 8ov?+#;t= ;Иy={܋!;f-Ƙ1O_b S ^XibpF棍U'tm@wAvA njH},qME먷hT̋`.c_|fAXݤ}CӿSy)s2 oƻ&{7s eyv[,+{мɚa|?ۃM:>es\)cι+Oɋc}8t68ԺN9l[}-NaHM)?pػݝq״|xZBg`,aeпqz~Ido~nƴq,v?;RNJ073 ',Ñ"?~u3?EO=sޥGmxeI !7p$Ƨ8pmƘ2ǖۜ\!-gҕfuyw_:8^ggNlwG}̹zp^%' 5.\F{\!R1& ݋x} {=Zt{f\߽ny1~qs?gy\Ki:w15n矮iGdQ4:ǗL\{`*Oxf]@K~J 5o!TZGAc?y߄,sYϹ|wJ򫬧C^{eֻŪ:ocйb#'aiab}'O/h_>18t'u_@a{uJW X id,0۟}Ϩ{){{f{aSp.ߛ;'كy^q -aBJoߏwt:㽩M]d3u[/C+,"pO֌8QD/~޿̝lzNɭ-VGZfNEs%{)vt}SB+[mw ~;0 p,c\3/S߬P7{=S xN|]{9,WzMv_6a_SC-?y`L7sY1oDZ&HI`ϼzNSGA7y}`v?Q˞,9L>&|s71de`<8pLm۶k/ě玱)<qd!?>Zɇ4Q!5'6n,oO@؇|Y~HIvC{8\#r?ף]<psM=5/bdouZ󷞏26ՎGoWȬ}]8d0Ʊx~_H_%VGW] ߐ0Z8W(}l?#;-ja{QSyc~2Pc ]t+)98c:0멹^̏x)ߓ<ؓقs=*C{_yx?'CXF!p]x&'s8޶TM~ߊzWZ.m<SԕՑkAඌ+Y/:Ì}8#@KC]S~jpo{*ZXU~l>\Ǧ|lG=6c60a{c+O8qԏ1]E=Y{DEn"ϰ7~nowgvŹ/,l$53*% ]pu۫nokBES[B >gkqcZxYnU{Wž/֡ɯƼpv}B6s ?SֲN/`k3S? 31G郊kIR4OCgO@1o%R}7|o sj» ̆(]aM\x?n[O xv=g2a=dG~>cxAb$e`V0[.6R}WJ2d<ɼzC& [04]V 4w$-pm}5E̿ډoSؙ#̖ NS'4ٝ'g >_ ,,!~1re2gv|QlBiO2/c-!s ௷~zc:a=AO禞p3nFtqԑݹ<7un>|_:gdCc;|3. 1 5|k2?~eOu;1[u+;ʍdJΐImE2)䤄^e`޳>]/|2~yڑp;4g`;ЕmY ݓ:kۗleQOtvmY3q4Lyim>|iQ,ziQziβExq>%v7U7rz t6&3r 1sl$qј ¢#/~1 }=8ldp|6[9 qj`+,ڳ )!G_0F?w, cnVpwwSiL4Uoj7iƴb;'<7;[|_ EsB͑eUyYw`?ʆ(YY = J7տKP8i{s}S:*7N5ܱ_DlOgcJD@ JҧywoGy_YL*+>+s6E}.WWW>騣ܻ؃yDƧp㔐62NxGg|GOfڔ>ׄ#QesԑWdR N,!'<_)KcHO k[䐏y.[ vozW# Cf]oo Ɵs̿ztzV6su_C_n]cd:WK],`.*?^Gȟ 푲|7Gt)OP/L^~t!Q%z@%J'r8 2K_ne jM@mҦZNC(6:\ªR s=o~AwH^ꌑ8"g]bͳo>99eMsYoK _Zk(dž~i9?*ԜJgɇ񯅚1rN~L{<@αoν_ߛv$lS=׵SŽw~0}rzQ{pRp_ dƸ;9-֢Y_¿^ĸ oۢsuP΋Hlcթsٓw<pA罻1>Y;?-tS{pfK9N0oS›EVS&xs ǩ]SwU~ق˂s͒رI :י'M# qǘz6p0YJU1vw{rG^+aR {-֌xfc|0ov3g,@b gc W=xڮyJIg^ɖpqœ$;)0|~pF6smQD,'ˏƐQ'+TN,'a#q qΗqekkRpAyﱼa1kFEz)& 9Vr<}Nk ܁Qz{BI_`mpV`[^g{#cԏ{13h|Ϯ,߳ 56~m~l`GNL7lOe}3-ց99ПE l v!bw_|g^vm^e!e#VAiGX²7S‚yVu1`Yx99;>ZC+CxA}d{u`'IM|pWt=g{Fc'mx]!e )Ml&qa`z=|B2R鋿,[^j(ϒ$1󿬛< d@g|pSFqII#twT %1 \,Z w9A=^0FHl'mѓ1+J~3D'xnia=ƕ@ .OCeRΪ_6Ïr,$GS# xHTB>ӻ˥j۶ї<iy'G3+#t 5uMүýB=bۯ\޹ h27e5//Z/McC>o0ǰLى|W֬HtS=??ˡ\=wۋ:N^*· QW )|Z c0YſO fl=m+N9 7Ts)u6N:/rOQPcׯekw0ne 535\؟u5{6p[x񷎹@s3?C8mxV8Ƈg.. +yz's;;n_ՉyocX蔵2 32kAp-TVq~b_~ :*{_Cp6댏/ 2J-xtK /(@^qg;@n7UvIc^U]v71.oHaGD* ̒1casىş#@b"c'$(V8Vᑎ'2:m?Ea_'戲8y̟y[G(1)+] 9"gL" o>wrG=~ (,qOhc<| x-aRxwp(Ib"UqmUmu<.q"̯vzFsXVwcYrW802{VVo'c67e 9N*qw?Io/Cm˧̹W+?]Lb@Ƹo"㶃![R ݕAILAS_7*?ϸ.[ +^gG=皯wRo'f|'tqFqYq Õ\`0UeQ31%R~˱O|o.}.+]9Ȏ)RoYô9.΀]s*~Xk,UաϤqБ5ѧ+LJɕkxT_1I*7֫ooR̆r{_~?@=˕W5í ?]z̿ %13*0NeU`<Vqɑuw;Q&^1ϡ>L95 z2a8OgܠIg!6}*q]rwNgqfI%c,׸Ee_[ |X7 O2%7z.$(󞂮j"#qފy/@f~d3~3غx*s ˽?w&4~ xRfx^ƹh9ψ9#O :b\;)^d`^>yp٩Yc%KYq;%bSˋ3ZPFngA@.ncYy]#1b)yÚ.kG3buSҘw[F)iǣ7>ȶƱ=(8؞|1%qmƛ?a΍K=IEes"?T/0Mai|a0բ G16WbȻ%R;C;gTJeITHLnr%혟pL*S_OewSN1>Vw@nO*NeT~]᧩L;n곈pSןxLv$OPiÍ]mhv'gZ׮-7uŀpM 72}ÍUڝ=K쓡`02Pj^Q'wˣyoi5Pi^Wb0N!o:(8I+}ՓATiyߔ{\i>/r+1O],';:o-n_d-dO^eVC; 8qvzλĭo:1^YGĨjaG9af]ds#"k12i"nAuZ}Dܺn>K}AuiD}HxnJN+uߌ0s!VE7G)O/:)Eᮀ0?M$ۡЦf%BHSw4#M15:Y$Vop{]Ce1yKH|HKlaaJnrGʥ"FLtNGX7")<|Ixjs50j?V4Zkx4Y% q$ q- JKnIKc?r>n>b̉Z,?uFz[2w$]~bҒ?Qǡ{C“x˲hdܔJ>NKaZ´3-(\Zc;Vaz֔Yw:S_&Jglt޵BEz JӑӑӑI5 tnKQ8HGv4-JOO͟^OdU'Vm=!ĭ3o<ыucx❿I3DZYX*35<1܃!OX+KϷ3yh$iu1qXY0 3<,+IH{}\!xF=Ϯ ̇ij[O{<߃g̑VAB O\6xPxxrx>x%x42SPGd4.ҸvHΓW.K{,{f ,&k~r^c}YO6&vw =d\m6;Za.&,;6Q$??GnJ˻'vU$޶,3'-^ -~i,RS"+q0sf4`mV*g{R9;ٽT΂Q9F=Ӱ^O_VEX)rE.ʹ- G hEz?خH_+ckʹ{߫GPOl&.{fxiR6y,gO&s__CP_[۹9}|Ql2=0Pcg>0?aaߓ#,C95[}X4k0 ^|Ia? sSFe7N?*=I6cn4},.Gߨlm#%=ۇ|:, 2ʵLT dV+S9 Ωp&L N9CHv a^aP8Eyr3>e]a|cƇI8w-߱.# MIUyGW54蜾wCѕ!O1:nr+sRݙg,CI8ln|,'<-8u]1M0ή$w; :(rq^onY9rr%xv>RΔb%Y?)mrS˔Yxǒ\fcC.3e.gً<d0~Ui89[?C6in?ͪpƸ?*c\фqxnߴxXKaeug}ߝ.@=|e]i{|,1#i~6X:bM%IͿYGl,!'cC-NeɾItOr@2&#%8f&Cy$"Y}I."ŁDĿT](zAzmDge[k`>\(ԇwdK״(/Cnr}MrK8vZG<:yXozWwئgTyPyÊ-y)_e=@'=gYO־ gW|,usf勴!tO-vLyopVdIsY.?ȵ[6yT(sPyir!@3WБ%LRwo`RrAkǟ;|7ĥBl=xccv%ٟo6S-::O }07=UM~̩4s 9\͜jɂu0{]moS*&}DžB~&a䶍:(d{T]}}@1Uo0X,uY]μ85&O<{k~e<]nedͼo@1E}m!:ʰOZEHJjC.:Pb*ޑ$OmN=gn?r~Sֻ1e}TS)jǟBfx,X_Dm,@Sg,b̾^m" z1Nv5Y|jSwz's<~6.4O.jx=1J}XʳR׸g=oʖ{yx*ZN@el-W枷.Gw}a{y6({ا zpM4C8EFzL27ϿxmQ;ڨ_&%yjr=uY爢f ޼yxuHM#㓟TQA7㊖i'{(06֡sQO"Kɢ~EߍSţ< :1a?G=I;Ha+e+K1{2Oe}/fa!_[ UEoՇ7OK[*X2ֿڦ\:(wKz!GƎcɰpyJ]o{҇~k3a,GA_ 7o8V qۋ|d[P7W t1n,nJIJZ=f^~P7򹊛Tq6Q;#Y=Yō]1.nl|9%]^_sc}ڋ"/G`=l_fg$|Xݦ,Adl%|?,JZ;)eoVUGż+8tJö̫Ͽ%3 ֕4={{i\ɒԇJu}FyTc]y]ZEJQjyN7ܥGmTH=HR4Wnkyo8I!'Kpk#~8຤e:m+eLKʚ4a c~;G~ u$W|Ļrwv"/rwh;<*? U\;<Ķȼ*n0?s|`?7,#B^A&vHǨ8'+gK\ {= A$̲gR,?w~;Sϵ`]؟9aU> `~H ?PQ' }O*}[Ϩu=BҰG+˼qPƌ5dwtf `a;iKcMw)UN?*EFV7g|8cHZwtfb=s]#}5s.9ϑVaw|Y-.Uc?\bo-k.Fb9ʼvn¼UYwIIKz>?J,<|"hFAI߱O]yEWy{F} y/?_RV"8 3i0]GǴҩs9m-2-Y'i!#͙,T1K=u(+λ qzuND!?u䓠(~lS=8)c-G՗^ ;ǞF{ 9@ocןYQxbbnAW6Nҹ?ύ,+_y3.XxV4X<)Osʛs^6(èS4fF8nqڃijd 9G;x6Wv`Ay\SNgN)|pO O=?1!<ZC#&C?}qybz(apFX׏i-@3-m,o((Oc}nk>V~~AQOq}Ք!;_|ky3njr[#xWlxχ*Ҏ~>c /s.~_Ryvޮ_57*{f\~Ht<ܖvSM`U0ZkBRYd'[*q_y+72%{0cd"+yLtHJP_;lx:a=VӒ ׮EP!6N/IljYOpf#߷MO.e?%k83/aüR ~8 {~ ۶NA};O/ټL:d<*Wk:*~z%>|ZG:i,0Pۿ+~ͶTL,{Eu%/ew 1avGaхq2ك+/7i3Es'S<\!1}Q2eaH*Fr"(w~pʦmVԱʦ̬l%M})c\/1?1wN.&%\Ƹ>.^¸-%㟁4x.c<ǭ c`Yjw U0yቌLlx:k0)&x`O5S«2ǎMfug^UQՌ?kT@1۽㺂\#40?wVjfI5ʱgbĆ[P)_QJOj8j.]'L{\ݴ)0gu/*YjW7_?P1c{N)|C?[aO #=B}dI܆5 0>eq~e Z^|߷vmݕ+|zwRNc2G@//` d!zKn<+koN?Fg87('|"/6$c<˱z)/dc9~p5}R3o;Uq= G7.y(77x` L+̹g>~>r@eMc&eavQQfeנ&K8 yNMqqtP:vl-${?q(vY{]9om%i`m]<!ی5kٺ6+{U\c߯M:XᙵMŶ2Ʊӌ[ ` cC:|u꘺z_i^W1ybdwZt^u}@߀Q?1a?U77g3ygkC.ZF u?] η?o[Z1nqH.v8On῝#q[ܗ??kaڛ=wD;Jv3ꜶCM埂ꚽ;_Y7yY7}ϒꚼm]N1ƻn5i]CY z&5a@z& ۿq9 \u*U˚Pϫg*ƣ!Tu5:^v9ѻ.k7|?rAόgKr:&V}GRo3zxg&?ڇCK<=p/7u";dO> 8WWb{3{0Yec<ϯo&;oA1Ƴ ]^04 oqs*+=DsEpZnȧo`σ_So=4cH  ܷ8}YbTÈZc;cqz3T]Cr5f w` n#c W7`Bz3(r0?2|e ArgKɦ2eCvv-`b$ 1OmS8Ʃf u{Ʃ-W?6}g*{?5{[16$,Mj99 !{ִrL*_aohL͓l){x<1wx[r=zؐ*Ź NtvW`̾4ԉݺjY޻0r&™ΫΉȤT8YcL]}mZ6;_2ǧ߭˼1ϷCC Os+ r`g;!:p/e1b؎.{VM0}6TC~дO'm-R72;4ޒb|{KJoS#>odٍL1hdC @['ƻ>$kY?-77AxN֯.la祐f]wA)nc'ܑ3LL,?TԬF:hΌft>f77:ϲΛ)LМtoN:kN:gc:ZG޳F* 47O&57iѮI>qXƸb Q¶28fryTY%ukmw [DZ3[ǹUqmZPjAq<ntI7yu^eOYgXKҙ%,ےt6lI:$cZF;=&vWzxJxvK[4_Ҥ&3~:h/vhIq|C^W}lg~ ~mi&%meTN˵2z med+dW(Ώ&1P{mARgƏ^V&~S6񛧵rM6b[)+(7w:qq (`''ٟ&8ƌ\0?#~8GVuY@N[:e"x8]o$@e(pT1.h@qE2h@x)&J;~i ex,r4 py^z,MN8q+"\l|r=Ԙ;ڎ`}2Y&h_yfQyeszrQN?3 iqv9tHk>qNt0y^SFC:3v,.ɺlfk3Q8C$fLU8I`HUu廘hҨ+uhJC]X3^WP=U.57"lij~Sk3'*Dskр/3xsbωAvS=oA[ Ү&UK?q%4WT\-b 4W_[޸f!FG\}:Ul~ %q:OT_]ěT oPsSyHh%ialRKR:W/:e~gSol}Bd=j [ppf Vl^@> }»_1}9yWp{iow5{Lvw5kvwDE/>&/w5o]M\aO7#.{7Ӿʹc%v-z(>IJ^_r[W?OR#32uҖxs֤n-Wnnyx6+-q!08 8Ywٻ8/ 73'\PƲ]l<)Fx}[$v4嬁.k P^72./}p[O_">I xء4t9r8fw?Y.Dɡ^\PH{m(}WEB $2?f\p_ݷ=L|/ax?0}\=#sd{=i,'͑5Iqҷ'דzْ4G'͑Isd?4g'׽ݷ)y-@wRӄUOĽ:8C/Sf{4Ccwn cec<3oc&=1m>_8zч/})Kyt_ʃ5Rlח'}8w8-tX<'//עZ6/Zn.ko?OlQp {p9'"(QK*wq*=}޻t9-wy'$8/Aap c ne1:K2\#ԑ nxYGYwޅ71 1"{#[V3;1"ie%β|/&vw'g1Tׅ `\p߯/}TT`@nuǿeivw{,Н`~;V k)-^,7D'|v8gİ0?m?F:|?#g8f'sk*,F>7NJsO3ҫ&&v^gDS&HeUU: Rx³ZU V{_p/@W^XkEs`Er)ˢ\!DŻ~bw<,ٖ6c)Cd' ct3]}H>zӔ1w?;8q3xYy}Q!)c<s>xO?w)P|)cs9![ChM?te*\q*\87`B~^tC>ݧ蕍bY4{9] 5ŔJ#. c {e*q*'Y>ped_&>3ċ 5o@ڟ~(2j ġ&,5aI67?Һ-1j`9ϐ81gYV➼iSaa'N1lad'FiCa&2[w)uCAv cLY?̔ `ԏ5x ewǶ!:3ϯZ\=둳%П.mL)ыS{/ӿsn }͠"xxu'nK<N-/Un&ѭΌ.ဣ8&2ĝ=<һcvH35t?HKے,kփk褽Ź>nPCR`H{ K1qcc~i?&nqm&9ܛǚ0yz3=;aJqr =g+/揉YV ^8x~[hg #L~9yx Y7ofq؍S\oȚ,Cpཀb_NJAJߎq'tiYg`u֕w}zgS?1R[V[hwQy$X߇|Ͳư\ ;;mQzo$39,W ƙgpY7f TTFrnZ;pOv]nd ;>( xe0pzxr8Qf?ʭ+Nvz佳jg9ҜHH*%؎1L?H/ĺ_,}QƸ&D~أqm4ʴo7\/хkzq(O& Asz7 EWXխ> SخeG"Ԝ(3V>c\Sԑ1~c 72ƼE01T2!^V*}>x̽%IΡ{xƘ1!cX1JYb},kœx&`=)2qEUxb&쟼G~!#k7}׍b+KAup qqxKB="'a̼RjBnry?a~l^ vy`_fȻ S'm' e;(h{JpEe^>,+zP6Xw,DᲢ^ڱzJ (~~kT]} ߻+`rަ] s"pwO3ߗ}{N{-+?_\K0δt\`4k(“0f^AuS_ ^_Rg:Qy K<&W񸖿wĸS{p .p)<_ݎt ΥX̼~Nd{?Gސc=}H_x@gCdޠih<7Db^x5E'2Yy#U=[m nn`>ʼ v) {oOk@'9򕸛O9&=MF7ц;!'͘?&=LY~O)pDS '=> Wv݈s@{)_on3RYUl?UDNuq"u/A`ǀ MjY&8&d8(cgD1DaK$f\ ,-A<=n$Sk&}+[k9 ygc-w|O>:N-4q% ,1D|, GcFx:g:逳`C\!PL"`gCH(&]Ig&\c;;ƱOu'૜vq_![ڲH[ڛF L͘Mq ݠlOh`L q}FSW'S lA=q(Z ؞ɦl깋Md6Y^̋O18Sb =t( z)0~|;yGd gʽS]G~_W~~smoR}ke% %C *Q!HUMH*REz!iҫTAБ̻3ٹ{{qlymEwJ&|1Ge)uH!(7H.ݖ/@gmv~}q / lL~6AU~ߟ64_#) Mк;O1?@Iv5Ͽ oOxmxl9%C9Q۟ukLsaGu3Q"o-/> Le֓~!y(E_H<$s^7C-+\[T '  ҚuFlV LRs^\i^?I51\"x%qp2:YJg.I{PEɴ 'i&F>Y?_Ppx?"o׃t{Wsx8^YY'!B?c8~ .hzUL + =S|T#pT OW-,xk&?!f\j{_Y? _oNk5N{r~9FnʏsD~)ʏ(TmJl1A(mG:/USS1SQÛ;>LBzkHkDFk6{{``t7ep?QwCO$'Vo)cDD ]րڽO[ 4k 2Yi'WZǙ ':M^!Nd׶!~'iz]5G_7=>qyw`Q8 8!KxҗL^2}?R1 ^/敧>6zՉWSKǬjЄ#aO*YYE~cJ]^b]=@XQȊ 2ד\9 ?8t]vŜt6|-kUB͒ƈ߬rlL}HEvXAɖ *b`CϧGAjL Dr&1mvrHnM9\a϶8Gf8ܩL$o-<( ~sɆnPv6[L\ W;*xmƾMr@OϗYfV \۫)L_՞h⯶;[k'0XLGvo;@8nZR1297,7+ $alƏvN=vvج>rfyxi)]4"/?#tf__vh)8X,ISɆ;c3JꛝH Y}aD2 5d{E16s[|+_A_9=Eg+ۖr{ Iùύy,-O_[`OeX_4Ļ煷pۈ@ouƱg _T~b8H՚GH'az7[g9z-MG B>} k<\=<^>6H'\ڷrk {[#rOr)AW;r-k?{U }=}Æ,MiQڔ& 3gz^>;{ҹ"|DS1/x.av;HoYĶB%vΥad&%gR+X<8<~|}I31"aK1f!ϿVy*.m%0p?!C-}.XWOh8q?/Ezvf= Ys3(SGU(7yG+׫خ◲( ߘNjs}_.vhZ,iSOVxʶϯU,:docʯ ZV? |}e|]u(Mq[B_yw.-p63mR[͉_=H [;g1Ϟhcxut8[{'ZZ߁fqom1*!mAMc'Y8ɮ $^sƿ|'a ֕vsuc,@Xz*+"(G^F~owc wqxSg09>wꙢ:be1/PWw#ƍ{Wl0;{麛{IWNf40lDǸAx.$\VZ]c#X"b۵Im^ns#:MX67?205e9S%S_3,PMrc,*x։[0 ǑҼě2T%.Fo McMG8ǔ? j_se=Pz7EHnsoѮvϚwpv,mtpڧ7j=~KQ8 E7t]t-wEz nH߃>zq;esE*o!V0PNMF㈷Jøn$WP׭omIjKތ~8H]뾺H p 0c>G Z+N0~x'OtẈOGzK/Ȣ1ZmnLF<6&}@_%zC`#٢;bymaT >+UhCNd($+oVu1=KfR Av۞d/:9Xc=XDbJV"[d0ƫ _BW "ƢKsly<'q^ pڥ81Muhp + |ܛE˳=UhtR֩LYYqxuQO;szK]=y=G{>] aT?>0_ 8{*: _}o3XAǁ=B꿴TN#dQcgڈYڐky_9w@SpӼ/T\}ՀQ@'*e]HeM|\ ~j2N~oP:c(sk6+WE9l k["l.wSnY,O+rޓ,֋uB||+,r5F|>nLݻ{F9ԷRφNJ:x5jUM|1PUܤ1!Xcbuq5V\\ƨ+NO|.Y7 p] !'9Gc~AKq${~WKզj"FkSS'<_5r5Y#.+F+Je+Yf}\Vс[\ιy{lj9G6q_C=<& s?:}B MǯGoJ.'̕o6D?,wJ>)~7O云5 .d#1fiz:ؒm籥*ϱgW{j;C{ozN/Ήqy6GGmkg ˪h{<ϴ]y9}:G0q6kakԃ.u,i]2$ҷ>%k< | U{1\#=6xM".?Mjk=ΰJ5~<}I鵞(qhmk՞U^ ROv@d}N?Z}CTG^3|~+f3M[9fdYrNkXK'_x@;cl຅jR7]yCpP:]0W YNbuG:<5zͩ^v:=u'd |pom8 NK*صѿ~po:l]/69K/=3m^u p;oF]NoȰN>k=q|M7%lw'Cg&ߜtvm&x~ذY?8f=.qWhn߬Z9fyfs<<626Vx?,6zls~@tϡ8^clsz> Ƿ߿s; Mylo9W6>9FOJh[u_& ;?'@mHAW'Z+(P2S9' 8HG9~;~/o' ~?a-7l5K-z(AOOP'8ت_ ?"̋(*qNI:PycEyz(Ϫ+g OLL\/_'cw ɎCϠ]~gQ~Pc1UayE_Fc䯠%~WQ~5O:'?ߋGn?{/k#q [ʲomOOO~o𓿛CP[~O/<?}OQ'8/?M7䟥k蛾xmA/ WVʹDS9e $Ԓڕwt6nny>?~7sGˋ߬|'@R~ZTC:֑B#/H/r)Ov_OKʏ"k'c};\_ׁ دU|ul^;~rʟ'?LP?~SS~N1l}<]ɾ9-rYʱg2,G'C oyoEIoN7܋O_v H?}&3,)O~aʿ'( ?~1ϖv_K{/<$O%>ʒ}y!i}k4SY:+GTD桜 x*(/ wo) WKWۋʳ+uʩ'ʩ%|ew)^+W|oH:fBs/3owI+}%K 1^!vG;~G~Q.GZ<~1Mv~X/?w#CJ^ž^W={j^;_O~?^/;`? $CHF8奝F Mm6-eP~Q+OMRռ_Ѷ<±I 3k,;koǟEɟcO<'_񓿈__Bo񏿌hU^4ֵܾ]9+HW+*g(gq!}I1.zđir(3z6 {5I椥,;OO?~MWj /t°{Z_2Zʷ?$KYeAפ1K8o M`Y"pf3 Q 8Lt88DT R \d  S`?xǿ/~.3 D' H?+?-_Oo |KWn)p L7n(p \O@G \C( )p5 \E+ \IW} \N.)p ~[.(p O^?W虓Zp"3W-%i#i='3ī اe; o~b߱ (<߿y?9M!^e-%^Kw M+qz%]#Q): kaKaE R~g0[4½o#~H] ڑ\7/2V"g䷃eH-mi{x,[ccK!T'>?_rHr)aL>a>W("Xbh jl VSƑqkmGno -.Vo8 }?^N͊t|;z$7|ٽu!}S-e}?'6 ̞޼v=x1x- YCr?{'xƯ;䥟l |{e!Qz/c&?X-}Fr+(7xg|؃{vqW;gR_)TVrma9~_WAn]bx$G S|Iccnc %Md^ ދ~;<; ?\86*>)˵"G6mpG?)O!S~`5&^{D22GڪQ^}T&eGZfQ^vTIczM^o<yczM0☞ N1=g8丞k hzl\lq=/;q#'~Bcu#{ch_tB?cz|vBOqI=f5<ǡN1fI=^,?ǀ'utRg=gS89mJt_xJS:q8y w)K> IlAaW8ξG=rv`F"߈\Ox<O &|0xgKC}#mSq wϐ<3 ok)~&;J TnSZ|~Q|%hG1SwO3=U@Z+*\/ퟴ&1 3v4P8)?26o'lm+ݮ?zp vp-88_F6B\PpI2%Rqk?p8c X;O.:u~OCq:Ng k(!գ`$([F'szOM__H. ݢjS}?bmxa^[0jL| |Y(='+ljLEEcIoNqV 10ɹ"s+a\ g+oy==pwqExNrC¸F thFd_ F¹8ξO)|Uq*īlc}>4&\Bu8'/# 'H148#\_p?a8|rUtQt?5D5`< u;SkKD=R~3In\_^4v#(%p'5lmK 9@Ȟ}n=u6Zn=!]E[NͼE= 4!^g1|)J@~5e@g= ~Tjρx݅~{DSߟFA#NkHC4T]joe.[NK\p?ao&gK =[(#ٚ}. Ĉ=P 9Ox=9L'?a\'+j #W |0'\8gؕ\7c^Bxس'."aܿxýٿxx8%^@z4Rˌ %?)DZr_j3$OrJog_X-5B_#| qOW-q' f9LYn*ov !ſ/2.0&o ;RľBPxοA+| ]XgAĻj v`(xHl8 y'< ᚕ _YGsf:ǹ56)cOΩ^'cℤRpHacϧE?`G8RhyȏPU WW>lr':hERD`GcO O܋b;w` G^8RI /bGTh{ݏ1׆F۟@w6&w`cfIcD*Yl۶Ǩ7 ·~pK2~Lǃt>s5W`e<ןJw. +ʱ\}-] =]l:Q? 16oVZxFx]#~îՑ4RYQ6?6(YRp[';AGȟCx|0-#|0 ,,kNiX'އ'9H<pM=W''ҩv3F^o bp\mA38e]KG@mN:A:sXƱd'ޣk[`VOIC!)p L஀]azU$L k:azՆpk<01x'ᆰnprd4j\>I,.u ]%h1A~_= ?I.,f8V'^ׇ-'[oЉ54ߏZz(7<&3ӫ8b,"c?pD3QЮ'|p-f]ռc7悮#Jo>Rn60gq%ۆNʠ.Ex .t Ka(SX ,+>ʖu/~7L x#qp,gp=}m_|A׀"Gq_F1rgM{o8402h3Qg֌i:qC<]<ڏUo!ی:g1gTׁ0p8h,~ Oh6|x0 ~oكȨ8sLqn^ݎIm8B%{)2QNG-* ۙ^FL:3)xL:ey-D xP&.@gL5nq]d?_Q̬)fKX^ӢzD_8GwޗIچm؆ʮgOa=Niin&, ͬdVx2zM1͸5zf61 R'kffY 2d68)>#qRnfVmRN\N<1sS,j|o+D =VCpۘY|STPȿ s(ތ,*3_C jK8ŕ<>sԡdQNE?W!F|\U!>S8B3gH^XY=UkZ/zX6FȢY",?#t NTiϲt\L:HWJx5_ŌU g_ɲc*:VQ.sJQg#.vq,˫Bg8m=p8ॄq>Kow̳v3#fS["J;HxGR{iY%/ޯBbl' >cc/}9#gl蓟cyR<^o>>ZlxڶxwΒChȡ½R.k'9>SsԜW3;’y>x;m͔m>9f U~/ k<eA&Nt'~SP?,ֻgkOKኟ^ؑ[F: Y3Q~UF`w"~6a[C~/}EFXʱe%_hp3y=ڿ|)?-v&o ]a>*JޣrJrJ1^⤣Y.4_g=ayžk]OMO_'_Yſl?cx@ =}{;,i%{UTUzG@ncn=!5 W,%/ |A&9 |F'~.3 D' H?+?-_Oo |Ka#b${Z~~GS$RSHoKbyMށ_XHһ|S>2)_[iS/~f;Z2|<*6|QqϙGY$+ϟ|2ɍo;w,'QcE]= !?nϦޣ9Or}Ym5ҵ f\ě죽x+Smb}qm܅xlx7x}?37O W>nOi|:g%t{˧ $>^4nzuʯ/˯8I% *;' (뛿o ./uq<{!]Zt},뻤Bº 6(;0sv*p"N=_8ƅn$   ?o71;-Y!?+f.ͦ||D|oo$|eRLx 'Y!񯋱: " |밎$'#:ܓHm춛{Z۴=˵%Cmc'yh;ڛ{J+qadG;Sg KZ{5ء5ކ6O.n-̟Ҹa\5n pDZx<0r; +dG <𺖆\ vS~1?F&c¶f0vG0*,bJP-e cSTzJY*ߪ' )#^?SxW+݋qMIuWIuؒoK6T+#XRG𤤺 ])uAR>*}K:R '*iÂRʆ gJ)R6V6,l*lh[Z0V:K+K+{K+gK+W3ґ^i2J2JǶ2J2JQV(PVAYO*ߌ+|WV)/h\Nc1RW@m}R_Cb ~ R87CjR x)#=H )ҷ ] ivH@y) F.#悴04S ϐ zeG?@ /#Ri!n'!DH߆4WQ Һ°h吖%! 2 !- iMHa0zCZ-!5 {&2C!i,zk #?52pܝF@-\J&*'/Ur%q>y05~\ Jz>I*k,[UFZٌ']eנʀi.:цd4uݦp1~G뷟q$~1A _J8q|eu_.9)ǃw 62R1>iY#D T/C9'aƾD~"B4&F~$B_PsTQ>W*ߏ*'TQr_TQ>sUVUiU߻WU}BUWU}WUTU^UUSP5կUSW5կ/0Lxp-96t:pll55>rj*3PMN |T/||:RHɑ+#D*ߟT+Rި|}U+7 Q:jj j߫6yV]I(&TڤA;EfJ{D69J(F/؛{ PcoT 5/jwR 5.ބj=WCj7c{Dj{Ek{Fh5Vch5^Vch5B5[{Tcj]FO5|[5Js} tafCZҍVGHC/Qׂx4R-5~HkaAZ i'uѮ.!6!] m(HC:&n)W %R3qpfF}HC~H@=m!= i mi.= #B Govt1C#@z.HBn؞\FwH @)! ~zHY"H`2( i ! ).A: HAzR|]X`!i,! ikHA pHA:t$CQ~< i HB q~ xH77Tg SH'A˼OiΐjRkjR͝ZjR͝Zok>ROKUvP+*V.SV+%q+%3I2H`+eV^7kkeoq:Z[kUƀ֪ɭUK'8Lj'q>Ԝ>Tr*NJMΗęFuB~?NyH>M}O_88{؈#_Cp_8 o*lJ9 /_'FuFٟFG]+Vխi[*h!N\[۪. 䌡1/FkCCyc)1J*ݿMqS)r MکxvԶjNt8ܞhWh7j{’ڤk{WRp;v^al{U;TF;htP;(:(٭Ľ@? }G%NG%SךzSJ~nGoCGe˾z̻B2/IIɔduetRܾw~mGBx9~=,oodFv0-'=xM՝Լ:WHe8P'qNmu->+ty.Qb{/|_wg>0gzzbUN$w4H36$76| Dр[N#e <#z?>{#>RG@dBzbUF;';l0q\HG/93r NpgSj#p}[7#%ׁ} hS#e KYe5\uIOy:\owH =׷d~+[>Wʷa/Ko\i^òט-FV9.Òl;be'ګz(B/Cwm-,Aec_XW h3MR#~eBul92gkڑ`ǂWj_wKs}ש9:7$s=3rİrG'!#ͽ~O]ccU|X#2񤴓=GdaO9" rE~+0!WΛ7.od;gv]jLtƩ1W1i)17+&cJ~cՇɾ3eybMLg"~gb_3|fTqC~<zL0/dw4Od7y>w^Zf<+,XPc|{ 9b4O> FβPf&'#{ڞ-:Ro(YjFBBÌOg'뷩\kַ]CӰ(6(gS[[>^hca۾A.4s^WCvB!*W.䢴@N,23cL*̵N>dX낟EvVgRsMsK*pH=uX"Oq_x<p., ʋ#)']*{ӊmV-6eK;,RLz.~_hΟzuh&3w! J}hɁN~.8 t+&?umֵ @'+su)lԵz '3 5D1N5JDeƠ+sr\zp%x-Dy݂mV.bςsul+1&xǡmj7Jw^f[*3.| NkpK(x#r]~YRsT~2#؍)ҿڞ,p6\|0 .IgQylƞ4KUGQ轋RQYWfgV{+s쥾':玏4N|raco2\eS۽{G6'}O)yrG9j¥?,~(׺?,qMTHiewV{x/ ηHjֳ뻭F \\!v\Fi-]irȴFZ,j Wfܪy62+Ih3FLWaW lM"v^NNr ]&;*LS Ͼ:G*SnW|~i|]!7[٢iwtx*yx{Q*Wt]ViuC.?V }D6BuIn@_RuUTf2}8}H1Uۍcе!+-vYv]2d?#55r݇;/+4[V*NWp ^(|2h^P<]k[zka=9_ S^C3xB[w{quY)s1]$}gйAاNz`#J3a~&UٳnG?_⾒z+~ ïkDq/cx >ʇgS:ΐ{渭UvVLGqn^?2d7r2ӂI6vx\T[j{-6V/F"bבVRR-(m]+M>Vm՗-tUԘUk߷1sa5ֆsj҂IU=5f'! Z6[$u'#vu-oI77a6ODp\jU9N^0p^g]6jyyve6<'b,R&~v~n'Cz[ݿ?IH O >o^Wk:dZ[ 8Z?z[L/~~I6Ng׋[ke~>Z-[]UܻA{U7+s>?j}֚N`Ս}>ScV;=UZ>w4/eL:PX8@0nP'u٘db=3;ɚ?~u:61 <|2mquwJpyr^f,:).~"_>w=6<>>"1bqO %&OJlٻ$kD8~RCmrsҡ럟yH`|W'kI;c(t?G+_|&;Tr2,#iND>eO\8Sy'H}dv$[,w-E&_+lrFI=3</ޡEZ_JU:?x27]ཋ4Q{ի_j|___/p?9ޕi&_rBrS <z[^*8% ߖ/`4֋o;k |{)^e͔xYH3? wիmp罣^$Uwvzxr'j= rF7ݨ=Ë(','4/e9g ':82o" ~Sy"!_1E?vw6үxxO慃鷞@w8Oe-6[7'pa(Mx_!;Q{n%s2Acgƣ682>աKe{-9ߍZ"L>%jaΚ9|b!׆.Ǻ,W]y(j*q}wΐAߝA-Ix[(>od e`-2ZԸ+PoNo71~hL 5M cwe -<އ&V#Ͷ6bkC7FB>kUڊuwF0T;t Vl{>~M&[M&n xo0muͶnBrApkWTۭF>ivpk#*  W'2XyxD _n}qu)j7muvO7Ģٓ=Ƚ?.:gc51L ֶ<эBm|L pG_RG-r܋dq8=v{gКR㗚 ] ([<"wf<5i>fxΏ l_>2Zt_{ITە9?_; t'֑eiM:}!4n<|ƍǁ6$ܸ-uo2=8XA^Psv8?.yV3K|\Npe\l?uN 8uLNY%r^ӃOF?RlKǺP6g x%Ӄo^Q~%v׽qa^%9m1Y5L%ezp5x=Ӄ1=xay?w67?0=/cbzggEcL ~W2=R&EiWcw), -J"{I(K!$J~̄RcӘ`V3b'fůh'Z{{mL|=s=s9܅?z+C_ײA!~٠?OAJς>E0/.$~п>CQ9t=ďuO zA/GA?聠?'h _?i"~ЯA/W"'uAV??x??ƠlЗaW_!K_tuc)ݨM^ȖhzA+t?U0gA6~3@BM,kFA/]ۗmVK7n@2t#_ L݄A瀾AnNM6݊A_݆AWw[w?[Aw ,5軈to5qGYh**'?"ҴyX>3m5_O~]|U I|zgkVy Pt u.^ 9PqO\OSךO;PKe[S'Gz8)cDC"8k3IxgYy>'~!N'4iLrжyjOk uEȩ8S(KwKeexE_2vPoz HHc4t"+"*;uAYy\y bgi Rw7YUPE"β +!S)ȿZQGЫ]+ z=A __;  A_quxA|W8,j Pq{--, ʮiҿD H<\Q/ :v)/ t~'GqEpqz uO25L_ n G<^.O JȷK(gry-o#U9}]H9gQ9N;'E;ٿ{뿬KNx睯 ƻJ=o%e9|Yg?Ugjj^@T_SהB_9fnQ5ĭ%#Tϫ;=z]e@uT_tzl(/Ϻ!6vƂ]^](^3yyf =iqyw]}?yy@"nA܎">N<϶ro<ԯF' Z=*y[{&`9okU43|vw}UR#EK;$ہ~@1#|`ђ_z^tLG}N`>--=`ln|MyZc.ZPw_W[n/`*_tTcO]\aA!ҧtf]FJj֣ʗMq5ٗ1+cgp{8+zro~r{!~Cय़缆%kX⼆>J÷[E4;5a越g!l!F [-(Нن΋=hkoe C?߲[vkQ9˩>X`$^'$C1@}r~P} ms y\>\Fa񼧠 ;@VVQ*, CvF<$ⵞUTJ#eж見_u7TdL鐽o^ !ѹemB?׋zlb 4$3ݘ2.\۱m;~>O- P"i5^UOU*Tt⭍4Q45xhܞګz2T[J{93+}iDeJcczYCCB7Q| 21Gqm|:Af~ytX_t:Ӽ?ɷ s >g`NsA=6u=K籅߅ S}xީ|yXOZ9ғfc+|)s/%{UY@S4lIFպbQJQOOyяuネltJ<~J U,:\!L P-4 PcDv?_}Aom܇dsqqy>O8;M̲g MGB:>!dS2 Vkۈֲ*~i̷7 nR1ܭ7CuFk)trj}Fɇɔ%M&ɨM)X'o~%orl#iS%T}K}1j:ӯ] 5[^s9njXx5>pm0f6>pmBa\'8Ok~(!};eD ^Y&^5KVe2Ӹ{Lv |Msţq/|[d|UxW6*<=x+y<0τ=XF+fJScEU*>/+%ټA/Rt=Q˶ bt)[>sJ;[T\'vg\ /ێ>^} V|w*yNG}/"ܔ_럌nQA{)乬O y;wPm|;A};A1l;A1s;Ar;AؿwOt'G#H{"ƖVHV+-WOFX=NcsGٸ(~YӢ.Eq]q&"Օx(XrawW;N`a;{Ѽ#fs?sT4͟|go{,ww;{cxw4Fݛw6;{嶞wxc.|7|wx;vܵq|gndߙ7,̵f|GnxwR<+oݸ|7xůMvO6yN纉ϝY%>OMu Jd$,{f"E"n%͓XXQI,$@y]:D/O2>Bܞ39}?{ gAq'4O&4?O2iSe79kɼ3gΫUg'ߦn.<^K39dn'dv9_}c 7k0s~cC3%K@_䡟~W[~GT'ȫAQ}rUngZfr2J2]~fld3'̃v1z35Ř[}7Qo#.+~7|@:>Wʮeu4Ϭk E^TGc6Ro |/|d;g_{r7i.In{}TxgSʉ'aUR&.w}VI{ʶ{֫{G_ow(J7S׿>)7@/zwN?R?}A>!|R b{+ܣضg􎊦^ ]O~ץ8ay~5#) Q&)t"ҡ-FA'k{>eIZ*mSyT}Sy}}<ߞO庩{x}6,F5iNIu4MuM!Fbv>!",:y!z5xK}Iwbcg mN'HOd[| u,8t HaɖúܓgOb|C|Xlzܾ8Nv?:TYw'@Zw78ⶦ;S9:Y@Zgt aJz| Ha!ׁ ' oP7Iqku\w{+Ex/eo;kc$p{ +w[~\Gz\O HÁYd0X >k7Q_n.[p@WSAN pMcIßcu MH#PS i0fRz$)l&Oso(=q_E<+ygh;TNV)sape[yUy (9j\1Q|ߦ;?8o$1 b0KnvnXlgee_d)aYz,KXJX%%ļ-:,4ppҶJr^u|)oWjZ/}EO(5K-3QMs;~Gwľ|~~w9$HKV|@.i~ry3 ܶ:h]_"}"PA3~r~쯔yKy5ǯw"tv%9_&ľZBBXc E;! REQ-RU["T;ɕ~<}9gΜYܹsf7> !oĿO.2P.OY&[2՜ᛪwsz|l*B. V H˜H$ă4Q\});BMX-y& V_xoJ~$,K^^69~ݨ[b4ǔ'r,N&m. o/4{·Hxc佚o&c dq\B.ѷR>uq^u&gm W+ow lU4ZS+!ȭ+|s+BF3{8C:k6}S&|5?4>fOUߐg$r ѷAo)H8Go(sOy]ɿ6aKɱ_=LB&KMzZˬ]yIrjR-^lP}bW)y¿FTUg!'|;/jyJ\ߪY>RrvQ5~񤻾ߣr|,=oOj ·_8ڛϨg +ږ:O[E+c0I1ueJ^ǘu\8iu\8u|Q[7}#)H bo{ 󸞅{֎qv~c-"s17OYwnk acTgVWjs}O)['\5Ŝ'^͓y-vt =y7[ӿ@ӿ2Ŵ-QhCIK<0Goy̙xgoeXEm/eI2X3VtsxJcB_ql ݁.s\XMӷ%tA٣w.RvW?% U: :J,ԆeK%id;iy\'7U4z`1W:xԟ6?kmM׿iaίJgq-]ɩcsg{\=_bOf m.Z!ڙ44^+JM^>g+y\F>pMϩZxhYN"~{ Ϻ8ӊyMExg>֙=[~K7Z}u_O^=Ǥf{NKoBRRyAψ|ƽ 칦p,Î3nlYR`mET[TuYuI5DGH.zuG9"3#l SͶ0?՜S?Toi13ƇI~T?K8myչ9ۘw%16[=- ߣ^O(4|jQ!i5' ߡX?]I?/|gvߕzww]i|WvߕwwÖݣn9nShYNɦWyϞ1\nL:K??J/. sXe= A?I4O7`vEڴHWg,q:c4Dl{.l6c{F?TW:+m@_kg[RbjvWg6\ݕ3ވ-tS݌XLDo=-xwÒߟ$)ws:-8HAWgSPva(9}N6Fkљ~?ѷ4owwRF6)@wK8slH ߼)r)9e_ _nkRCdlW5Jnȕ8^[ͯLr5\Y_#r6%r<_A2y>qwuVg&sSgE_9-\7~晬"WA+۲;ōF- yzIO朝}s7;j~yQ>^'')_IoFjǟDKOܟHO8 NWsc{ҽ ΜdIZIz+Į$k'QV:O{7eoImҢdq+UE YQ kc"A{+ _<0z?/~~`3䮋-r2 W.ru20~n f`>h3] |ϛquLg wZU(3L<52CQ.s1Om6y9~(Nn5Ɯ9{t> ڦ]j1_Ig>C}>x[ꥮk\Z>09/r_1fR)"Jr}6|!@٭b=B}R稩1ilynZR-{p;otz}Qu מm4>uk;y8)̧.e( ?u=o&So+z֪vK:m7mVc{hGcl7W{ͣ ٵ ĈR4;9>'_NCwu6:_ 6ƾUf |,zXo%?ʸj*~\.|kq8YY)9ѫ+ _\izF'k\; 6Y7"ϗSkLU:3m꬟?P袕̱o~--X$ϲgQ1t1]wŷ^;5m.jF٨朎!瞡 c{VwYyO,?HP4@|_ͅNyTr"wTˋ[6=n/pbw?iyRjLNY2U|%AhuJ,TX y'v*M_,sT/8WzU]!o'e#=+'XmSr6繢 z 5~^G}^F`Oa)+e(ӒOkY& ?'9ӄ<Bs QJ{؍x&<ҝBFpu6!C!xw 5rryA*K`#BGqS $|p!!oɄ!{ڍo+!ϹD7з jBc~%o [hv[hIBXc7.&䘼9& !;=ON|Ʒӊ !;I1y;2BM0.!yٍ<;!"䘼7c ;*B;@؆"!="䘼FL[Wc:wPBO1y 9&o! 䘼Äwc2 yM\Ÿb A1ٍ؛c$\@x3!& I1ۍJؘFk#."@Jxг#[>@ȱ 9p:9cc #wB5% /' 91c?"SɄ mW +^^'Gx;a ;7Y!2&LIxy);I! 'XX» 9q'!<%˄I݈}KIؖ!rg¿8?OMˋasKc,0]?!t%m]|)>2gs?o? |$_g/9 \?U`Lޱ 䣪`L ULӫ`L^*>U1&1yߪO1ZcsU1&Tc"~Ø0&_1~>gw_ū5ݪoW? :oTNfulV:CCй:t|X:VGyި@y^Yʳ\ƵH{y i }ҾVi?#H;iGk>o3iDZk"5ֆHۚH+&*X iծ!ZזZr}ZHdm]6nSiF1vbm]m}66~nTi:vGL}RҾVi?sEڥJk{CG1.tԅ uK,:օez?쏫փzhר6߳>h+﬏6c}h͗o6moi W7@m\?n`w[!yoyy_,[s(D S jޡ;x7 ۋ[[Fr=_l K_s4;@w&D&uF3~9Bt6~Ҏsit3y7z.x.. D]Y 4YÆ~ l(T樚†.Maøg5 IMawMaCzSPl 66CPO7C} PsQ5G}樏4G}ѫc[>Nj- :Ϡ?ZT%A-qݾa͝MVMVMVVV9 ix!AH/i B} D7~0t 'lTknaְoNkػ5rGk(KQQyڠ,}ڠ,AYvie9 rC>>W^l#6CC&O7DMo+o|z~{[|QۯxǴL]7v~l%ߞr*ߚuf_5=Ҝij/WG#ͫH3oYv0yߋ?y~cNC;:ƸbcsU}+@/7t:%agS᣻r,rVNR>Nv;[\aOgӮ3w=v=';#ak]Pu킺u\B l [Jw-5–VrFYK)tH dz[ؗyOWSW.t*wCm+_vtܻ]#tG~^={=qݱ=lw)> {zz=a{`;Nrl5ӝlq7g9 `|5-[=zb/s d׽ fuf_Q7dL2͕Mb|Xa!=#H?_@ݮ}5}o>:#@{}sGsL}Xx ^߾2G눾OBGB_8:Ήr,ò ӼdOt?$C:_/~Z@^GmjӾ%;i=ݥ.ЈXl\o`1v->H3SxJ'0h{ 9P#<[CaPf(|k ʺ@}끨Wʻ@D;9_CQ% E0zFY?V`aRl FAd 6:V OAINd<2A3p0db#ur`s6>+O{'cH4DQH(9b\6 i4oBF#zFh`4g^~P1m12a lY1F1%m l)> [zf_<:?+XBgNM~!Oxuqfz5A8' sxK1xƍ{A]'2E&@o 32zoENLDy\GM4qxʼnQnt$ft̟Hd7&AG,1vpYW:L <͝n2']dHo'0X~b=Sd>g =ۜ)пii)/2yoNTTbTwBϺϧB+: L3˱4OO4}=LۏKx.: FBGHh }"LjHqn6DBH< "MNfNl鐍˅g|#h?>hϹn )4>dfۆvqؓ;ƾ7eqvKY(3S28~ ʼc |p |ҵ-Aogi <%XoƢj1YX&bL"Vyb1&wY?BΒ9Y4ˮ#_[i3t^,ucxƘ<}4FhZmUy:]Zҭ:w-/C5}$6{lU΢^Ndh]4=j>_it Mg .ѧ4aC5F;!X}ga=2wVg*#om}%+EZϭΧGL[OTGнj·b̩eR75nȽ|s!FNQOUv D-=o=F7YSk=ݳXcy׋"oS7 }T/Y_Ͷg;+>w>h*!rZЪ|9ߦuFbBj_O9NVJny|wO,eS1HS'|9Z߿pNV'ة._[X?oޛ{*rEwh㽟eS:z8I6/E0UrD+K3\ CZy}~6N!;q2^o-~PW#k/]'9_^ˢ$N_K}ZVf}6[/*B )B;;OU*_,\NlR|/7h-K|^XV>%z U/@<6]Ejt/yEi}S6c 4FH^$4]>f_b䙡ʀy93"W.V.6/{̆~U{.IO=MF8~3Zr_-T.IZG)F_be5:`ɋdT}k\󙧷Ekd8pMFH:|ȮOWr'Ʃ'HN-4%/1}s4NJ Pҙ)[N,ͪRTޞSDnt,^jcI[Ah׻Zٿ]=?aX< Dn2MnѧUxL`||<_%wlG  r7/LJ-]gXYƒnx#2&]eCw\s^&O45z2kEC]t)6Ysb9]HcQG?ѻTKF_x!|HWFJ^jG? ZK`t._jg)¿,eU.XՙϬ@џYHθc}M%_*j ˾+U[O|HgdAN-iG\s{_o3*ׅj2n+zoxQ/#)comgk$[|;@tX>y˶};]~(|/JӞW<h+j]}<'wZnU~J|(b?rTFr%i8s/n,||[#톹"*n"џco}fFuȚΟSl(j1dMOF{X4 J=7a|| u?B$/̈́>]?eCTyBӗlĚhW }B@>OQOxFPڅhDЮDڍ B]Sh MťОD B"z^D:;\F|FWyhs[328\vϨ+s\/W|SDiyUyMsW՜u~VϝDIOyWhދL;\UW~h([yBC+0/:%$I/E&a~Qcu%IF4t!W9m;ga-kޔZo\ob7֛u1כ%zoU $}=*8)YNZיggKR^m[pMWJ}x֎σ6Qk]a?Q>czEKY59Hd/SfzFYzPW^&l_dXg 5w5*Ct3=)-uo-]olF8Ōn ֕?2R:9:x~/tf㬶YmB3ecyNp9P~ -yH0#y8wV 02=D{o7$;P( ŋww&ww-Z(pg̾;9{?}t7_]b|T^!Ҵn;cv(P1ڡpo~|ZJ~LoR EW}uYm3x'r\7 mw1#^u_j|>Ef;p>Ɨody,^tENzѝMˌ ;*a01_`v@^onp_cZ(?K{*],#1V=}ݧ8xr"!H J ̟*-V*-}ҢN aIa7h+"-"DZiqCShn*-n*-i6Ti6Ti(*-EZiuGW!mr,~ZK eYn=zW'nK1F`vn8r\8`c _|N4tUa:U0W]o0`*vWGotF%3Xϫv_Ck__?}0nֶ߆X|ucOէ-wjY{S1<+hLˣJn,_xgw%>s17p~ᢏdϏ zbKz5[NPA+?׊M H("mrz%|L!m+&KQmB*U3\3Xju 媏;Pws ;JߓUU4^Jw~5Yʈ?+}䧆sN2OI֤u.szIkDekLzu5*1+#LHnMYǁӵj p7x9jk:E#SUSpZ!ߺQC쟥=UgQDgOd>JU>)ֲ<`]ޓxLӽS*qĺx:?N3v2Ke]|촌"^ո`n|l*>FgcQk7\LP՛"k]d\Bw۲ņe i˔^w 8XkxyqwuY EKH"]BDtrCo06 41'mi|% b#V!:.Tw6,yIMl'WuwFHQ|b9*׃eD}Ï[.u`m^(T<8Tۼb9ֹ?XkSZwcvf"~oE7oDkV[cV^w &jweл`0" BQ&'-,[?vk`7? m9Ug^z3mvrsl_ ?as *-ְ=;9gax9GhO<i=qMU֕ݢݶ4D[]UzUz.0 >[J[eu mĆ^6Q&jٷ5`Wİ'֏:1jGG1N }Z'Q׉ _m<{s \uKu\sO$:. 7vf~\~Ku( ȴ5f~FMV?Sy+'G&ߘW1uۚ=/X[3DO'H'XO_iaƯ$'&[yOH/5*8o[{YhYX֡wm3m[sdN¬Wr6ENn3WumQ׹v2ٴ7gqeǍUQu-nNG͹KkG;mLRc/p#Nv$t{ZwM`M]`+ wϋSNm{O*H3n7lsnOǟߊkWJ?c3NИءpc[|5G-zͥ~,;LYM*2l*l :wcFC?. ;hl-C~Ppj(/PP?j(uS.r_~ x*# vF=P؅cG{ni$|ϡitz.U\beb|cU8Һ״3ÿmMպah]fP[sWUuDcpn2 8ns/7&^ip~hx~+px[RB;̨c*pS~a68ty[ȏq}h׉ݺu u x lƅAa)k\^Oh;t?ӽ-mrv,,A\So/3y[pXjxN|DRGty~4>(#ոm%Y_ 5'r9k\Ku|,yߕ}O+pAW( xN|R'7u؎Le">_U*n/ڞxg 1oOx~)p+pꟅx pV wxuNwxς敠`E~^%w*t{PVO}#"-\8X<+/RۼޑڼOo>=N%6l䏰y_F]6l䴷l~6ؼf6IhCÆ> }A"ʫ Gz5bŸ;3y_%_>A{84`;Y~!q,Qo@o2B~n^[vr[1\;v3CNw\;]XFS;bG5z9N~"ߎ~?Dx;7$!,>& gہZ:NP-zGW~XqTfyXNx] a17eu豣ߧ2 1Wmn:Zz=sc=Y 3_Y=pu,z83JEJ//үү5үү#os~>)௑|en賣/1N]1ȇMNA%;&w`{eYY)G;O:2[џQ!Y/\A}hM'O:ϲv˪}c /*o"^b{,s+zxseUb=w1wj}0^gYczp,zxea=/1гC9˺z=oCFzkWxLh=^cPwB Y}8TzYVEW񋁞&'ГЁUϲ$BO/yI2B_o%gYYO?Rzӗe^c}B_˯= `k/З# eqޮP>ffٕYo7g z'Г7zzȗY@y ,k)$.Yk͊29/!SA~U,;}YWZ v`g%ߓ/XV뤧S.BO~:K~-VApԃ,$Tvn~N7,*Us`O;I,{)~N`YXBOOzmu٢ǎ E1~TO}㼳<ħ! wkybYXϏBOXVuZi[ٻ~E+憳vL?;uxC5Ϣǎ*?L]G 4.܍e^a}>1k/F}?& !}bg3o #U9Wn.xo̺܌߻3(11eMd=ㄞ1cY֚[CL駈xL0U)gf ?۩Yf[0K1<!lDz沬*?3b%1?D :Hs>gY"BOQzi޲{睄є7z ^Š#߰a2"|_: '?J))2fB7jgO*BϷɃ2.w/1;5>%_;~ϓ?e}z =bP_;3uXfKPkxyO5`Y8wc_D;^d,*k)]ʁui}҂e޶Bo;zZvz;ESG~XFYU|/O.]XFk|;}8Un齘g:E^/1A᧧~ }Oy0#;7cvC3}%=Ag05eUg=ㄞك,灘~ Ӈp9'd/p61O0 1[ZYC;=# t\9](.=-)O |BG>,! |@8Bx) Mox( Nx""ŋEuof3/ofPosez 6>f2~:Xaܐ:J32>0C(90لӪϠF#O||8~s2_l|xF7sXwxן$C521sVdS|<ʇc&3i72;q649zuИ_,|>O5ޞpK0],c g|v=Rzu'K Qw"{У 3}Wn̗ F}e LzLmv{kQeQ|9m>XoNta|Œa>\5>s{t*d;?0a_a>Iu$#Mrzȣx%=qYߣ>kӭ5`s+C8qu )҃\oESgb<<'|qs|x/c|=ԍo2o`oqm`q_á^lW㾋T/;|x5sa퐯*:W!*6S+{a9 Yۮ߈Đ_95vUf%5A~}qE}2rS?a">ދ[; L=m Ž{(0_{2rSAR:yH/WoS".n/|<+GMP_]Cy"Gö㝳 D;h-0O j[${M|-R8uoC@g7g} 3)T+G9kó32+cxÛL3!-3y 0ߛ M~}/CݥNI37,gtUb G{ MzY2 ~Aï2T%5)%9ʟpdBr*cߜ9|UDZ Ի/ YC>5t: 'ų2oIş=_wN.(|7a|Uּq{ G2=['dʬ3Ԡq,%g-j˪ Yh^{pY%@rtGID+1SAׁ>Dmk3`C|=AUd6㍊ӽ W]o=+@07L@_1fuj_8Ww3ƹq8EK0yk)c=.ccEMs3ƹJq-c+W19ƣqc|2"st|[861·as\87ʛ07asI(om qcyd}J2ƹq>d!c8"s8_&6as(3ƹ~q&c/1Ʊq?aC/`c#8b86}13?dcǾq<81e8:1d㙿21׌q1U\?01:q\cWq$c{b1,q c1G dcL8nt1y#:XM8R1b#aqb c8n1qϟ1k1ƾxؿc?c>b c4cGc}71ն1ؗz@0YB?$=ո5!ȧq+S׸]%!n+8 PL*qy[ &Sָu' |OEDONVT|Ws#m U" ik\T:xgG%DS$?طƟ T/ Z/ ?{ -YYjCdT88xߔ=+|.j O?)Qb @wB|UkcHtmc)/eu{NMC/'[]1f$/-2d)7DߡԾM~tW{|}OSC>i-wȧQ+iS{zrphg뾥4?x^~뾧,K{S{|c_px|k*E.'}O?1%aPb*1;{J\_uS%2ZWY{0-/boÍuoW0*fdA*|?LӽQXnP{2_3^Y|-у^>axպyו9 ; _bZdX*W%>=O{~nT=l=S/amgj5AuO7azÁ?A5gmA'ӏro*7&j|17aퟚr&9#S?!ucߺ*iu~p׺.cQ1\m)?ݺ/^?uaz1O2Tݙ-A9Soy pT [VSC~9̿a=Oc̷ѡ_?&0V̿#á?e9c>O؉}XF0<}U2>Xce;ܛ9vv; ?zU'~M7qa]Mj39|jVGO|M˘ I۷pӾTI޵*/?˹r* ^jUS |#jrguyת=p،*z 8:j^+?@|(^Zϙ~~8x4fm_pjK8207?W$?S}@}uՏguͫօ~:r@7nlS*}$g熉1'ěAR5' 7eo@<[an`6q#Q;H7cԑe,o@* 1O 6ߐ 0}g ƨcٌ1A}Q~ڒ |xoboZCΗe}hHaK (`,nm!xQ¼er 'X1ڽ`wH, gbz8%LFu;5n0n=<(zߙP <ƀ'rPP` DZK7d?t#3ovxшc(?fc(oUc(?(?'>Aq|  3>A˨b>&f>cg|gq|zIOM3qzUgp[zob״BzgiB6ބlr KMt~i]M61=z%gginJ<0O$ES߃&dǴ+vxg=)Y/?SOM^,ۘNӰς$FZopTNo} ,^:?R_#l׌$fG~c7Jq9J8ߨyp"&g 8x<Ԍ4#A#kyqQz!.X~oZQm@]){Ov}|@MNx46} d~K~ق~.S@3:75#ߊ͇1ҴEkΝϱ|,σx TdƜKw9cKӜÐ'r{Śf w=]c~#G3^f輺 "h GoӟAƗޔoo_$)f]`3W  |dn‹%1 G 8o 8cp^}z,SA|!WyCܻ0;5cQ9Mk;zb04Ztܐe0G n#Nx_8M/ &x8i \/c)]#7\0g WK!e+\>I=y88  %82`Y\ Ivc<pl}Ozס[R-n`[a?ߊ±B,S 4*muKȓ *?m[Q}yi 盋a~H|2Mц-e7۶qw~؟)+7p;rՋ)֒bߑQȆX&(2klAe-)i\V^rGƫﱍ7na~0{\:g없|Bp]x{X^r¸tf+jOnEiIך;EiVmM4mf"ӬiM}#Ot5\mO J1ǢLY5P{]? ɞ҆ڐCLd99߆]w1Nc_^m o}`~3QT˶TNp `E}C- (_a~1Ƈmi\VebD8nn<=~jzmܶnR64d($5aw; >j2deA\ac<-ToaRup-˛{f-Fmu{7clkc?kǣ- v:/oE7(/7lGys;ˣb"*Ay 7qjl|P@8Д2hulW*;8ւ[80c^gyc7Awk,M۝S;\ScScW,'1ALx2쀗3x>2*f 8NYSul\Dp? :2hzUЗ?ڃ}ڡE;lɬTXjpUPGlh`mƳMjnCCϋpzHՓ|BTK2V'jVؓƟCz>Ŕ9'=/I:|ЋR^䗚y%[M[E )vVw6pS^NEaҋx^|\[@o`o֛~Ks>վR]cOb:6G-8 +v%%T\ee2gnܷY d5|FwcLu1xߟgoEIah"=GMO{ S#}Zw#Ju1Z!?y]Q'%cr{[ݪ(*';Wh%z2N͏Kݣ 3p~1~-a'_|BWB#4݀ht!^BQ|E mi7DA_ѢOv8v4mW6ˆhʆsNU|~l4vQD%ߌ&6=]#EYV<g#r' px>Ooކ?&m+uqy@9!+UV>8g]S X/}B=U '|^q] CDK-$ޣWh'}F sFǾQ?=4@/ ༳7庿Qm(uzKj붰h͡ᚌg. d4;ƺ{)`=N={Owƥ(k^̹$ 4|aG!gqPw`dqsbO <4 pPw.PwpAEcdRFZ Oe*}4HAЫ 3dX Z"f"pK9V߹E-(lAܭ/M|084*} i Ө ˨8ܠ0:ƣ2{=qrPKy%Th5K:?y dm9y: 2̖px" R_` xF΋7"lYZr/vˁq!:aQ.J31µ[pLkڭ/k i!.?d~jܸFP[q e(FZ16)e3e${cu~~*4h|&Nci~M4x"\Q| PnƸ@}k;AF=\ 89-aޝ$ pUƸ7|%a[;n1b0 c0(x$ಃ9\Kj .\ca8pPB x(fc_3Cg0;xh/D c!2td>x lGT17wUx1QҐ5U  S3 yiۜl=%9Wp(xGx >aJ3S[\o}fՀ 7B{LC\ N #Gc|2pLo&m/'2q^!!fC8%x6^`l@z3YyP|R8#c`JCޟr('psGP_SWp~k!` x'ƀO2|Æu?r_g~8(^cΦz0ʓ! ~Fh8dk7,2#/ƶbqd;?1v~weg0oK0 }SY e|8#@ P? @WޗxnY_^_+Y" 3; gfzq)6/p($;a0٩venxr%>}]0~ĺ|6x_1ғѯbocxSsՓ|fB㹽=p+usPa S4#x56#|4&`_a,e\0v^q|yrqe_Aۆ/#lmڷ ~l$yhLIv W7c^ H!o~HZ>)|x+w* aueMj,AGyvq; 2o^3?b6q i?jWvU A~KVv lx1ڰ#pqGmBiXmeQv>z2/z8(nĦ[86fQܶc ube1[sƸ>ۃzub>*tN$?-gNf `Wx(^W'Ckc7!Ehusa_ݑq?u:oToF5cB,W5ה1# @X\7kecc/tsSMG7"#8&l/7 `>T>3ќz](Cu^DSGͧ^k u\91;c߫ck4Jk;/( yO×y;jSگac&M(hc}pjS{   x$(ّ<,U_5h{m}qMm<(5c\;ߺ~?_׽q1C p>DQc}>+X5xq_ӱ 6y*;g[ic2a'cq~_q7u~tiM8635J،a\LPq1?xGn,=<^?p~[6 psǼIh2Lz^>$vq:鵜Ì6Ǒ1^?3F?p;+qoh 8n^>kxe?x-9yqo'UZc{&xzŊ 'T{8< >nܔ\pPZ\05 kL g $oEuDO/mgZOHW+1u-1_tWOD߭%B+> [ΌzGd߲46嗤]x3؈L+;^EcHtAF>U0l='оi` ~s9ߤ?L91wW >7W?ýÂu"}M bqt|2:˂;?ril'O3Z{oYH*!";{R7mu |y/ )WgSIQdؚLb&QؖKhLy$;`^:ˌ>%}$P8Ec 9'sYՙ8oO,x9` JSc>v S#З@΃#32 xZxm0}X,~Β7cw#r1A%i 8v(Vw'jp_1^V(sن.jιħ}f!M>܅1]G{\ 4CCiPn?r{N|Kƀ@ }a914]8xH 'ׁƄouNFih,aKEC*kw` BiD0k1縉3i]4miZ{FQ~?f{7PeySLLae5!uS8g1||gŐ+?Gg&g[-8nǃcJo,*3)=Sx 7Ō=L6 {O pۤB(9X?=E);dNŀ"a7Sk.Zwo"$kX+3iK^sɯ)ԮN^4W.-ܤߙld42 y$^S(&J695U4_M%L3i§gx~:dT]t:+fCNf\!̿OqeO<@kK\c rf 0MAÏ~=zگ6>~N(x:kh$6[O6f~X+PW@h?2 |1ql腯"'9nH^3E/K3O4:$MϠ̠Ϡk*Ϡ~k0CezϤofR96\F~ek^?։1y jLBq3uX168RXL_ssٸ;qL_Iuq?G9YÙϤZLx"j?1zr-2)<+8\c Dg0Nx,c?_h/?Ӆؖ"ؤG["7*iy֛U^I6AYJ27o΢ҙi&2ͺY׳ܞֳ)φΦs6sg6ۃԚEWV`sTzͲ|撬$sIV\QcC_>xEy.>;䬞ת0g5'{,=й<}S7,c;~br|yXX+{:K~/0of/G27 <\`h* k%K(|)/R8,pN_JXJc)R.3J/3:,3,;/;.;-;*/;,;-pYNxg^Ak W+H ;w#H{6晃&wl"D~& Mk7Q:D&ʗn|b3B)ko﹙0m3{fff##c qBwv줧uzC)ҝ32=C:mƍJ6Xlp|+Vlllb`6A6S)s;a6IOv~;,Nzn'= vȆa/i+Aya;HTFV2o;(\7vP{X;mr'w'Y~' 祿g'gNZ[wvҙvҙvҙvҙ.:S3.:S3.:S3xYwi9dEqܼvv-c&[M,lY7ٲnʷvS]ݔo|k|f{(Vۣm!OݣǤWѾ%?T;صZ]#_6tjoB޵h./{!ړR:wYKl|u>z&c^<dwu8v ڟk50N w{ynƲ[lkq}LoEMrd {uO>^>'GP~-^FVY[ZAco dk33oX4t~6Vt謘L}ӱt9'HfKlOE}|I_TQgr)K.>י>%x+~7|cw_dUK|i-+t@վ'[u6C}w;a(F#{~eO7~j?zvOw~ݽ|{'}|of} Pj3t3!( ]vOK3(Aj;9< :$L:H6X}lp !ACdJ F0`<ĵʷ}_r!tE^Sn]֍^\Ng<ϣ?yHƱ̧)ÆUa0t 6 {? <3V9*Yt}=L̀qkx%g{`s3aRq໐W9sIi [;)l| =^13g" }ʲ|oa]WIlkm988rYk0=:Lh]?&?B+{oF#TF EGyN1#}}D9JGQm =JmG̻y29Fsq~a =tŹL]4 L]C=!⼨}XT})^.mmcWʊHW8RǨ eB^ Q?BV^R׹ *$>6uOHlp[~j/AlK8մCi,V=ScâNWG{tdŽn,okmS?1}C l_EWUJ2ϱwCޟqQcT3A|t,aQjǽ'#l䪰wfQ@7X`ӬtcnѩA(⹝&LN0$1pSgWpUv}ZCwU)9;@NqLgguJ 9_rK{0Ƶ-vapV˳՞{G%`{U"<Β=e 9vYZ{b=t(9=MD y_MgI<,s+9-?9mt;8MQPzW!'<["s/wxs#wˤ_8fƸc'jB)+".D\d\~{ecx}p]nf: Àwyz^;u/dQeJ^Dɺo=a ~w'dg(q׌qI z ` f/ 4gM>*9?ݣ/Tj̠ \qoQt?sEc<g%ߋ 55=A տBmxuU)z_5ɀoQ}˼^|atCAڇ34 GFkOMw]Lgnw,*lf *.Шz ku8(0/0'at?On`}cwUɇ=0g##źq4Qk1'at?6Oh/ٿ~&e7.ˉgat.E:ѽ=߼"{o {{\atoOEuZb7޳p|?$TqGzggp EW`wf\!1 G\ p~_p{ƍ` @Ɲd ƸeejL? xӜ|29L'gLpyb?7zmC>)i͡{a-F7 ]sox"% |@ |FMy2WȏfHG|UȹjZfq ܰ+zl`yEB,cxOSy(_gU>2^hShC5`9SÁg}t/:~{ŏO %.>W[Xd20]"]9&UKtyF-:N_O 2R5M>=&LC︇sr<Ï wEy_oôRk>Kc1񜼋Lo]5׈gȽΧ3و]qWɟ~lMϛ]l>;Noڏ8(˽9z3a+ϯ&~MԝxیZG}g1}EK}"9PnQdR.Aq( wabgG[-zSO`@󣲅 mŊN*|CpCUI5ů:xH" L>%_`@v"`[TIMyvOXyO ,Cqno 9YSSiM+TaT'gۛgz`>gKGw`ۭy3][wr"N$p0>#Guvdm/Hȶ [[Y*BߺGѧq{.w0m7~&<BYf;Z1[ܫBn/a%NS䬁 Ur<&dzo{9G7WCA'eOM4fcoBxnS?8_ƸK=;I伯VOUw.21BDz\6j mL19)y(o\'VB[Os7OK"r]lH U*߿-{EKVyKO8*Yobg/s<[^Dwb׋Z>`"O5VJDm W$;}i{mBLWǒJ ]8j0=o-JYg[Jc>S5ԛ\*0V xx>q0Հȇa~M@ |7 *3N,xob\GԟMg9?8-~QѩKCu"\.pX%(#|oJ-U"MCE`Uy|]F^Y +%Xz)Н0_6zx5evt+|I5ZӚwg`6]>hFZ2}}ׇ︯1 0-T~M){0zB_HL?i{ 7C:3UzcϿ6jm,c >|C-Xf6Qo O sX^|}!0t1o>3~f4k~MyT&q}Zm] No/pkq@[_jXǔdxwfe*lx'&/-dCa-hp3ʄv:~{aUZc,ϧ̷o>Up|[-uԼj%t?6@2wy(8߮Nkm'U}"r 8lxkLσ.2aC>p@_\}suC/-V³$е<%ӿ`SuL ]Xoe$:j+wqr̳-*x~07}\_:n{`#n o+RN$.-/G~=sH~?uAO>^]qX''M@ϟ@繲 (S;On2}ČQduŴ:y/E1Q60 ."941S>-> 앀Nu{}eZA~/㘯4g| yWKE>L蓌a=0}]1Mq8YF`9Bhjߌ.[)~7u/ k32]iQ-z3q1]sA'ʉeӵZ7EK:6|YY僶2RվC3c\_ǒv&9dz1IDuObʛ K=Sr2}?AkvS_-~%[/+ݛЏa.Gp-_+Mϴ=]k%ڞ,8|&Jlcۊκ.Kg; #{a_SmgZ11v;1/U^<.*j\hx/3|ڤ y[t/id屎%OF|ͤ5QkaX&L$vʐw6oNeTq /m̿& t-IFmɩMErj<։OJnIw׉>yrc(SXw%ɵ'dyƚi g+=SUwfeBm#fX ',AztAGfk-|@F-tggG嫽k|&Kt|LbZ1"MUg<)tH: S{03j5nSS}QtwC\cc1l~yZ:߫21ﻥ;?GD[i r?u9~ûVO4uwQ~⹊V{z2r~*b<##;z׋7]{IMǒ^.?uiOuTU14GU=vPryZ`iCcq Ϗqϟ A93<  :=#kҩ,ޥ}0VgOtCu!ӄu$0 Il:ܧx?O >(`]7c97i_+ȷD)p>_ -x՝_FLoqy6y*ϡ9$uȇjjt-Kr[ʐկ^2_/oFW}LO}!LbQp v`c\_BH,'Gi4 H },'2'5m,5C90n_ﴔ2}^OmUjw l/oq8~f> h2}az)+:]JŧuWy/E'=ꊿX^WJN@^R3C|gL3ZU9lpc/m,:]F2_9-M1g~!L6GsL_8uGUt鈯ȓ. 5nxQ+}JMb7~NLWM%t~qLW lcD%%1L+L=?8x(鉮%U{93|J}X [^k|Zd(]M0a7c]qmkbkݶef·vxt6P/` ^=_9E_ly[|,c(g+|E~}NWA[wA˅ʯr1X]fw a:D3j&2)1=nգ-eiɈu~\NTU@ eU:{' Q}U_赳Q}|3|;6Y"˲dF JWms72 laBa_FqWQP됌?Oəom`Ǘ'Yc\2ӅH5]*3kQ~q/8"c@M9jJv#i}2Rߴm&6Ư>#>_O,'^s#Os| fs>p#oΝNcre G3L4;2S()zޓ|;ִ=^Y"ͮ/t+Y/B(pö=PfqN1$NYhl}uJv|'pHY:o6YI4hT0ר`:unq pSV3%#+իH:kGb\3>jUONwoj8"<"  |Tb 1p] 8)j.XK"&b݌<o=e 0x-J2*s`:җ kv?gSgW:|VgB/LJ:agtcEV?95>DȦGu{Vg:cY+׋ى ݣ tOg̣Z*1ݟŽI3cs>8ヘچ!F3wf1sAڊ^͋+LgVS83k˳6N@s/a ~fAt ޴ y=9"0oDkh {F䱚5c?{=vPu;#>VҜ8g`N·WL`v܍ؐ:NN/ni fD3x߈ӏ~y2K|IDdNG}x^%22K[1]}vS.K5CmIU ̈\\+tiE<\u֭ƹ(m0 )(}V 'sEv<_ߙ>]Tzl KkBox3,|Mـ@nݾ&p Ƹ_{U/?/`< 7"r2"8w^U?,3n/)::>»8GYOAGQC 6ʫyOӑf=jzvZ%aHkCy5=,(ˋoWcA|][̱L%7m+}t!Df NʏV5@N{h4fy_Z|#Pɋ1>fӗ0E +/+^A|j_AM$lU_RFT*R`8XLl[NJ3L_&>41_=˯4Q y oz)M)q:;0t ]ͣaWcCc|JPt-l򂢳4{Lǟ,6;CS[We@ׁ1ΣEţdf."8fg OcnI`dNRO0OW{UHW݋g>3&P[-|>XJi /a|QA>&]p^a|N㦸kW4w WyxIZ8x>lA5nat=K8fq2C/q+t1FyyZx+,Qd‹6@kn 4?)L`o}*xkO4;j}'UZ60 7hj ǫ- ؟e.NU1,c8UYƵk,7e90xEx; |?mϐD%.ߓS uw`ZƼ*Q~Ab|G0HsC{9 zsK4ny購69#vAϤµ7< =<0zj?RZ` \;X01v5pC c;B?-蹬'OoIYꔢ|,516Ae?D|RЧ/ߚ/Y~1_u.D̺IIB~qj0J/__b[]^OFsﷻ(ϻx9+lL`vB==UU2]LP8Н'S0Fgf!Ðp;]rrrʅIv"j׵nZ^zra5E<@ow]n?^1a_-aŸI%,bIX| Q;fa2mG HfNl',mQ60_#NEw~*]!=pK;\ɇT{1{YݝX}ؾ±AIJm"im{+⹾VcNȋcI-NjP'%עM,EuAiE?};yW@w=N>SOY^|z8&ƴ@y>Vc1)ynO~AoC tyU/O0(]G#~xuI%]:i-oQ?lM0(V7 o_0lʿaFmz1ߖ11ӣ1o\a~rZCjW\ލ/jq4{")Vq,X<G7- 䧪#o;MTvFc8θSy9*)56vPcSU9h5c_ +m'#]Y'qȱ4/54=)zOw9T~\ß~>-μw%>mUàڶ{O_/tީ%yvMq:x:o!f"l>pi]E~8ӷp,IuMP,7 2;?ɩO!> :dNwaYlg+g(Ηg,oE=~^ԧI/l&ݪ51A"k\O7΅-ּVi1L;u5^1r*29lne ygE/TY0k߳1+O9rNw=4^?}FRy^ ZۥJvOWeo8 ;7vvvv,n.&yθwOK%D]J'ZW:Н1~wgJ>:ꬢn?1]/2j=uxÖ>5 Y[΃(*Ã͵&o܃k&ǑHWAL4h/bO Ff/5_J/]d,vwgZW&{٨iؼc~yRg)g}7ly}ү~_6G}^6FyMC壦$hV |ɆfZ`~4}k^Ӳ݉kysr@[Ч6y?.w+0&jh64smy}@W]_ثrrؼo~le{&p**6Nlo>Q}Ol߰yjؼa~vU#\ȗQdǦf~6w~潏Wm䧰ořn"o{EqAE}>O<?9T`dHn![QU6+,_ÁWcY~-!*?e OA~ѫZtoZkG߈/wHߘsw[-{}+3m{ o/ҷ|c߳B~>䫽RC';n}Jug_E| >&;+T~?~_'d[ Jz2sBHSgxGYV0#e;='Gn;Yvm;A@ҧuƳn5}C0!T~!t凙~lB Gg[YL?_kpM쬝βB~S\2t'.WLZpEoU,c#_/oe)Wvֱ̓oз%XӻIֻ]r)~۔HngO ?bE}/~? o0(_8?DzAz*{H,wωA9˲jKBAEٵuǍctƲBziR6sO@{W1˲YBϣm=dYY穃⣞?Y%>;g/!2϶5`,;Img5/D8ATeY߱B1˳=vvz߳N{/DLӧwzz gjˢ׎>OYO{>WYncƟXe|;g -%;=YYV:'Г{㖥_C~2 |B_+O˲豣dB"\c~_e,_eEL_B|Cx߶Ɏ>s(KO^/Ng}* PgY5:7Lߚ髈p}\YWf#X_uF 6"xUcY XO-v |Dzv;w萾?a")WN~U#M:IOc^x7U-ᲣoslCw;6vr0\\;}wMpxaX~w! r6փn,zu/O]<]/TYcz~y;R?rg(ӟqH?_3(ΣcP=dYqBxzTS},*z& ='Q#+ef=1S&,z=Uz@O9I9˯Ǚ~ӟ:t=I٢ώ D'jܥQˎjo,__7n.5W W,%/ |A? |F>-)O |BG>,! |@8Bx) Mox&F7^ux^%JW\e/x^$B<_p G89.p6 Eg888@ F8)N.p2 D'8 /p<+q-K`W~'[ZW[g%S~$?']%Mƪ -K-DNI6X6X/OE9jH2;p+ZӚm7!y#1 |#Srȧ♸1aw`9YW`q0[|kv"Cg[|^6|nČMo>u [|vRpufI;_! Vq̷by[m(ۨbY1o>ug̷&|Oה;:(; Mu>h4r:h<O:fG6<{ m|Gm&ǁG;T.mT89j˘r1>4OΨg=jNSK|mRٷ]v(zzZGА~uSOgXx=,Tl`l{q:ŵgJ:WŗG~ij"0slr8:WϋuRfyڽC>װ1c|Wăi\[gLs ϖyju>3aE9xF$M܂_\qվ_i͢ΆWŏbw.Nk}`Y|Ơ}kw0|x/-)|9ԝϘ/gYɀŵ~-3uP>(YNx'e@ٍs?(y?{e9'@wިLC5IN{T>|7ޑcEۊRqfWs {{}XN*rОgњN*,'x(>uRK0]սOΧN_ч=]UwU3zWTٸC}~Ju>TZA꿗d=3}..L=gs@鳜?#+L_x ޴mtp=R+F{Lh21wm_VThnٶ:lO~svGtY;WNCg}]tv/ߥnP\u߭O{^6}?n9~t?'w}ݧASw}tA^t}^n;tmquzmGV>Qvmt;4s'ݶn/6ۀ!t;nN'6Iv'LκSn4)}:GXgݞXY wRg]?tuu.-Eש-rP]뵭]tt_wuwWuվO}Ȯڟ}㞮]}ڳ_}QnϔUnڇttbm7Cu>$vwCrt>~wCt>dKwm?k!{dZOC>vOC>d/C>d|/}Hڇ}Hox]o+.־§~~ߠ!Ch!4}G}W}#G>a_CR>$!i2!ir!~)__߸$u:@i;@:@+y7@۴);Po;P 4P}HCjh?Dy!ڇ >I!)?H΃R% {y$p' K8n/p["1F )p  <_qE/[;'l"p iGvsṘ0X¿el3*+im驎#?rցg/i5w>9Ssso홮}}9u$gpYԳ3__rx(v듘gme_|Ec}1K5?6$d͛b61IC'7v'n0ϙ '`_aӧp@q_L诋gyk,6OkQiazӥ>㦐fc\ g?zcw>:M𷰣H7EP_`GR?M]oy5Y+/#t:2;',4F}Cwy|mLW9TZ{e}s\k-po]~|yʖE`W9Etb'LWl^uZ:֠/ uJc3z3 c/MjL ](t_FC7z3]9GI7'¶polzMj>'1_y[2П}(P㔌Pon0Tp2ӟ͋lXN3ozO2^ e2̓P:B=0fa"OP ES?2+&LW&N*M_DwG -Jߏ~j,l:5Ջt;"otZZ9b[_Ng){A(,%-8tvq/SNt_f;k ZdXddI=7 h燥\mq#25/b{.7ӏ`k29MOQ<վL󇭁sU7˛`9˨~_uL?Iq_eApgpQ5'X Pߟ"Eƿ}*ЦYq!r <_!_3~ gx`~{~_QQ(7a8-6}[>/j\a@x%M qۀ8Ƶjv2K臰.ikQy]7Oq"T9'SF+WRX7%1|~ =މulFc{JW\Fgtc~81o%!\GTQ;Ho!h9oJ*' ; _y<VWҝXv9jV*mSMbݿ{!NEZIg?K:t^E+h[h0wOVX?Ǹd[辳7?cyqAet8ihoWݖ_Uv#`o ]E9ue~^Eh?/fg}7o:Xr5%o70 !jL,Zݪ.J!c0zWȩ׸%ViҏevD,pހMkܞNJJ໌﨎 9`̠Aό#Q&}_x5`/>$q8ݖ)g 3ӀCDqm,#{sZez=}%f5 /%NRk?aި&r?lF~U%9+7+__5o;QЫi=/r_ѫjkY1YQD?3?byg/81!=JWOyOg٘ ^Fn_w{*&iz]a;y1?r9;iSӥm]tJnL`c.]xd`1}`Y|KN.2_fKL_G0^6mqu3j[t,= u7/iL_1(X/>woI׌2~eDŧ!yxa3(r3_ ߓ 5=A [@P=jI| Sb-U|*^ ӷ!o'$@om)?OaX? ₴VL}|̰ag6=Cט!t0Ow:ò1wx-.uKgUZnOk܉8ʼnHqZllmX˦q޸^mqR鳭xV-jԇ'!t'm ta%dӶ[I$,˾˲߰Hvm$6]enO3&^3m4&eOGQ1)?9&q!9X`לߦ}CGA{ƽvɷB5)ҮjX?2xxOOX8+9n7:lf0K>9I[)-_o2J2;(-칃rJˍ;t>˲ v;Hv,{'.dI$!;I1KI٧T O4,I3NkxiY0 wRyda>Å?Qyks+Lg < 1Aqlȳd&F;ȩ}#9~WF2 DY7>z1.;?Éi{w5SNk^_o(w^.D逾NͤƲ8.Ww^,?LH m < pF.S*9:ym>1u2߄rNcQ4ФϮYǖg(7xə8>Q/w^F7>]l+ц2/4>#8pY` w>G>Oˌ?47CP oȸٯ CQ`,+ڸ;0Ƶ85ߍubZb VlʧgAOߛŲ0$aX>mY/^dW{"LA(K~*8R0 } {Aķ :0[32k~O&0],A4 EL3_돊ץ!63?UzquM/p!e|R.%JtIAm#]oC.LauPUzti}J 7=eIt)@!]|Ct|MaOi=BiwIy@^>ekTq.JO}F7r6>B]Hs Yg5x=eң%^GmmGuQ6ө6Q + r Y!s9(_G՟ ϧ|3@'x~](OZ( tx;1^Cx|#+^'1|[l{<ЌK#|\P{{ŎSqU=Nǩ OPm'F3 Єis ?>Caz6Z$ӷQ/쫕_W}D/ř=$ /釱aBpuFjyn3?.ciϛNS~{s"I!_y4.M=46ӾJ/hN_NGnǨ3R\c?`izVG7nUt]OkqOQ%W}<_:goD3=-7sWΗ,gUr hs͙o _⼯I5-vB ;pHd=.qM%RS^|KmCѫp`6>1g6tw{]Vн^gn~D-iL^YF}Mzt ׷Ң̷"U`@`ig"XNLW:]=Lϔڄa?eYʟ5x oIt\1)K[[?NX ՜^19!_o Gk{U/|,vU|;9tW,@=ϱܟV}x^ Vzp-z1o6x?0uA'u~g%}p5?5m)WJz9߱yt f{VK!}LҫG[nmZڈ1Sxw\ᮐAФ>iJ4i3>*L;cw|&_K=?}crG”2k˨x/]*OBw{ kvJ3 c?ө6k 1]E,WN}k \}_c`}l? F1@T|l9 LcF@ cY+!yƘcZm|8"B {< ߣ.bzUzqI=.N=&aha \U-/n9Z)c g_Ex/t6ЅY/C.`0w%`s9;ͷym'6o6o-l6Gu1 .{mAqiдg9*-Dn%pk1*Xo/p[7_UM꺌DqNc.#_Aĸ62kƸ&ȩ8w8 a˰͎~_E}3?ӏaA;\3LXMXe|tÄ?H*0?ty{nů(3}_J>!ӍwWu]x̾^`23uW ,mer2d =C Ú>ӇY]]*P#M6gj{n~K8aL83c>~#-tQ=Y]K{"-Пkߗ Yq}E=}&_mW߷8..s[MVL3p|*i585> S3W4!I"HqFDGPA <8*W,@U0: >Y{痵^{ug׎'/s…R7ݙuzE^i?`,xGs?Y׈fChq#hk 8_?4tҟ]Yo|,3#u3|n*k=vHT]tm㜯gO7 >~3Z}% _lilE]zZ{#оqw.{>첏w<_߆`ӏ妷:$t](\9Xn[z'Ժm8F?YVZ= _(˾!_ A=g!z;3z^]Whm2 R9ٴ;;א/&{(iY}5jME+G˩8MK6G4G+>ukS1Y׈g[Ok/>&fH8i,,FZq A498OЧ3\u{p,>L}w_$3~w,Z6̍k> {Ŵ_PΘ'vC[+؁>v|ZԳ{c78ϐ=:Ǣa;C몎{B΄DZHs/^oMz|K_7]ToO6o,Jy'(J}ҳߠ T#V4u-c1},}EgzK"9dWLn;ێߢܴQ)ȌqHӖ>v(;|EovӇX1ꋢ7D>7z:Xy+DZn{Xw 9'b1(s>{*V|B|{K8-'vk\M1ɘ=s|fq}ֵ keivV*80suמ4Uq۳F9@ݥ-|uZ? Fw7?qGs4=Uqdzy8@k(T&1]/~Mbzamț+;:~W}E}e-Ϟտ_:+>oJU}Z2] 4 fk}հ.ls!z)fl}yGfZ?懜7[}myGءs/B q'AݧС]:ر1ьbn<'<OʅwDt:["zMOtltl-Š͕˵qMܬ6DF,n3狱}F8XzoK*G=Olyʴwh*K.SZYƋ5󽵍5Gѵ=XV57]uuWܦHkoĦ`wvN;`mGS}%ힻ^ЍL3"Y5=0>geԙ9[| l8{1ߓyۊ_|ߋ~#mc#_+mU+ƥ=ҷ==熌I_rk՗(Qk^-Re{f9mߜ17~~FߔCs~GM=8~3CJ=h=oxS;G](ku7RSo2oE-m,z9}[9 ='K>&Y}迃8qX35^q/kg+~V{9|;3w|GD |f-@_hrCO4\M&_7i74Mޮ|&FE:ccmi +u!mPĝ!K>ub{s핵=̥ǖ3aڮסz[qn$f gᰓ089DP7뺰p@|_?@N}czsSS]&} 0_D 'y38tO_ͿW\n618mO!{xz߀->ts3~` 0]#Ofk< HtQ:605]ϰB Շ??u Ytv[D &\m`:._^@ Jpp%\..qeN,δ"=ѹt=)&s{>7?:YLs~>Lo:~1mVmb$nXcfvU|0 Tn8 rZLN82%r};`2iY&rޯ !DpXPHC {ĉ5N3ǧsk_gMg!//?"osXnh]w;˃o=9qbLFP\wt?:ϙd?3o6uJ-`V{3,9Z}0@0ۀppK;pl-8 o~uo=c؟Rwß|m{_W41!mj_|5˚}S7;e1OE3m E>|'4k9)>qwm_4~n0,`+;TSEOp=P9uxЩZ`us޹y\@nӳ{)cp\4  nk`FG<&A^0čNsX n\57_[7y.]vPmbK,׋N(e6q[UoW2G27,A qGNZ.bF8i c؇`FX[0A1x3}e3׹pߵO+K\m JXUŪsjҜꔊT*M"`~KV)1)-} #9l(bcYM,-Sg})Ū1+H҄R aChyRd4BjSF"ZT˯g(>CJ⪤t9>T*UZUh*TDeYe\E* 3ɸJ&Um0&LhuH©?TGx"'I)LV &YMLDKMq$f&PaUp4:2Mxu2ԓ*s lU oxMq`;4,4#<б #A <'=VtYLZfT"QÉ hA2R1cRyXїĤA=RN*=dx4a$ji_xe1)Q$#4*"᪪Nߦ ŭP**XHK +#F楋5]:+ 'iGo8I$t)b]oaЖFk&#F{~h=(; p2b"m 6vo8ğETn$9r?o|f|Hm `&ۗBd{rkǰdev3h3RunGbrr`L!<$"}r|gתH1=p0$)QS{)rd\Ra,^UUOڛUZJ%qFh69)JYBI1H2[rPH -*ʽO Yvk&V7 $YF(Q^șXB/u)M)!'OroX,>>k9JS|Jlz BUYU+SYBu~,$J/XaTL[$SIRg:x& a+ڀ>&DA H{$S[Y~d D\6c.ST:ZC[0Yׂ\Vsa2<\uB"k\ç|mRB<8>FHL9býҕ=oF5YpYV onAlqE"$ CkG; oRmZU,q6 S(~!{?z.mS8tK)BS'-oB,'us.bl̉[$Br^+uke6. 3a*dڀx]ЀÇ.kSG+c t۽渕+\\¤lR-|E} /ny )R9 {f]k z(LʱG^d*uum*~LRqi&^!_o'SjF_AA§\(=nЯz~{=@cUOfOjo9W4F7U@aB@TB"S=ض% _[AGCmU):5}[?cpȁ),q,8".M?]ZWp0QlJ`G|c&\1>Iwtg =yզ.[}$|#Dt.,3>T"a/|i#G9hTMH S͘tv\e*@"4/|FPr)Jn i%fxgGYso!>F}+'=phĔ%STϚ_"ǧ1ۧHxE)>{agEzjE1GNcLܔjZs9BWxCO:+* H8bed׶ ǖySlNUI^GHUņ=Ow(,^e|0HhXg+d<yෲ۽A9=TI=VW QryәKIP5m:r֛jB$ϕ.Qnc-|6^+yMlepYtB$4mf/pWQFK4K$®Ȃ# 0ۺ/(лriC[T|6rYWXJɊ2;yLrv%p^I[ivd 2\Y1| lj"ȿH=YC՟1'fK}P9 X,BftS9!Wa? ךF#1:a_+}HcƔYXTR|.D)H!䠿hWq:OXEHȶe TdO<"B|C-7JuCz<+-A2*܂rMŔ 0"Pŀ2my e&.]!-1[wU*[')EȽiz< VRN&.qTd++Ů,LA%_&[>>TŞb*m͟Y$ 76u#ce9!ґ+hYx i ]{&ԓT$=Hg&^LT,q(>6f`OER :YKGr 1?lfv'G4wWSI2L'#oLMtb! NjT!Qqr‘Έ]a$C;Oqtmeqϣ 3T%#qB {}G5(VP?Dtrr-6z`01ڇk:"9SLve=%øCvEp^*Y )ǂ5@S[(8AwٗzwAL,WtY|`HordO+ej(ʷ)J)33ǤZk? .]dkXda&ğ2U&}kM<ۘ ޾XV!~RrT*G(dM|pM3÷ıȣz]=Åj%f.dO\ۮh!k<7O+!|wWR$?vxxCs[|vmoI.XDIQdRrP*'=V^S/Hޓ{r\\?! f}5{2ceq̧c^b#+ǟ;4HA7(V X\CnT%_@aAʈKR䴹Ca(7EJm]jɲȄޥ'd躝,zf/G&l}_Qb؏'LĎ%A1 OǭE+M)U©A⑭ZXRFm&/x$}/$W+>d/wɷ ׅqC kwk N>? W(7_5Ovu!.oOD1; Aqdb)οH%PG&t@:eRa'=b.ҥA!ޯHl lb/AWɆtbcV#o*T0I-(Ҕ$s "h=/)GXr  as*| f[h&hHe晬3YRhkH #r12Qlr1N+++&}^ ^)/i %~= ی}cʞL{ .Q_rABA4X2m2Az)R@HHK2@ \<ꆽtUyf80MC7*e(y:^#B#RTze;zA)"]*!]Rit_TPID Ǵ95ٕ#y! U>=sH-t[ 3N"8OuGytU(Traȼ懭%N.%|B0seiL j?ZFO߃JuUT_R}icSn}J`E~dmxCE:R1Y<< X*]5%|tt))&eb=2!'rJ֜@>UYgLYqmCy/Γ$CAK~5_i]I?Hpix`-271(8apa%eQG/z4/Be *玤޴I e*jI AeL G\lUR3ry1O]ҶH۪T/qR3yجrʱ 1A*H)Va"cbm1M l3w@UO!{"MMnҀa:qx3F%!GWme"ibcpM7*]ʺ rP N+ġ@uܠ}X;o: <(+W;@E ؄&\ƆN;t͓kݞQ ?C:m5p<uTq9=}Vwyr~h/E"l +P!HqS' 'Baa38 /Eai~rnrg$G]|w٩A~3Uu.v.j8K,y׏;UU%?-xT7e'hܭ]l+@h07;1:nr7UXd$dpX5k_wG֋kmP'w)$F*Rw l8`%>XNC Wܕ@*/5q7^,4u=7oGot8teDCow1j,v!xmu}!9\O:bz;G6`ߧђ!`>聳xa~[ vKtXdmtI.)[m8Ps.Y?]˟q ]tL,bY_f:7jĕb[ Cu~|w7Y[8/ry+- Loox"3xn]uHBֈl \zz~̆ᗑC-m! &i@ j|kt;^,r0 tF &]6P`2ͧ7t"gC*2ۃϰLR6  _ev&R@7M9F@PZNK~Lw$HOުH=BXaQ-6k=$\d2N&yR=xCBo J?* lf֌BguD6(Wʸtښ9[? ſ*zkZ=J0Lpzr ;)nAemEZ(`WttJ%IW5 _ T oTsGv`TgӮ‹ hd"l-"gP䑙HU%6ۨF-6j1sJ8f[I7qY 7Kq0a6C5c3`/9`Nbk2@NsȣJJ#Ty&=>&Nt<4ӪLnrkzN!o_KS="y[.) ; :׎r5BeUEN`dd>Eh͢ܒnm1dBӌf5:ua-~ Jĩ7PLOyFjp 5dZ>7ր.H>A`'`uKsxr O]x]n'ڏC<ܝ`4 +jz@v;P_ru$a?YӲm Iܖ$ڧ2½ |==Zzk.dlj%ꚳ wB]ReF""mZ'UR@OnBQA:jFJsāp"E>i- {LAK3<`U{SӐjĐЋV].C=BR4v )]wrb-|iN+0HPn۳TuBn,*T2ak/Nvk#Tb%&¤mO*]+\NFv@G*Z^$GH<"*wpi/Sڴ"X꬚ W($(ȇ8u j>J]9ײ^ze P 0xm=ަV[ߣHOZ*蜹R#8\ꝭ.3\5>XC!5v.u?6ezRí_>'~L<1MB[rioIlf@+r [y4ߢ>>c.u7Dz*OƽZ*"/.R 'u+Jy Jꭔ[)2l刳V<[Mrv+aKV#-;`=:viE &р|,8O>u׵*.,ėW<jF)/Of>lM2eY}G+=أx JKSXiG=f`C]tj5IΆ-CeѺFCjr<-N/3_fH5ˬB)wO f/SHukl dbcjmzCa^Su)r>U3 et?¥RM{zQKsz`liihȬW`22LJXF wզ(Jz:=B 7ٮʶvߢ= x Ev@sۤ/^DwV;H=U "r إRn <%P*tL^ Qvʃ:4L8u#OB`xUIA 0(h'tOWgvA#Q|ɛlSZP~x]$dTsl>)}u݉Ol?BBW T"ٯ aӧLrLnm]s+sTW.+9M<?X:Ec@Z?%3kP)V'9h7PQHiXWwK[{K\wfgHy)m4sr]-'3K6ʠ%a+e|v_/0AG״<ߨAnGLrg9TQV.հq?Ax%@jq(0(N hnu@΁1QiJOZeȌFERd)H&UMWS~sӅ ^n0?n,¡2 )(WDVO*:8!jc_JM;,Qc^؛R8zүwjGU563 L̶.qGlX'@H3劝O+9F]"hy{@&S Zeph-ϐfojYxs&xsYb{p<Z _3A#^ߓT7-N2$FAvߟVEQntWǷL/hpNؕ^om]ly_ξ#,\`e`*YjbͮWZ4Ah|=X9"F݉i PXv*[OWȐNR,GYN{tM>*g]q*C4QPC|A2Dc^JMh_x#K>`^[\XOZ*>: %~^*R0l!7&t>Z ;Pjw^v4w߷D8_25FډKl[PN}^ج 6dS)fǣ+ ϱe-We4mY|:DmO+T@L0I#$A%3*ke+_X>]sܨΈ}PVʓi%;ApgD . CRb zjI槺evP/^',xY~lѲX4i6U*+"wWSR?Ȋpgd)s)^#(euTi :=sFE=|F%jjLQx|Ls}ܚK=À'/cJO\loWqx8 : U]dlxq JL2Vdr=mB` A*\_# ;DŎ?L-K Ͼ¤f1]xpu 2a4æFL306k%|`m/ΒB/D҂"X | 2,wŏGJybBÁ?Qi3EOZ ^=LDE8-1)=܆ Ԩ̰EHI6.U'pq:\Pv3 yH' ?,߭u@%]瞕q7Ȕ-VJJ;J)a9FX[ȿb&Dpoɢ94,dtZD9P%_kf)uBRDx8~WxL4wЄ&VvJ 9w::>fd*;`׮ T;UWR-SIā .f euL*ompŰ/6 tTEWdq2|[޿BUM; %̖;~jQ0_UVWF#^96Opa˯;6}_8BqR/:Dvs^@ Skh+AM=nY 5WSd}Sy{x0;u!p,S;>&Н?[X2`UI T]h4F6}%O:qri.f>QCeQ*@3!}L?_䳍@C54dn#h >GǝP ]qM=Iͅ]# >)=hPHSG#;r/75X|'\~!9.?EmL=sk>. L\"~w/2D]'_+4Mw.0k:kŴ.x^aZ\H-C\'М: H3'Zz1 gI$q`P/X}П2~6=ܢ,pu1JzNB8=GӃWZ!ԃן6= 35Y fޙv;#D-RMQ9,~dp^u-L"Oe4(3bI_0hFuZ]j}}`=+/R՜(ub[.[K0vf_1#Ve݇p/p/ }YNbe7;=UQdp7Z .4P`.n5?ypk fS}X^ -^oizH]w>ORBwGM2q41@exH|yP05Wb4\NaVr Ƴo‘]]gJ?/M|0XOûY|reKYC}oB \zFa_nC<(|;1N :;BFU~d:;ӧre`>ל ~DFuw;AWGM:4xgN!x1'Y'$ ɉ{ S*N#TFA>Kr쿂.qHRMT=^wK P#?̾ʄ4Ar^ϛRxW@ΟNaqj>ϧ6|9^oz&S9cֿ|L/'C\}􌆬`~s~G':v 棿zOq_7J+Q_0\{3C犆L t%}?:t89'Da!0 ζz׹ f]wk$ f+hcx;Y՗ b>Bez~>_|4Y ]~{Xi[HAϘtE}q1H}#;MmVVs{[\s{y}ϝ!3ٿ\(j/ۛ|J 0u )`KxY*s@ |Fp.=D覮EL#xdSiU=J.-{|Z]ǚs,SV :_qcWw˱ ;nῤf::pMF]4NCm <ÉFmx z ;Vp02(ed󷊄ʎ`Wh3o{( /tG)u) lst5~]BMuh&8@#/:%jv|peFlvtS ܆xf J&qW2_5'Ƚ@W6Z5/ǃ+ ]BLn?dO/[KM0#[jhkpn ':g'[*{y8 @6۫z%3%-<-\ - G%/큰"GᎼYRSYw[/dzTzĎiwq/d돧9Xf5 S(t 7 `/fm@吺Y{Kk4Ixbk=* !l]CMS/ࠫg; }f/ ne-zPJ5oB_p@˲ >N|WE[,|܈w ?<ĀA/O2oxF,9^/~Vl娍֋ӻS%jW-lS}0YS,s{R.c~EϺ^6JR)<_ ZŠ>:\x{qmsg=&h4v~]:~s:p{KgLLz_]w.qؽ!˯ֱi|Dn1s{+Ls;驨,w@bw@q]5#@@K C;)5)wӫ;Ų){\XaKu.\n> rA_A_\  vt3_>2_l~w:\sL+HKFG^uZt[@;]]47 YX|B+tf{WPᝅ YPH)F:{Ms@Gˣts CP> V>mW I|"CCO(̣ 봚,v{pb}Z% =Zf ^jB%k1\.:~DopW_ DPPMR HP.d&T0UA>k.2t˿lFT*<?=5Ś?|*F45*#5X4!YAQ9$\ndiE(H!=kЉUvjm!{enǀieK)eW}Z*_-Q٤7]yH 0*S.BY.BXV䏫n > ݈>jA*0×/ZiRv+::O [Gx–s`UN6G{!vgE%~79»͇Vk8ێxkί,05:m*6^STYsnM&,u`b>*:x-F#f=gVZC1Mdz6n[[zk\.K=/#\oޭ`?ec]gs" Uci}mO43fyw V F 7؂UN^7 .5iXJ )⽬$kh8@I-9ͫ ?wQg<_;*.ΡT XbR9v-XBt}|?a[۾2Ape4z&1M`)In75sݗ)4@ jmrM8nFCE:3g`Rjg/㫛Qg &?Ly.WtS44[7|3'x~ l,Rx֟vtiRJEBA–h?'1/_`^|6/Z%I+&.}pM0ͬεnw-!zG&~#YlXƚqR5˕,IzlH Vĕʟ)I_}NT%K"ZY~$piMYbf +2K0D{uɧ}*sÊ9FXJgqH" ,,C @Bdgh4 ;&/.iJ-gʄn3V@yUvH1̑C6@laU#9#TSTs5^Њb?:46{2~ۖO iSNCAɵ3FӚ\Ye$iM8GՕXzhpn-gxKl:!tF+W uMj'Wi#y.sFi :O "pWljek WU16Yė3zZCmAEQ`>X./tKT,ԍ2QV! ;^=)ϡN!W';ڈ!>&S c.@e^B(t9e݇*ص`Q-sZӌ6IߜYNZ,o沔7{F("؄w 4߉4CFJYE@d xT2cQ+UhՍLA3G*b8c|}S \>sb y%nRatg"A/}q%h2ʣ R{(_WE;TFٝ(Hn!BM2GWah\+S)A22Ci {Gdހ?4“$7"Mk\Jd 4+Fe0= 2pV23.&klָoe[f:?}Z!:d"rȸZR%5'jd\]!2_0&tԸqD_^չqa]7I-f " Jو8yY͍~N)#g!'r|vEJ44u39c=C vua wXG9ƪl/Yܜ;j6zo ܨo2 22`}v6-h[{C&{+6mIC'nT{*}Z5Gφ%A&(2K>ݥ]uHte;e! dN] 1̊qEvh9Ek %(nPN" 1.G7? l +·#pO˂TQ]Sj2s.a{ʨ*,~vO.[ox5p YV KU~r9SzWu&gy vpM.6xWp=S#Iqd=S Ap>qHm\,d܀!dz0LFr:k. ]/9W6@ײZ Z4 nHvQJ2IDe(e1-p4Yٷ=fEwvze:k{`uruZsiNdhSB촇ӼV!Qm>HF PBp7/j=NP HUM`4IpȆ xJJ{Yņm|mU .J={]R<˨1l0K]Zǎ3CWgؚB3rCc߂P N4O,B%SUeH sA*m}=4d7qmXVg ?tdȋBiRjE=m'tXϱy:#sbפGD`hEf_GyŮ<@)YkxsΦp&}64N${IBDcXrQv4ڔ 4$+93!pL !dp]{q GBi#|<0"caFugXZc$͎+m|N_hTbL{xc ,:2V~jZ(Lcδq&Q !_JBWvT#A(0mtܔ,|k&rTf< <,ǔq  0Nk] L4#;b%j-V zLv7ԛI3mQ\S e\dK xeqa9VqA8MҠxM!Nue!5̵yfHOE8PX0vtep5ؑˈ*/r-B `p\lL"~lNq=XbFMq4M fVcfh i7(o/곓VW8yD_m'nE٥$U\ؿ"A@nYZl z_ pxi9֖hI,BgX]$y)!t[ +_:RM$-HAV&2E!@Ha*HWPp,PxʝoV8:Xq+RrvJemBҍmik<" olwɪ:A)uR)ɎH 6ԈI00{@A~6TYԸ9/iRn\Mp:_~͗Y|^X)d4.mS[8XLW"m+UHNu'-(Q) ZQ)r4@*aa.Z'Qz@ӂ* )|YJ/vmJ p_ 1k4]s'<ݢt wasP-h['/ӨbLg\DPՅ;BuVL/)Uz&]Ko.˄h 7=EuP  A&9QVڏMnӐBKZh#H%2[8Vx!8223z5J9u>'DqykUllkBo9`^3aSi,4r qIuJ ΠP%W7| [x[ IU.!gzrE-L~xQ,%! z!6>V4byúEDkcƶS״J#$+ Of oCHFr(u|{ * twOwѓZi6v*%-BP'" kpW9no)hӛ TX%~:s7йbO)!,ZS&> ! \^`!S%C-H I2l9cE 4T2bZՑaPՈY6nIou)-T(t("lCH2 4*+n¤ӞW̒Yu X~ւ5v&}h]&R :R _I_JS$F[Wkx 4fES,:&\ҴQL Һ<]R4:I l[r:0 ͪ6 Tթ .CE/w-#P4{vMnX1g7EMp}.yOKDTR=\SCyׅi]0X Q7%mHJS4FRaܔfq\r3*Er[<ïWEZ6LdM?XC ѣnB70GR"fqaԁh,*&۝(sñ|!c0ͯ5^F`P/~mJ.h75,)5>֨7P ~)U:L[[ѷg N8A 7h3_ <@(_2eñ{[PUd+Prh]&E@&鵞]2yWꂎۣ "kjb4xx.[zԛ Di|RH6)V*oV(Zo2Tr9lI{Vxd`(>gʷ4Qn#M `k<{ p=Ck SVCQ+(lsP {ٳR2r^V^[1<u]8謧?goy"0*kWH5x+ jVW3[bTb2K9=kD?cByL~/b|E/07TĨiK;ϟ$E92OzN" V+Y^ 9IdBF~ },`sR Zʘr皴`ɚ9-+MEu^%!Y3i!,eUI:>e5)5Dy\ .2k! SL=n4$]wizVΨA!S1!O`^ AԍPhU 0oA 7IW;JgWD HL V$]v}ᄃN,hJ4fD[UꤗX$WCWV"1yJl154JխIOQF]ۓ49n+'͛rbnvm[J.d(>Ozx jZC vʄtצ*^]eB!q7^30NmꉰSaA1Arl+Ϯrx1V?O+Ө˞ȓi'sUb+ ~)I}+W ;%ʹz0Q(Apr#G =_'#55b+G FyUnHѮzɒQoA`Ndw@^P6?v4)N %;Cy+@G=8<=U(pq`h\z!n٬Y s@$BmIiYvZ#١q\@mU31Xxtځ~=P쀧]ع\P5hRmdAںSW\WEFӭ0HTGfMZo&AV7&aU#r]kve8>'IPMV .иb u݅@ w j=ռ $UP@ 9 Zi2꫎܉4jhY9&y=ʱ*fаaGIAhuiRX#rQG!NxB8UĽWA#nB@)Dv|tY vw\矀#6 Ut5UMTe/vZs.H >*Z'9'sX4$ml8ŃϾ"\L  MhSt,ҫxWUCzJϖΖ K 6eD^UO b̯hxnfWfQwT刞\͍zqW,i>ۭEc & M .5S4u}e/W>u-<ƍBr3WrxI hE es<YUp.IX V{CX%fv^FOdMbYO Hrw;O&A/ۥK`{1ٴW;Oh9CIQٛk%9%T QɅFB܆<iJ#8mZozLJ8SEhJ9 w}+dHuқ3Eѵ P-Ӛ fIp?&j4oG]_ mP01eV &ϲj!,˕ea$#孜p^=] c$>]S%Z7fQ Vi^7{B8lᵠ}pİIYZ5#?ԟ {Bcz~ŧ4j؉Eۗt+ZזW 7aEU4ґC HNH Bգ@F [8ҍpM8- Uhqqy42R^M'5,K~w,n@!S~WOh=$gN5v(Pє_w4)rƙw@Ӄ4;s|dt "⦓C2_̑AG+U+^'l>[f0PH1#JHg^ne#\ƪE-=/H^YӃ9-lc븷T!9$7 SD̻Мy4Z?!+'CϨhf`q/?_ FK ԰ Bз| W3!lq{xjtyO/r jt7h҄Ƌr A7;%ᵪ ^"=By?v*(]d}=rd6M*˸ c"ZC}n<[hOʥR^` ds G)dz[_W?`.8G_e<әLVk<8LlLϏϔrtwl>^Z/[d>wM/o_Fd.!D"?[qq_"40\WeWT*?Wc9,= &7Y\g]ͦJM't~ʗQVq;y¾S~#?)9.> !~>ީps;ܞ.X~ V:rt3(;R Ϸ~L\t7_~*8^`~(W8>¶9_|{wq@`,+[t\v:Cf ekV`\lzI*ꂯuq<-6zȞ4)䷫T8A|E|M>j}pGE\O;ņk> ؘASnK plwh C !(|QshU1)XZ&d5*w` UH5@y k?gnܞ{s3777܊w#~6s݀s[7 ť]Op S8 vU2fy t=qyw>-D|riЉㅗvW~xǻKC/Gd:4O7Bet S&Md~ƳFVS"Hg!\|,oGt^{,(ml; 7TMapv0##s5B_1ǒ]*-^쨍 <̎&Wb8rWt;-ƩLms ˙s>\([~% wɼ¤UUqOkl?(=>0G?VulKgٮ9[.9-lT=|`% y2 mki[hDC"FmsfR4`⼖Unc.#brnH!:1˴ɖ{H "ͲVI[DUUFٺ.<V5ީ잼s^OU(*RޛzOR39z;ۨg , ,e`ҳ\z.̓eͺ}ef5A.4ꠁsU[#`8[9NhAUw;LU1ɰg3P8.yqwu \:p?7%%TS {.=]l'z34xiIX^G5x\UZMvnk6~ 5!`9jAq('@hC9g3l[͘ԒpeK1m(_prPcA/U 8yW:)iHpB ./{ᠡ'4>0rԷ HBְoP6җ{"b5׃1]Q2>UyۀBߣ0?+!HݺSsi#W{9aџzhCp/IJjHfVB .\߽cF4)H9NZ3rHϟVFBLe\BJfDv~_zueor.3{*~_ǁUxD_Ҽ9 CC(ZE!j~SZk]}<EyM>,71jz?-pC@6ДAT=asI]Ġ J +\(ZZL"^j4"*ɞA\x_PMNJ˪@6. 8?ѾNe#N%U~n5-ҷX"mﰇkGJ&u}}W;a)sSS H` @7`9n[/+ ߥ؃13?~[M!$,nY,6J-n[#~ ]e;5@YT [RDV)W1!uCZ7듍%XSOǸ! ~ R9 uހYӇ׌ ꨃmF/8lOMV}Q㋧40f5~,piMpbG;y7!(*atudU]wߡWun<^jZ@|2%mFR(b䜛Í馍갃Ѓ.Yܮ=[LR!7[2bjVӵz:X8ilMobHJmFk4=UvS=TyY5OlFY$Lɪ=V?n Yv ]P 3l%`0wʫjV)*'u wVN[#L_u&U*H;:_e/B MWtH7 m*zt1l#Y2]]Ȉ26f+Ցi φ6תɘ E=`:'<$_\pܲ"pL@vZ5R!Io&FL 8( ^t08t9jlx7A[؉l \#oiUjpQT5ɍ2DQ]qY1b Wɇ*gpFKJT\DGi 3!LEd>^dcd/Q%/\D (N\&e*?|1a/HiwIlWuCיuAǤņK]d.>T'K tF{߯wQw~W8E{Z*c*R٩$els tiV Z !(uJhS{831f,N?tEj)!b?TG0UJ 3u\h1mj+⅕M7H-xTm0Sxr*kr֍߽_xA]6ʚxAyt<}Mz>y;7!N4L}+D뾲 H:rb%rhS/LO Dm2.p!I Sq]ʗK_6uL׍5AdCb#iIԭ>̂n"r0. m:ZÉAྊ-*'L{4\2o"AZj^bwJJ`J'K˝@mW,4*0*=W\-+] (;qҺҭb ~<"5gc0jٺԹDZɾ* vcN*dk:?"2!?ic?oU=ꆿP1X ..,3TR0X~N?+a;ٝq*G:8L38frgurxdn,M Wao}fVG5|'V/G.M;,FowE 4&ef86QAbi`O2#+6ݹdZ'C9KWYr(`݆ZqP(ӑ% {VT~inУ⚇"a GIv?gZPrs`'YTPtBs|H.B@ҁS#7a豈2^ RYS; v'vC0/Wot c^]hz[d&ͦ^dj\&l Y%.,[-?B+%uR),r{ގ;Btڧ@#TQ b^ FĬjTlg5 {&EAG*4“>%ԕOC3eX3s`. KWa'1\C)ݡ}"W0G( ,lGA2Bs!R\a7qgή .}7ac.JЦu ZL  Vܜ. b#+,ٽ$ɟ\|k2]ū ǘGM떗qin]7Q K$ Cai IenCگ 1 ]lV8JHbN:^u")_cuǡ]Oל@uY-Z\Nf&kdHO.Fmf;B-pYaq qOQ`;]#y5!'y\D\`-x(ãD$\ 3 Xf8DǕ^j_:|fL$[z(lђM$<Ymka!<jD`*φ:VE^/xVѫԹ?Ejy\;U=lkeqA!P15#B}НLr*:ן j_e>J6 9lWpHB x\]GF8zJ |A?*'N#}](94[F*o䋍Tͣx!VDD[R^SIHK"lc-TÛLFC|, ]A XW;^ rŏt ۸hABцZ@{*Q{3㒐 $&p>59MJg٪s3}}ڵEIW ˼\-[«_{dz'`B˺rHhʒaqV7LC "@ ʜpfpu5o`l@~t|+vvN= -.=YTvG1 ռACfE0lB]{4^ yzu' SZ 5 :¡ %SҌzKDU*WE)N}MCB"ii 2dӸ3yNjoEO%LҤq]L5Uh㞡xǞ\sIE Â]=TqOHޏk.#22`rx֘:dӻcα.yGΗ6 I5i#KR*PkPp_J@ \B vKX. f ]XUFAYSM r$|˂pYG2 c@kH؆deZYKW ,jm+rDՆ׳B$H<n~ԓE* SN}4ݥdo^1BxhBL.Mfop 捧&S~==p5pg5\>^}uKܴ?+ܹ| aяpfŢr\0aWgLyh>Ef^V3ȡ|=7ʶ9=ՇEQ|ͬ !]+Dq jI{(>T6 bLpu# M/ "1P iъ 853#KqK-?oInFu~6MbPg_0Q#aSl;:ͺ _ԑS/Ê\^ ^_T1Sn /W>ŨHmLKH.T~R=k6Okvq񢑇>8@wp+x9~rAf2Zoïj7=/X^vttyy];A3PE{ A'яUz` k8n3m(T7B9/^֙:#/c-$NJ-KEYah'0^0wdXmkDn Ic VW'y;|ꍥ%@kC(:,TuyE :#B>8 aT5-wFF&}VJ1e^d绬ڬ{]Y4 )ZQkȥюK+:s9hZk\_]g;;C+.'f.L"cIփrQTLS+2-dMDB K 8&g6^% P]7bn9C fn֯Ҽ 72O]ӷ[LZ!ͽ7ԡjUnJ5x` G3@j7:YT}D W~]| m 2VKNd[kx"rsM/,hn}rշؒ/nl]h瞄xRWݏ_C8Z4x$L~jZ{E[}B9r \Um\L\~磁ɈW*H:VFi[x檿a:Ps;f!]ο nP]}pQFCe7%g.myOcVHzǎᰠ|P'/GƩwؚ<ӴEa&8!:4uR"Ĉ;еe?JJZrRJn=AI:g;;*(Հ\5a:KR)2EB;DA)5{0T]~b :ogFܘ,d^wٿp#nxRܑ $th^?4rq9#˜Etn@LE_ {PԽf:RвP*ue?E]Ėu .,D\ I~7=  :*ҝrB;ȑe[w zmV%}nX~Ԉ*w%!+:]Ⴖ4LU&'Ҹ[Ma>רd3Ҡm@׿$jy+" !Pi_8cdzflzd"Vu!qdڳwn6{|d]|uJxM$ih Fu=¾ҭ 8Oǡ*WϟILs}34+F['v+=Z}7_XwɷDž*@gGh 9u$$|%:P"q7kyk3euR il̤ւAoC@Z`IpA\z=Q#& &_tт;A 4Y ; )=DPPgπQ$Y ^\ .W^xU4_~_}Qڣ*G&rtpm?ҦRE`mt0d ͠Q[H P%^^R' (ae^_ 8 MҦ#JO48a!^JUn!v$:Wҝ͑cW"5 J1fxt,jYLKd r].y7].[r@ka @^6 @.yv:(JR-G*ovл`iJl^)~?LtGhI׆봉]VNER4L*cLw+@Ō6^M#C9.vrWʲMq5 w:u;S)~~S͸O e[ 4.]^ӯ:D ɐ'/Ko;f>bfI/}wa]ux>~ij2swM?=iC7 V_~,$9 ;jeO?FBi)JmYdt'ljæMq<刍~ bԳVϘO u WA\zZ7TGs=4rszZn#)b %bB>`İo17ٴ^֖p:~4fjVO!O4Yt{] !DWnų[,F~eE Vlxɧ1,% Ƿ(_?ge \p-A}ͭt߶f_4|_þ<^|_/JB|oefޯ|n[ΗCƻŲ+FāwJٷ%w?wߵNß;U?L;S]3dYJ]!+8յKMxRw3,mX. hx%.l 9&Rd;\{FG+J3Z&5@4,GPvOe;]yJXT=.`m,!؜-6/GL! Ձ ANKO$9 :Q һsV*|ޖjJ%vX]ZfX !x9I6' ?3svk,7\}n>~>rt A.F7jd fuǵЫ>9:{-;<,bh{: LaaͿn ߜ]pUL;U==gm٪|C؁ A\)d'DO"T*RNHo F'^Z`Ű }c `ݦ0%:e(bf5o6rB8L}H;%Lݖ:֏E}Vy{*CCyid.rn9"jR)q\M &o7a/,.y(B&]ADAY\,GwuVm|#m!53?n7s։YD U%)azc!ݵx T5 a.h(S 9|iLzTd|5&}ndcOA~UO|WjFB&mL'v}: IE2-zfj'}Cr7Y;.'m1ǂ*4Zrt3n&2? \KTANmRZu.v uQOU[m)81bdޅ!1 ( NO1e0 `@|5=_i P(SNI)JivɺFq<"Uooi+۪ߏ"7NEt\ d dмwjh/_WM1VY#\"pBj\G r?E~:C/WB4 HmV0tϔJ #Rk- ,)rB9[oNB˒mLZi?ԗv]r5Q+Ax*۸s:>Čm~/"vh/mt6c˲`OAi­X燔,O0ZަIXͲ(1oy3>iPuxH(hHs*\B?!zj ~ţ;MSYVntwcPrץ' I)K߮\zh%za:1:4™jAXN0BAnK2@ |71CGRum?ZDpGp+Rc4B{ Q\ʘi/.V6$ р^`|rv &lKW3eQhhLyo?̊Lwۀ .iީCwݎ$eoz&@&`6 +ׄ|̯:->+/腣~]B$GQQ<j9Fv|1h?yæfx`Y} "U:1mhy*}#]քUPbmPu3lGz惻FaN O8/Tk[zBELzOۼyXN_LYgJ6E%iWF{ͽ+|[o-ڂ70pj[V9N3bM5*;M!jn؉Y 2P DLC tPb:lrUcB2(1w2LbX9 1}+nN[9dNڔfΏICzzY# fnпT e~ӲV%G[^7@2]Aȼmf\tQ\waм=b>l}Jǭii1*MosQbni<-ou7JHw`.콞{T3..d!l;Hkp՚Iå`-Nyeu!&;-Y3M9\6vƃ\8,'-~EOu\? P yze~*TZtS(QAD0<Z2bKv^1Q;*`]!_gyPr7z *(x|#oOCv"05 ZWJq9*蕧<"Bu{_W$ VM-aQ}]) Fho !_:͕3d { Y`uLuY+`Jx~_Xci>8G]g:ߘtDe:lPj]`613?$=Z%).+rL;[")2p5\`bqS6ˍ׊A/ɩz 8@d|?BKJ'hqH>Ycuz 3iu_nZ9+z>Isvt*꥖I#{RϨ*"t_sV<bی`Hf7m[04/!^==W7m"@-"+xN89ef{| rTR)~-nFM!"cl0xߑ(Kf=uFo2 SQ%zH ·&*tÌ[uf"͒<(ݰYa̳-ڍ J긅 6l6^caeDOS"G#[ɐӲe15uY֟I;:[6U0q@Sށm !` p`L @!sčSΕ7DM;ا!ut+'S`4`}*ֶ$^0~pd>RL=2LO.8J,e~ܼYؙRTueiX0$*C0$,j'ScŹA*_}~m>z@[x>ݔk15 v2G|CCH-%u"h:7-+# wOSc;,P~Bz%4va;k*ȮFcYs`fWp0y! =pAnT݉ɮ~JT[;%H$}GmH}7zTU"Z= 3!(<_wTt825!XxNf hB/[O YH@FVYAņC`(c2K: $:z9zeP Lk38ooqƪ=s 13xYsYg^qL*5x׌=3 `ԀJ,5Z AJ!0GZxUM E࣎3X# dȚf4ԔfN%ֺq\]c2E9PcD3zH83CǺ9D%)񦭌fg誯XK0)]܍~0G?C1xA:< 4 CHeF\Ix^wujh4yDpf:@h!8/AZ0MڪԊad 4ĸh/[a+/ƭVr֪ʏ[.V׭|h>m6S&Q]SCrс|@>u @>w @@ÞNw{Gn%@ݞwDwGt R2"gcpO@3RckH9f Q6ϑ@IԘn4<=Ņ=4ƳO(:l@[O^3:"O tTlr`7tp==MJB&SSY|\DڨO>;k S?ݴgӼDμ'>Zr{# D(LʇvK-S3 rPOA\ 072Ici!mL2:Z<~y@J[ 4 5K*b?>+ FxOE9*#U`BZWEX<`p * RBs Y V,ѓE.%*Fѫ%sc<[PwWyJ unBg2A0So*iꁤT,}{Kj/ <.P)uZ^:M'%Yׂ #l8m l@>Y1wj%VpW!^Ĕ|P)(i>n}dHM#6Qj0Ӿ5؂FtD5áQ3oW+@Sj Ag 0 JԓZ .AyĆߏ8#P! wzCHPG{ƔenKuGiK+N3}~?>: "]yR=F%|CFd8Hyj jeL}ևK)Yg7KE)7|7aIbAڻs0-`[m7Y 0;jK lJ50ude+ Cd%mӁX]ӱ&|P5VhDdb wegzFZ´a5}udm4&\.#g O01㡬6.sy}1ۖtU%@hL)|sb2h Ru۷#;!4y]sE 82”^ǬSvMM*]V& ]눸(m !%^*4S+WZ7Fd^hDbZC%Yip]7ЀPƅr떓! <16B@IFN<"uNQSa1˅ps~b9/}z:ճXABkܿS}tJ[Q&J<9Svܤ;rE3D HS0=li k!婄:ks1t\R1Ϛ5r!!V,pxwroQ>㪆 32"LmL?y/?uAmr|8@0@py*^ zIv!O,&p [ `|AutVmvh Z?Rm GAVbDQe-DszRu5ͪbyWgGFeo0ZHFjuS8a_m6Mj0j؎C鼒%SY@O R-w"/˅ jX'0k6 @@c " cnx[08B _T?:*us o|oz Âu +nfժ A&.06J[EAI鸩ҁRljBJҮje85xS̨б%32cu!Py1 @)|E<2C^GQAnThiDri!N>C7C&F2٦?z A좆]Dr!4UjW\'l["9 c?z.v&_yE=QSH4jۏI ǹxEt sfV ,| ߎ!`8^ 4%b:`3RyD`{[r?!iU&+LdY&:E,|dU=q}4+D3=2;R!WtU習L+z(;Qx.6C1*€u˗^r8MfKqh˥c59b}ƥ.lrzS^ov X:0[7EsV]NUF!;R-ش^Ҽ qhJX Nδц2hMj*FkVnm;vn˂#=(aX7Xؘ{9z@ AAa%R%R$yvG QlC|`YC\zn٫5d-~Pifg?u5)nR pwh!}RztSih_p9FP_owh;꾀g8 /^~ܪ6X!&Lr^y6a|6CvT+[VC?b zvz1yx1麟;Sڟ? ݏ&ş;n5!K=vz9i>-Ez&YX:Am!TsHy lҙB| >/ݸJ6 *,&åt^F}i. 禎*ۆғ8-zѩN9fdˌtv4{rt*`:5xS@7gK6T+SH"4SqttUo?yGwuzF"dJKx\gXSDvu u=YE6>Tx-~Iv=. 2Enu"оvlv[XvLc`^᙭@]Dы{ܙ g>#|1_]Wx^|vϜ"YR6h| #e|}CZϟRroCSK;g`[m0Bc*5I3$A pN` .Nfbnղ9/qdWq{IQ7uXU??耫:by_-1|?*\|%U/w:o/ϗC%|!>cߡΌsҙ7ޭjcS3}u~ӞV<+EJw>v|5)RKg+5Wr^J}!+5_bp*ޯr;6OOBԮh,}+Y5~;krU-W]6~aOiLyOճS=3=5,$fR8S-X>nHњ }oU_Y-}7Zl|0`ocd->&>6^g6cnzs*#szWN~AGQ۱p$^\6YZ:"!a*Ƴ:mAG<ܿWUޖXƇָmm7~ۋv݇ԧ3VaS&dNTy6ѐ>u>Y-,->WF@5RG'wK+]lȢu: &_ܟ-̦%gZB}#*_{F; |6&HNa+G(zjo%5P]Y^y XM_ޫUto.Q!1t|\7~25uzFI)48i0TK?' pm@*ʊ!= u |4u[Ld7.eڶt h4!F}PJ0-Wd/ρ4U]|׼wV1-ͻVR*Wj)[ҽ&*#nc$'r*$M'Y*,>16bU~/ʈsB.y|=  ndz0;d͵Mxr= tQ*o<{uq'K+@0Fڳښ_%C=f%=W*΄v*gA#S ׅQzPM4eJ^U:}u&| {'0=3ڭ"Zy5szpoL' GԂDP8DvJrh^ {3P鴕pr yfW+C㻂'N}bW)["vou![^.klkpөE( ~+`]i0/qO{xlqVkAMP!bҩvSW7[yz|<4K-^W<:ZM,3%L>?S/I7zE%7XƱ؆IhI/z7Yj=icN׽`OOf-жF;]mY!ع6)Ԛ%s8P]ZMAz=RF)];ںm0ݰ;?0doQ08ߩubҡOL'ٳZ{ .pdңZ.8x Ud(w+|Rh_սi "dsU6 ]PՂ&`>Z a1|(K3B z4XGN/:\N.u~:Ɍ.^s ~ x -dȕ huu~U/߮(Q?|_??`D|Ϗqo *xKwTI='|OO2W>қڽ 2H[8+]ٖV !ۈӐGܙ7/M;йƂ-il9.wKcm[:IK >y7kU?wx-B8]}2ͧwA;k'ȷn 6%>` w8$5_pKǞeKSt7 VLpUҲz?}r4I`!.{kTYnyp˾Kn?bݠ/ vxJWPlQ2$vלVv/ZІꐶJ{O`E{oLP-ө|VcMH/ǂ3 bC}%Ս%'whpXd1<բ/haI{7Xf`ZY;8ݏl[K]Ч~;*"- y?,@o9ЦhkԢr ݩr|q^չQE@zW (W.|W[)U&x K@e>$L|Voїb0}6TiQVZG+AsU=&+6PWǷ gܷi_|MsRd|1\CKp2RM{(߉ϲw=7 ]B"p2H"-WZB9T\~W:SqJ,u<{WNyZhi\7gHmUqPQj5L:O(;êxl>^ڐZhO3% e+!ILrVar p#R4Gcj`ioY8T/H*,o *$ ~-W~(`<ܭCOksJ>\,Aԫd#dtKn"飌>Is&\ڃ˙Kuܱvn_gI1gQ . lxQs.z[}$=ߦkaG;42gy "҃wTFg`agcshvuĉ&A V&C)o) o'{nl#9Qv87,M(w[\N9eяJd =(\4{OXk8 X[j<u % r =7 ]%9xE>վuUw;֭E^V$Q|StLU?[850ynZ- Q@A:I !8ty,vվ;x͋3 %d(@:ˤ۪~t3nltGIF D`Ďn7MA+36RXw"=mPDZ7$va,-cP b]s!nJdb`ٝh"!QF?L܍~w\-f=Pjuwwafj!]W<=Bތu k}EP`\9 ?h_ 8: {߶&h=oZK[mn tF+"/€9W t\ 3p뻄BYpB>ю}E..?3>AAҗ1kbc5"o\A/|Ycz)[y~%-ؚVI/.R?U7X }Fcߒj1lswO:)Q'>z;fBTgxp0 nCY@r\+ : pq:B,dz0IiH_>̉#e9\?( }ܞtD6cm~VީT]pʹs*֞vO^;ոkhJ}?B Awstj`b/|R!^+?I-uWeн)߿fc:IIRE\ՏFEL_]P?[e^g]`oփ.=: BozꀏѮ/0]` @Q yw Ӕ [5`< X FbXCF~9^~]ܵAXZ[Џ: n:7䇗Wwn܀NƋF;䣇CV&O-\~(}}cT tcC=I>s5^?rс|@>u j@X׳ËBtx Jj̈n}FF(cXse>{LEG@XZ#iB HXҖ, 1&)c4E2kj$[tlgP\NMZWuA{{0a^v{e1=+:o,4@ {۸!й".Ǘ>p5)>BǴ- 's{Slz96H=x1o?PQX,-q|E>XQZfouZ,31gޏ33*%g;kkCcegT|L{Yk|n<m_e<>Q]{әNLswٝ{Qn R|uəUgv`C<ԩӻanefj5ۤ拺_8=]%2hB[׆ѱ9 m+fmբ3hϏ/hO/h~j\O'*38==$U,bX6jC KCLw3g;'?] z>1z/:  ^xIkR OM2>2wYowlxة>{ܙ;D;L ~\e+F\I+vIB':!4۞G`)?Aߪk4g?D×ۿ!X6OE^[ķoɂ}Z/:Anrl8 y h'˚ם ޢNuIr+#Azv<6#)̊Wᇜw0}$䑐GB9SLO% ~o4~1Zij A_mVL|,+,:@D|n0I&T\tiQ<Bt[D+oNx3rdNjr~Xj3v$h~Yňs")#uŠQq:zN,%egBKs|zJQi1v+KK T9@5l 2Qˮgԕ"eoxTXgV'FΟlbCc.N,\Np`uk9mFLpA_˒?@Kjy*PP~` ʼn&f.0G5pJ?ǴO0>,YcMF[L԰^w$, @3:5'0g]8VՔB!`b܌YcN1T~ww/hn9O`asͧˑ)Ah1^WgEf.D/FͮcHUEW[v2vF/IV \ ښ@QNra}ՇeP6=DGD9;kjj9"b* ʒgze0t Nn3!#nuL)NVuW]]&G*+ZPj+H H@GcOݤ{EWΫ6Crxa8CuB?0}W7@K8[FĊ9_@'hT{L#ݏzyLchר./ SG&r>h4av;Ri!!&LRiiewd =YT$3fKel >Wh ==e&Ms8rY#K'nʐd=h:,|XO J0azlzw).qoN@z42&Mը P- \T@[-Qr/Ym|b-6|Fʯ+s?H2fN9P|dM( ?ZFw+<,Bl^])W=Ƴn%߭>l΄b"IIxg_?Ж09.17kVc6'eʾ,g6V>!#A  'ݪ=.f-3L %Y}Ϩ4mp؁# **^ D(nχMº[01.!n*ҵSA_Ym)dBdg ^dn6~!y&Tʹ2:[ܼ@AH6 QdpTS$LA1%yO2O+=y)۵2cg%$&WkrԒGn/B]Y/5P6 w*W 8>>zȗM5@Gxn % ;GoDR1?cQ cU\h3';Ņp+}w!b젗%UrtA<8ripjd?)8*/ʏ|J"zRGͳ D丏( E PG|% ۴٪ !lR%L]u=.,+;-#HKY3:U|:zF/Bj=^*dCq) 02fDj/t`@wf[ J9NO55 *(Ɉݞ)⋽2I$(|4db]D~ު}h.ܧF_an<&h~fW< 4Oavqbt XCJdK]gb9,, id2 BO6:F+CWi<*IDeavf4tkQeY={xDyHɁ /yM7=Cx%MWvmX6n+c0zod~;lc@ 9@ᩛjk75HmG ׫ח2=g%b*C'Ԅ Ha|zCVJn6p[^n ' ݣvx!:XC#ʳQk3b74:O6# Qfy=9Bū8CZP$r*<+|y\1P/.>M)qN@<,8&lHx K5(%*liYj26j\e|( T2OY-ӇGTa6HQ=lF9f3HO%qVE&}]廰nwY T`5{sRh۸eޞd>sh?(RaEusYe BDIRM<f/Goq9z4Iݳ$a8\gӌ S.SIlSEb{;@-vKq U Zە3a< cfy_,}k))[,emD!ȣ@:HOL(_vvb(Ю#u~|+.^q㫥 ]t;C锺SRV*GB(8Z( g 7O&3*\6BoH9g>I%tf{HVȦǿ]OgFP+A+}sFn8r W׋Wʊ7գ(:|]GW*9E#8)!U |0W1b{:`Z BC4!p $ݾFAwz, *ֳCh|IiVYI:Tq%~p4( zTgJ OKeC|+~a[O}Y$DBi"9Z,9OBwik_2[+[ͪ.Y;|@T.F1^ Aǐă7s [6w(7prѶR^=֓%Xd1[Ad8eSJ:]D{+FZgoN"=IR!*`'ZZ{O}N f' 22I+}ժFJ-f$[%q]媃& ՗or1Td1Z6 bWnO&!@Qڳ;H,/%q`YWBdY}TBYf d5_l}xeٜwt*BL5Jn!qP2Hvw"Yq5D9K%eGPNBiC0!h1/#T%Q_^)fx<2a`1Wv_l4_6\&KI\z,K|bz*/.|OfL'/qpbc訪.[rT{@v3&d8%ʺW$uQ"#} +~BLEH$"ѐ!J9fMtB\cD4B9h }.~ֳD$"PɌ!JCw5wzw`6v]痠F * CI5.Y5]Ԙǜ|N5Y3ꩨt}Z5X@P%(JIj( -fdܕ2Q1>nO5ke6o$W{YV?Z! V=^|{TmJ 516&Oy`^XR[“n0i%ƅޭj1vH̎_NEfኌU4ENVY8a6.^A]]PF\aNثQ(5j4ٚtybFN _~9+y_I7OrDeK|xBnXq盝e% w,ZZ>k_3z~H2ޘӸL]>ZF*f $:gm[$Tgo6 'WCEؒ$g'|4ؤd;}?޷ #IPUAB /5>IiqKBT7j('Z$tVPvIg[YR %b$5ܐN#X uXup\dT͗j`3̯bcga"=Z)OB~6 7b)P/ *DeljJos +YS*YrOIWRJxN04,$%͇F$ד2)$a0xK7?\Px :tT`5{K E`m-J!TuL~yQyb_k8ť-SU'Yjag SY5Z<4HbP% 8$Aηy S3PgQ(D tӆc#9FU޵KG>\]$JMIM hf+n#67Tc!~ dtۙ>k(,X\ӫʫʺAdWrSQ4ȄsqO+=E c^TR|\suP< cD{^eF7 ߄bQ{!2y@@E?"5`}mmģf{7e32w9<<6J! r*.@2p_:Jx_c%]w?=g,;7p@|fsCJtG9{hX(;xZQ}|H!c]"VSR4"L6z`W]ʭؓvcV(2O>M6gPk6YŮlUTsENЂήǎp P)z^K:Jv0;|gOX\1\sˣ$ ^h!L= qV1DQ^9%Iu{Iקš8gNY٤[m1 #gj=O1ӧGѦodֈb5?7"9rhddlxkA69(7s@/w/K^Cf<[N2r0'{Tf kr*l`st94ȊM00@-o; cA,W;uy4jY~sbK,iXY8-@ ,pz/Q?9u)C V+-U6M)uFbQUS+!fJ -*l5rV-~m50P+):mmxHp4hC~0ԜАBlNǓ#Y*wrFiV*İܼ"r+uzYN#gq#̜k,fnS]wPJ6D8'y㙮 lߠH'B=m3)/5V Rq犣ւ@W!ޗְÆ%Ylj)川"-D :bS y$eGA֛n@8V.!J9 tui^I+gy%fFx~7!Pf.$VѴ/0߬YNn,ݶؗX gm@Ȍ¢L2DV.{v,Mu5K ϒ|pS/|/{lr_E@\lf.UdohKrW`0y.t`8n$þsK7QagU/^iapj$:&S=t$]^vcL e6 ͇R7 o/j%9@aֵjC%M+ 6S=Ж@/yTKu@}Ĵzi{Y kaUgsA5Z{z  :1k>iO5'1(kP,JWs[Ykҽ=߅MQ3ڍ1r+l5I-|@;;LodCtĒ,؉z7޿mwE◈R\}e{kpw^"TJ#e,XL/z<.xtO ŧ2+xQ\!#G‘'5E+$P0wD00 ̪AHR\.x(kr, cT%p4\:t:ѪՉxJpJ9(JC)q$E@JIq:Q;UGp }3W N{MGM! c"uho 5YJţ5 :pxDD=_3 GiNOp4d:? XRVBٻAoϾRp<')v 8ed"1a%cfB44z48e|[Yx}JOk .61+#5mQ%Ŷ:8=Tn||O?!Oy$>D1~8:m4}޵?7qKUZVþFSy GƏhGaە{WNX[>lQ4mBtBM&b4f989!BТMF1c}Y!ƚz5P 9j(]F |3&޺oyr#.h)ar^m v:Xz_MAŅs:?G:qWM?|MEֺĊ.ͺ/f J>@i!6SwUzB9]9[-բAkY%Vqiв~[/%˰)nSC|;@ˣd3be ImCXy55q@bFkYCbOc XGE)诗Sab{Ie`M6+Nƽ@~z,ѣ!ۍWՠŷDd&?U*_@t+8g[I-c rP29Aڃ(`a]hSg^t:7M!rjx!@@ii;R=6_dd 1"\/h$Ǵ`j/wثO^ I~KhkZ}b%VbzxllCɗO3Ѳ>h*MH U`BX2WYW.)c?zO4ߨ{SFPWndDz;+: NBCX7 kx(G9ŪJKGv(yE8JO9+:9SLU>Xf! !51~ڪ@^۳9O{y01i 3 {!Lz?rJu ( (g p5h㡐JAYw3s?\}ܥ/B{#kբ+NK^L.{V{cڗrxPm:Nua ܐ/0lI:ւ>hy|OffOWpXb һ p*5 Iq)<.^6m'w$3+lvM4Qo 吰UBcjuHte&@.EivZ3Q2~"VvK Z\4#/_ 0Çs (ɜ# ~[^?͂NgT{@\a6prSHIU-Wl o=ԉ;'&c֮3 8ܹ 9U8=\z 7qj )4 S/8r;C\N~39 :~b>C~?avhaCn.w1m>ݣtژkPLa>6ixOpyC=G H} MfjIYϚ)GbBptr]P(t*8YQEo$Snrm[g6nhw/G?[w~M'KŲ{3]o3x|/տXw,,=Yd76煛^٢6{ ~^yee z)Kce}nbq=pa:[ GO"K$<$(EߝOQuVP h2~n,iN{Y629".鉉׏@>ݻq&5T_>Ს /ү>a [V QAVWZa -G;{SL.W\lttJIX;&:;OƋ)ȶj7[ せ5A/b7u7߻Eu{fVbdt<[qN+Ȗ'Tǡ[a+SiY1o 6rsKt_\.g,:߃n(pY,♍]3q `9ӀAo|I46P~xW$8|l^Ң4>=e1 ap@_7"Tx?m]a PLnKohi,S:Fɼ:7^))jŠf?=,{8uVcN yM]Vxo1$24˦.2s:̌Ы'Bš$S}T&!SJ77 ?(bQnrOn| tCɝC 9F 0)5i[V; W/x $"m cPV ,w@"A)+C!xF !zοC  Qs4CI/9Me Hnec֙OcܤoffH$vf]J!R#Q*mjOeSyrH.a~a LWe΅<dž`w7{Nb# ،O3S_A_ A{A)kr\QP}Y56Aplse$\O]AZd'1@ Dlʟ6W.gXF|.*bR9zyHW)ӆlƥO}+KL X<=1\'UOIb4'qnnrq삅 ]VKg\߶7f5tf],98{(ä>Zȳv ٸ)b)^3tPm;tV[QsK *JCvIyā#&egl22aZiHrmN.f$ppr\JԫZƟm ( LFpU`q7yso,"yD_)^5foQwS YfX @˿$k)^%%}s,tdg[6,6gEN2ɳ𲋳>k(IӁ,H>R𼍣P]>YX{ 1 rJ {JU}<%kީeESpo^6,y=njg"X= 7C[Nf<( v-_آ$څM JMUi\*6vW޽<#Jcpr[i'j%%R = $md:S~&6K.VEJ0> #ktu*~>1ݍQĭ>|Gù>=~YSH1 gেPZ#Lb񰊩?9A,gĐo!h)F t5 +,e=m[^B%.{e/*3۬X6eqԐ,G1*cCoUliȾ:ꍝ(=a'7t%'o3aaR Uk>~I Rd-e'oH(iLtouȟ`0ժś}\iK ^JhYrI@r!ӵduoA̝.2ϗoh0;Oz:AkaSnaY|=&mySIfO1N[~xpf0/z:菿 ([q2OݰW~sb0aL#L̓J0Em *֊ex{$L㩷)-qY3{@їS!Vό#qiAk8?(AW!07/2R.fw'v/  g˅"(bN+a/G7 pTn&hy'?U˧eO>8#:I#Bx\ ڸq /O+kݹtf7;*^ڪ/NS dav]kׄc"XnX2`P+lVzpvsԩ_w9x잺ȺP{f!ո.87EG v蹔6([/w^_Pm+x4Þ(t8W OY`TM){\hiVf\e3@,IevZ# vg_fجg3ҤҰW0jA㉥vtU?\5x:J1GƖwҔn<1{x;}ʠ"S> p+'SGaˈ TUu69rT@=HȞ"R@[/P^!Z=*b <'x5pą76zl<_;{s`Ͱ>OAvmG3=hs `6łȟhm ;G-zfOQSy]6Hp F&Q!V5 P/ݚO ,ifD{n]S??IiSM%6x){4dlʠB}6uI;r$le_No)X9|o6i}٫8#'wy<G7?ӟ6iVnl8gD yu@Z .⥕kD2:!1~K]:Z j?Ҳ׈9rAEFjXܴTxD0`$r6x!P H2%+x[t PB|_ȶ9ӑ­[.;Z?d0ݼ]ԾƸ']C{wVj7gW_DbDmDV6G])Ge3gqk Vp$6 $Ҏhw;Dwc֖ˮ7|\dzDǐl6t6B(6pʈG Df Ɨxř'9 O!)Yt+XQj8kOsd@%UN>hɷ4I8ί85*QÍ|`|:rRAil[Wv0@WσHURɥlY5'pf ]ΜrS @ԍNCG KbFc \E)!{uj$*zC7Er )CZxrb{-R6 *ö`^yמvmMkCu.'d'9-٣N@qO:\o)ȟ3|KNmSS~VT|x ,\}vH,t洬OI:h6m6ӆŖHv.קv=8mi3y{}R}hDnIm]4oS ;{]EP3gWo\2΃Ҏon)cᡓ<| vbډZ{  w+d2Dr!`v'͊j6ب5k`QuM>.6MZ71bOӀ0|?`@O?/2f_w/{/7qP꿒yEAOmsuwwې{=~eD 𥦎",4sI/<[[@eØ~D}6N]< 㞏Izl>2q⭂[(L@+*5$|ZK $3>N~|Sm6߭x^>(݃b/~-gNF<$rX?IE(`6XoQ}3YHKvl/(:'D#>9$H!$M@Նbw4ģA@`4;K4F\[pocQwTGI߶6\ԱavTC.N"xZUzB%59kN٭' u~suWLL*^o] j6uS.zY]. =Zlkmt0 (GXK-+jW!е4R M2Z(ZOG#JAQ`4#Vq1MVPex.#\FM.: O2n|ڈ«2©7颈nZ3egZIp.ρ:P#A(/wkqD?>`n1x_LC$/*C'͐CnFsO~; CvUcfD6B@<>gz?rFrZ|Go-%N%*2(ꉭ1_F$@F3cҷ ۠z|PTTFpOۡ]Qp7|://1B[ƨ2I6nHXjݛRVzqzTv!cⰰ=Ѻ0xلtFUb_Zc!ST8o*lD M= bʜD1O)V>%c1L1ġЌjuMM;qgn[gWH7}0pe`*Pϊ?54رFzov96yQϰM/3 6ǝU5BUy(TVsF^0NfqvOe~e#0`SCȕ`ruxXԽֳ޹͠ܛ6bJffPU"׆iiS3үB\-R˥mi92_dЀu=C BN/-~6`1a2iVup(%SCY rh"fQP>"g7c8V"Xfh7qǕpl9G3%`Y_#%P]:u lIӾ0YJ ҅-j /5dj;h&d"YTFʒ{M9< LXN&~>K7^ bU6) MH'f?/-.G4ڂN@6ycENP;a}0aMgѵ/yt>⧞d%-(mh`4ӆӉ5,6ϗmCNtgpB6 yHfS"3'&`.٥O>%Ԓ|!RW2dao =j:N|6 2SW8eɖ R$[mx <rw)|~~.`gVѵwIv_Mj]nti8pY nC.s;~0f_Bol7/A6A?b"]`su>y=|MCL؏(/(,,ן2rXŒbFIs/o?SԈ>#o=7uF˽[H&;65#{##K`δy+A&ww9ͧ?]`2wm;AALa2v;>>O7۰ɇ-6i`=A>67-MYሓ,0 2ϻ#u59ݸ+Ķf?nih%oN0Oi||2F&Q!qϪYu6J\t,t?3Oo~cS 3?&_oZ> iag_|O[#nJ9kA0=f < 8[ZMV>'?|OÀo [* e xG$o[J^m:⒢ lq->S&#3Q! tҏ_tk]|D6hҶ lai 3FHiry o{z7S`q/jՉ1hi;kU: ݻڂn(:aćCv>ܩ ӆrQZZ`i3.iX7>O]H'o|}r|BDfn6l˻ -7VjațnnCثg95 *Ob> o+X ӥa|[A䥁?E%TEZVA :\̷K7s0<=Jx]!iF1s^?fT=u3Kjv-M+յHrZ.O{N ߟN{N$:;i5unաCײ ;OR|<,D/t<0ͪb)'rjcYm݂WY/ȮI ?2XNSs1+ 1$~* >! ohcpf`]ui(T+T/2:Ee p6d<* ^nEKl qeIelX,1ߕjL&g YNNWo΄V:ɥu2t9 `i:P9}LKMocKZ(Z:e]R pZށk.wWvBh=ne"kpm:H;Pg2ovZըψU:,.Ԅڌ5I#W"C& Ug~n1m"'̶茨YB ɥ0Ȁщ; }|Qa Gsaz AcRKą;/zחU:W{g}88"ˈt(?(\}\]_V6|B@ \{~58Yi8PfD]-ji  I[ :֡doy:]k9cUL1TGuᆓ`qRe&֚ItsNդakZc㝭3h]:Q'4pݘs:vvuޥO]HAҬuT;j*Mj"#5L邫9J fU/h]Qz(a{ 4ݮpqe힂m5e]hXΒEW4iZX]OJoV[a$w"slQ|Ŷm^C ޼\,a.x5Th!+^!pCTĊ߇Y6RjF jlɺ0$} C]x!zC}=H][`Utfz:cbn`7qK]̚ʩPha)C:6s:b`U=m#wWi6 "r+T{&jPcV7jxAC.\g΢+Ic"FSBJ8\m$!N~T}!7DAK 3,ea۟rwuq5oֵ;X0HDjXdauXƐg9jhK W@Ԯ Uep.bؾխN/Mb+X&Zk O aA\YMG nyFF/SeCE)@)TI#JŕjqvjqiE حRРXUU&NZaץ9˹.6o6<6S4o晀!mky.9QVu& DzIRBGӯڮimלn/Ef=O;tӱgFIۂb#8: aBp~pZ0!^c[82Dn8 f|ֿT8M q0y,Y `k9-$fr l,מ ɮAƟe+]ՒK%m Y7o͑2l@k>_QI%BMÁ`:r=q@D>PȄ SY3y좐gH sHM:vG{Y2(΢ȵN/|sa\X=Uc7o2kgOZÇds:oLkK L ?м5rY+ʔyD!u!"aOn7Iob櫩VřSX郗TƔYd3~굲j"6*%e|nl<7yC 8ұ@.7CI%7f_큀1REZ.褤P8ZZu SέuoGtTqچDpn^9˃^,B,曡[-g`-iAC:bd QZBmBjN.ڻG]YGi\C\wr\;TIVP@"+c}4yʵE)[)cPfÖ6RrhR h6Bx O%sKA&߆I<%ag^;؉ `Tg"ZNKn"n^3_A᢫k6dx.[ZתJ4hRMvTݡAcn@J1V$,BT~G\!PEuGOcjzC):9JM%7`6 ũոBp&1Vij|LYphEQhKVcQ ׽@*ѕXݿqҲ6X؊o[q+{euTP#`ڰ/m/nmLj4k Zmb ٖ)^h";1f !k>d: vv!!87C7 W-iH]IiMĂG%^){$]Yv N?BIJ iqċ\aC[9Дlj;[$kpƌf']+ꄽN8LejT+y^@wLhZƄclLf1SEL)wy€LiFKo\\5߽4Jl^s7띈vs5lrs +llsk!yQwoZR@6rdW:(Kc^SC'KNzGԐ(.5*QCj$RCD5n|2VCjH-tN סBPCJhjHmM5fjH]Rg1^ t0PC$ ԐӨRȎԐ: ҕ"ںC )Rn!&wӜzQnQ?J(,;ӈ^yӮsԼs :GÉQdV({,9Z"+DWu:6`M|R^ʫ-#zhþ1U(P(&,9jQ7EىvAىbCGnd0 esqovG>K{82[KHPu ئ] 8WP.{G^M>G{N&>ǀU 㱩VphůH=u4Q{m Xי[TI=_ [Y n@ ȶ5 ukB6kspVq88$KE @ N3!^um@v kS#Z2:ʭ6..珏fY/>b%A$`30p}rӀ|үahAwt`$\pރRa.fn h#pTH\i_ t8)reRG 3M`3dTs "BH ײTXݍ_zjIV=%8Y)flS[g:"M>qKnq+u-le\ۇTqO֭m^Z"~d>>]W ~fꊉ/kK?11"x—+0NqWoh;n2Bk΍9~Fk|t'-ӛ)D5C6uqCcqK*??OKT+e(?drd;?m( ǨKIf6~Y1#^-{@3cy\=_(AIp:Gtf)"yN3I|-8, x~2k@nsrkޜeg < zG`a 觀Y Mw)s +6矆$)L a6UH ̼e#m 0j&XAcXAZY? !כ ~N+X!5 -B m+}m 1BAZ㡴tܻ}Jv}h\' U] m>a,HvaVH~‚*yѨ/賥lx?N㬐s6lۏ1p5@²=Ò>=~` Iod$47LO 5\]iG&4L4}ŹƗM77 1WXךQ.%`[FiKvtEOvD؁C6`:5[l|yK7-:M?ǣݳ &ts1 pt7v!]rcQ(Oz_Ʀѭ{ӟh1l(3,oshK65oܪ#oOB:N~0{6)m,d?a 0u|0K k@>7 mkBN`4-3m 0py[3 o. :G9as0NlbSC-b2b~YW761e}W0Qvdmm2#˗7qpW=;'s^ Ž,,iaD"j>'H9C;}(L%,]RrmY8G tsLj{9SܤTpii*MLz9P*9rq;8Py.M@V"&\!ͫxMslM$])pϙ[VAdDҧAi>Dx|,Xj&RcV/}fșɎlgnm@REΦܓsNB~Yy]?gfjٖ+k~';|7>'?俲+TY ڢ7 +>3:ή2Q] N׷H̺_ʈ6Nպe%b`,YCbʜBd|csdQNfQbQE0'ZT6`5~<& * B'X싿Mfпϣ zR|DP%M֛tT 鍰r^]3.-52M-Lm([\:}3PniL~m`De'"XxUd!45lZ.kҹ|45y팙6@*H'`X@MuLF2h8%͋ }.Tu]\յ\I.8 ŞqUgy6z:x]]NX@Sލ69f"4Z:Ơs2lގ7dQWiyIW[P=_j罹9> ON&b1tG!Q8}t-&AGszt4'i!7zww&3 ȭh&}h>6A6Ara$5a[`6`ͺN7A7MP n_p;WG&XvkmK{rM-7`^n= , GoɣrKk8E4ۀmDQzR a[`-2Q~'/wa~< 0 Y8ӴQ[8 }B1t p|}k3x~g 080}}A3GY[Ts8Qv5kI0SҖcS8ィLq7>̎O0}Ddj1W7;7qCgg͕Q§&$>c̤ه!jQ$BW[KbakZ9W홇I-$ Mda>HXEUWdSW4kMha3c7U #A+ JIU20 R8#hCt}f@2Xd{_!=jvaWH%'h3CIțU)@585}u{qL]1{onmr1(w>9ײe)&ã:*=<: >ΦY0 #F7۟%Ww'kuٖfMeRr7f 聘\o6utSI&sIpk>|7$oþ}׸&f uCqbրO2 oLbɞ*K)τ3趲]kQWYI EY4pvHlZs.{()Ю΅+dD:9 dC)մb,\p`+ + !`Kl֖R!DXLA%xlAi$VH8rP"6[H&x[# pM7X)z ,SZ|&U־Fs]*eYA@5i$DUeFx{"Jv1R26c[Ф+}\F \JrU>Pe{cX_ZUYaE+)p_N:$֗C);UWU6iuP\}M+WkkWޔH`&6 ;l BaIƁw܄L|oiʬ|/2K0وd.15}]p͂OĽwruw{.`[(^bfvԧ]>)D;@(ՉՑ; n #n߶9結q77,l3e*tI<`D 9h m`hPk勾\v`X.{0_trj'1ކbm6tyc̴"waj[i?9@pU7'2pזpwX^h{c ݡQL΂3sƂe,V˫ &gflSB jw}~;>yz @dsʰzqdB->\<i^՟MNaTeVv}x;?&pԛ?N{ 4KU|{0nqS2{rxd/i#h({fIY ԲcW|U:$1aiFPCI/A6'G'TyCozrъLD*G>U`)f[:f CJÜ?icR}g:Rwj.Hq̑Ic5-czta81Tz5{F6~T+,u~f@^ʒMōS2 AT߄꼭nPf;sFNX@A`A/g. lMǯm ¯UH{0|FCG\: ,JZZØ oznuI>(5A5p6p鱌v_j[F@F5IGo*ԵqvrxngrvLg5a^-9i@_&)-#SN㺥 Gkg57zM "i1k.ʝD2ϡ*~X4 R:H* 3ͼ4Ǹ ;CLX!]"6աkH XMmtJтNTGTPaAam mPBd A`cebF#0]T),ږC4ZCc\< *ztBq _w6{/Z 썐Lzm"hݺC; o@Qy^`.0!⟬ر܊tjh'ʆ 9yA\f- zKK w.d3#4hFzCZSIo<>F4]v?_q }~Q&UMWŽf(w<\_HQ<otHmhCAo /&e_jg~wKWBV\ \_f}YUsBr1o3` ;.~ _ U 88fV GFaRWf8{@s>Nnj:Pgl5C,> z33S싃ۤ.$DD ")x:mm' k>Q aV)!'~X-{EB;r:~o;h?~ 3Yg,.LG1fr:3߆{Ay^oLQ&rŤh> g?0sxpՕEe%l 0<A-~Ow8 W),t }!0;5 :ǮWJ2/r*v̡!Xrt[Nfg\Y_HoD _.,ۮ-NQ9q9]7-^'hy'*v(U?ؼ1%m7;`}ʜ1Kw]P$EYu2Cye}(!c%a+䕇U'C;?8Vw+׍j g3IΙ, UK)[3n!,?uvw8bVa=߷1泙 aYpr6]؄M}V\|odFS!{9T6o9p]nU0oG4K] 7cq^ӹY(1l~%%-o U jx_b0^Wp7r DZ58?Eb5IV!ND̜G!4$Te[Df:RmKYx,Ց+biP%@ߥGw_QUXvp0#`k$ v5ˠC5c润ԁfffút UZN4FW7rmx*y7Z){8ϫu [2*ӫ 53˭ z 3LkW6\R¡49YY'%m=\h(nSP'n8H|\ʰ~mA#xK x)57In`lW'b-C#g /g/j|(auyӥ$5X"|jiVNRs`^ښ0Ɔ_̝q*Ƥ|%$>]JfecNtkʽZ7؉_, ѣ8qqxSe(Q}K\%"eEDߔl[kJ ,HqCF_Cܫ"IQgwJffGn?Oh6{"gfM&Ah>CTFD U%xq?eMjw#x2#FmmqWd+ FS>Rlɸ}"3paZ;BҼ."̀ov6 ,,i*e.bș~W؍҂(aE}"+~GM[5CZmuK~GxzWT8Qn[?*_s!*~#4T$yh .RSx#\#!*~anw{Q1M (.{im?8ڬR+MZMuWx7zڤ)vfv42Źe 89!l5K›gq=Z[wШhSF -ON!HjN N iFaF٧$-+=O&zuNOE򓖇7"8h |YSm:`<Fsӊ΁nFq*C ͆h[#9ρ Ij[}Z'{K,L̢1_`4`%!WjLIiRN'z1^Kbi, ? Raѓ0 ~$zM퍓@2"2[ʯ6rZz(3|Dv':ES^B<5Ύ'ά2|VRyQ,GKڬ.fzC˭T]Rm9a}br T=u,[1ʗ(tw XN#d8v`3θ\q- L`u8HNQsL@G{Ԣl9Af>麐H<] $(LdMq͂Ò.pGYs(Žt4/c6HÔ4 /xY hACTc@7tOήd^`]JR T1q:`ʀ DX0M{ģY+0:kSaIx!ٝv%M5,)&^]Q CցZDmTM멣8}*lܝ8e9`=W[kG8;~Wi'p#J.AQ>P {tIr;ԻLB9N.K EvKv! $lVR ($cB7+t7jf3)r^ M B֭Yff+J`{*hoipvI5y@y /U}ڛ"/LmOG*.!ENuߜϛ %;Fdi$2BK-/GlZ\2kK*%|д-*}JTќ'W1x*+SMEV\9`Uo;ةzl$7[æ7Kr&~:0w[q.dE9=qMkLSvߛiW' c0_DDr pܗo6 [;ڔrY7z ~ Y)`#㛘G;*lMdߵ@8FxV d6BAۚyOi,GѲ,[>#B|hSg_AY .hJVht6_HЁ$ b|2"4 2j5:x!˳~-̺ÁӠe~vS> ?V#|)> sl6JC^Y*:ZlW;e,>d ܼnܷ><2 lHfFlhPRA=me^XuG1!Aygמb/UYP"G/Eaa#?ݜYacqLyQJ=󖪖dɛTށqD+Bŏu?`Jl4sԚg_zmV=4p}2$F0qT,`B< SƖgr^G~y|ť׾,n[ GUp}x˞ qgR&(I԰cÊ;JJF!kNpCPRu}ǁM]j~QF٥YB5)bPͼ򿹄@f 9Zdn`8?rT86*50|Mȕ3R,̱< jGIԽJӹ_v;pzL7^3Q&]nATdr-4y8Vp };ۡ ߟf_CoiR%gk l>1x*2G~Ds;6RI ̌`#|PHB.u7&5ոh]RaLJv&p%њ<ͱ (K15V _j=*FExR wMTn#p7sc}a]y/ݪy0"gR fWZԷsF@,rs^kF%t"QvXכ+y7 )Jk1+PGb1sO,6ݎaL(u9u#J 6g?b'cnNnB7="yD1?6&ܫ->FB 7 3a/&?ʂ;[XP/ͧptWdǃI|PzR̿L\njo!|60o?o>16Zյ*bAdR/~+@ &/bID./ޯTZRKqycԎ0 \-}@)5&R$5˙D`/&C2r@+uVUȿIJ xWUB%DUchJlbCI*=?]0gpF8115;먎 n8hWŞG^.k/]IiIi$~ ,D:_7br7.]lCݬ# Ɇ1i݄7o}ޭȵe6QXEK\Ş7z|izߞa\t0nraYT19-c8z[z*P8wb_VP@zn=T~4oM!D'\/"_2fV\U~~[QΚՁaX ,aҷ_F /L,Y8 pCx2a{U1ٗu2F<(i}h}lG -?a2I{O?Ȟ^ƲYѠkiCjhL'1ϕȝ񅪽[ hB*Qm BP" 5 *II,fv ?pMi~L1̮ͦpWl~_Kzc qdR0J- XOٖ6x0w |jT(خ=[w+U4v6bUgzM/ejcZfTn7cE3JYˮݻ z]SOsm0oһ[P3mȏ,bZ+0m73hH  &P2q v=x緸7X]/mGJ4Eg]rD"A&W\lOK'*%lp߽WէcĤNaH9* l݄ 9H@>x̎h6<6,cM3TxY{^am$d)\U85b-&o`x>ꬊ( ji*jZ.^)&pQ#4s΄Ջ09? SL۝F}gpPUus]YG0g9,ߖ8^OnUr$N0dm!4S$T)b,e 047\C@xf=CnngD޷/{װj4jo/g\gB %I\A{ ާQ@tBS7ҡသ5ZokVfY>{U#Z y?1GH S26Op~[O}NjETj_um24Fͥ |.]nym~{  -|r,"H!ڑ2u=ݍ#N6Ygα%\WbHjՅs, "˚9`ܙ~)qn*1:VY./MHF[_%͹yTVW,U O^IhQ!1Dx":3+_Buqvq/kbY8`lW뢤˩yT=3pQ Ae܌0ĨL߬,M ;u B!s"I8ńM+@Bc0@){/H̳6m`^+C'uxjHVBJ^)x?&D&c fӰR ҜtRiDteekuhB.1B D;BU B*0TQQr.E)1XU `VUb8Y 筲'E?dwqywz~eT@b.hNe)f[7b J;KO1tuq l@ kY}Nu1BF8E=@3;cgNS9p(kW8y9וXKsj]=e.:]YkW}>'~h-7Qק#Z87稝K2;O)Xtm'0.sK{0`-D I6d3AِtoC)`9݋\c #:J|Ӱ?AUB|)ďTh_g]a ~BnQ>֐0/o} N \+Vbb ]Tlي幍E8 EpC&;0~FPK9!8WhxKh˓7TJ4t$ c@3☬5cn&pEX1j/Wo~ËR s]*gq1ZUn5l$`׮qm 2xy/35eP|*R fcckl@ #op?jf4Nk2$Mg!1RvNjy Y|- l.N7mQ7P敹$&ȂG%!; J\[FXA.OXdB569ۼ繼Z@۸1߱]4 u<6z#\}ոYCFiO5XЎDHbl4$K_AϣqXLHR0B0!B{[̩:)M7M 2ٻ0$&il'|8Cz4 p)j:ޟ6 o`f>jMg`3~N{A?B|y!!|񘠯) AAZqP tl2F@4a< dIQAD!1 !+Uy)J-/@*7ߨMw|0_~]m={ l ,mچq- ق6$}?4 |ec=ӔHA12OM(~Tѡ öJjT c+V KT6^҅;ÙYzS`"k]pENa0Š`0w7q0Ino~Xۘ@?XP-,a1䝃}`\rGn%-~DC<?Nݧ5n)/Hpnc$viar>\OGp$7LXDؼKG`pk>/}LsqK`S$_YW˄M?50*%ـ{Z bp6f>oXf=ξ 7{A=P2HA%`+i9mb&:ӆSKa?ܲQ"&$5Uj= ʽ&jWl=E`40n!_uؔFi9p&c`[8D0hi?l:VڞഫI)9o5Q(3%](2=U6"le.T&v|kPaF%a(!-A雽M[=4/ՙI|/l,PHX\\xAinڟTXZ`8*uPꮐ͑G"?Q*a9cY/5bC5 lBՂ9{8Vle3uO!&OFI%'hh(Rn\[kƮhZY+(q1IK_uywFi{IiB!Mh؉zAE)F'R',2<#;msd@xiSm" wnN{gQpu"*0G)%&s8<bhA5%x|) 72%@(z=:G.xh.$A ml>J15o+4|)+,j{Xu0 r|l>:H9{;V6hˢ:W&p%rp :{݀afd:|M}ln]v4Ϳ=vJ=Ȩ ?+j-Ͻ^^ms2{?gy"8L|EVy*XF'5,vD{TzOo?}e<}顟zt`Gݷ4n^`}rqivD6pi O[M&:vfLu Q†%TL0&}eI]4m O[荡Q/| LL17NKI8?鍆a?*py\s_qWUekxF=Z\m6_ӑ|(wG. +{x%`8Vv8TɬYe=7Y__kq43G3ֶDu&ÆN/ p;9# u|jևo$/mo3v4Op%6<cCvPټBN\ d狍UW`ZT~ ;MG>8 [L507̒ZZZ\将r:ÌlD! `f\o Mbt]Zs:@ O]BXzTc~g[R%9'홂oaW\Ү/c#sڭVC۳j!l#9\ΐ @V@1ͳ4e]pYAŴ j jU r?H")*$ f~Zފ0 2g)0GqT x /0=1-;SFүh(&@ 46Xo0C^WX|3W?8T ʱNY9$ulSZO~xu2zheGaeêԅ`{3yT6ti@ 7ZFQri ׄ̂8إ ,xVGr#; .i^C7H06ogqݮݻd?1cZ= |O-yVθ)-{~Cf'iYr(4KDW.PZ6v8ݙSh8~v4<:ҝGp6= {!3c@W d4"d< Fl8]ãjaC|GxH7 >g,a"ǀrЛ }~,_gLnoL00:@⣦44 Ў5N Ä" H[Cm|vͺlv{,STK6kg(@ 6Kdhœz΍ ף1k>1[ gm)E QJ U%[5NX\ܔ9 g&Rf gӺb'$ , <`քk>^]&BvYfXT_~nQ0ϰQIU&@Ksa ~v}J>8s8eϰCd .wXZ³GBZS iiT 7pP5f~#NOPۿ<РY d WКwʀ$4%`bJX-}iM.x`}3R$QrKΘrdwIr Ӭ?zBCxê0ѡnhA4Y5ی=_2p 乀aL Cz/ØQB<8( F:Ss+Xg6AaUL^US-{SgK$+X3bl3U cRo70Ռp8WKVoH\% m ʓ":qrpĠlO y.Bhhy_rgn?)~6ҀLJP /JP`~ iE E93|xA ~{[6,ZsmfX MLm"׼C^%hgtq"Îі-nm9ER<6hvͲHk-iZ,> :3$jr`0sC07'MN>5Cy ĴBBFx#_v^|q\cS3G :Ssх0[0qRg3Ep"It3s07!w}CLf]ᓡ.ᱟw6q =<>Le=I> x߾)Џ`kgg_3\Jf ݽY(gSo3遧Wݐ!kxOmiĠuuEF79rſekRi[mM&`\wX\J%_Ҧj-,AB*r=J5Sץ,lpn_[:x;Lߝt Wc.?)dlotٜ/9a

GMYNke(!|y2 ǩ⩓LM b!m"qP"JQurqolsxDE«r=zU夫Y%węB6k^ڏ1/wMedyO#(3ˆ$PL' J@Vc4c~+Vq|)vܒ(i<+\  <{YH,aC5>]_d2 :K^= {JbƤ19-s!`0,-zsD L%hfN_/PEQLwaH̉%*Zhj:9/Ra]V )'xǻ2 y؜Na!}.ʔTf̀qrs:$jypRd@de+ݗE&RDpj(+QCUgD" prLVhDWJH5j@?/O4"pm7~y/XoԀH"200?!~f3x;. v #Tă^8Ru,-Ar02MV9}: *fp2ッIw8ǒx\lhAPCr, S} ǽV <1#F)sdga5P .x˽Fw5`^ŢY$9[tP|0l.j)gtQⷶ`u >1߯~nz6x[ka߲$'D-K3L"X/wAH2#~Zg[fN)#C6v$\RO[P-ux\=- Ńƍ!4}f{*a3,JKO\HKCo8r6F1~I_ 9=_x'Bp*H,hj`Sx{6|zдwr6Z>zÛYEɭqg%2.uA^F,xzO>Mk"7c]ZQ(kf7 D0a A oőlHbm|f76!9LH>Sz9(T#S#l`Z+riUߚa 7;7J YXKaKV!B2c0 Ut:V瀤 [0{0ӽ՟JX>!)Ppf H%bոAL_W 3"pU#9NWƛ>jHZ>%NBIZ3%:wp7LkFx{}ZOŪe(oLû<M_ _o cJ_),W0*)s!NNp_lv|)"Js ^,j[U'ՔO=Ny8 G[ ]{o`_zSbM Ƕ`qh,'It+OX:IK?}egSYPQG#?BA;DoFKTՒ|ߊaVDL"w1͓gK GtjX#kxkY_iY>7'0,1ؽ-^0 w` "A~_\ۻU B}f#uc>xQE|$XK`I4pvצvW QӾVB#lѯ*=>0ߧ`Ta|Ҟ9S0(jg~4~NҏRc21Z;ѺdPp0kJBRPU.5<eaI$Pe<ԜtKmdT%#җvrG=fWxz₩&nf)q `t;V>koAN3F [>թ8|16>Z$[TmGJ.L@m諣f^}OkQ B[g#RŠmE[ tL8L;9Ǻoeb;X@ fԆ]9{S5p_&ஶ~jZUZr :Ȳ<(yĜיQbeK}(@@WW=%y8O~6x |'f64*zp8<*-Ô'7#)2\|y0ihCQeAP0?MllX(KeQn[zQsX}bL:iOwUҭµ~odZl `jPy0?'qUTWW9{ÀN׶0+=XGHA]4{@?} N2|tл *35PB~-Uw:|8qN/8C^)I*`/7bFH<N$hd4P) 0mC'zj$Q<mC3G|軞EZ6%$g,o O32 Z nUT0VZYC5\J)[_/$L3`| M]py9? &6A2T5TN}e6ZJ{O꼺N'='0PQ}}H^MSKBuE/aǰo!FnMgh}xN41y} f Clov>DKH8(/f=LV`}ӽB rox =>2Q V΁ٱ%;(A$q0ɣ^ț//|mK/Ş. oOOe*RxTWaeo7K+_õXoV_ h]@T'вLJKg ~:O_e#i.j(jr߈:mH+T+F{]5دKbtfZTjF6Qs"vzz]aOvuE^" _`(+q $SoCsD?-0g&`Nqq C7O =ֻ̡#N $;.aՃO2 {Dfsщ)E`Lk)*uf9O;ܙy^~:xa? ~qX̚ӝHo+uLs{⃺V8;s=|\-biKip9n@\\tE7@h\T+ٴZiTk Nt.uda~89zQ?jwe̓lo *x]c@dZFR)^)㗰*Bv8'{C7۶6U-ķJB)S@$< {Ͻflî4 QׁALp!&IU7ff> d8 KEhi%/%/|8Z`&챟? Gڂi)a4a+ ?e{ 3t$_+tD]/{9]\;zRPJ:WtPYƩ`ŕ\ljXb1%zxkZy@MtmRD@nvC6-$N:$7@[=q(*|I;)t}er4 PGY 0w./ n:UnJdJ^Bne-^ʝjjx),̘`G_ us iԇj &Vhq7Gќm88_1VOXs%K˶{%|SW vMNAk&$_Z[;kW՟'Q.: +LFǴXg=}AnT6D)n 58*vny.yU||`,b2lr014&4촆vZZr=&_.z¬bKk8}:ts-2 Ūjû:[EI[b TCcۛUGwI1ED|}  4T[Պwb~bs`~X#q[j_.Hy88]EJ©=e%jf-כּd'3{8Nm]õ?zT$bu:l5o✰e%( e]Z]ő/e^0Lߛʼ.N v6$. ]y`O5(\4 781:u "HJ%986bq༶dm`l-&܂lS̉0.GV],@U/9&N)/v$>lԬpU8u?n0;Vr|nl_ >ء-۶kf~ mb/!jv R J#9n^qYd~}m`Y6J5؝0q:kS[1./ƫ'3.KȂ8(uui@ 7ʷc \b4 v ۄS=;N\8=Q"m|7wN+"Z++&Iޫ+2@QTxU1A})\Sah<%őՑ6J\[eK0N`N<ӒGDថFvm4wU/!L4JQDȂ(gHSɇY>"/H'8Y֚pSH5Ю`Di`R ICBbdS*!4 piZ]c :qiw>Ĉ5}uF/Moq<饘9Bo$e@f.ЗcJa:W o>ۼ(^ ً;084Hy0; 9NGC=Zqh*wDi֋s sL1SF3K`9ޯaq46...gW%.}&;)-Nyn1 P*^(nΝfI# 4)ކZ5%ѥ\.r;xridz'eH ]P(~_RVnMw ȼEqQ9,L\=e% )Ж+x4c}͂Y6њ7y5ve$mrk1H_9_-@jۃbiG]+H:SkdHȈFldYYn$ne^p!DrφT9ޙ 4 CĐv? : 57qtI*t#R; yD!`XS;U54s P Y>[X{}D_ֿvsv?{_ څ\ 0*.LޛN]fS<%BО$ g/!0E% HL>~Ξ]c>{1GA d1G1Q . oN)5"nU07vw9gOG<rzU43{"xpPƍ谎\ iO4 DB+eN.1 OU3'._t L. T(XCuuu;zsC|F޻E?ܘ!VhGl*yTCE%Lg*N"byɟ.2 ͥ\+_mNj?hzq%jZjC|Xm1o" ׆j_d Nj?0V\F$p>sgI?lw56 su4;`2Y# dm?zn~j o~#!P.ޢ^loU7+-X!Yx]i(WOf>Ko-m3is@%vfnEIn3w3XPjdpx NrAN_=hm8)Vve{=M9FjEj.9q6z/;K %R)uyA~Q(xs7oY[UI|Z#5&3Eu,l3A=@-=?| LAA3YKIޫȩ iZ(Cu;{jZ|0Puub0"86XUF %;ivcPovA%QMS3>j5R+Tg *YiNL&?)B#3-V)N:Ts_O)Ms}s~i;,Rxх'|*> ,UѺ֗z,5 !xY; :Cx]P=M[E2ܮ`Ӯ< S+Iqz`1FP=.Y(e#B^;fmm)&`CǟJtV]j J VnFzUۤQg͑s}ͰޢJt>χ7 HA u/·~^,ͤ>Ty viAl&u@jbR}+_Pbv,`zztOEާp7?>J;9{= , xXr;g<Jh/3'.A _"7=ȡ@ ;$u9 @A.74"l)yGt%!݋&-}ܜQ\?~.; FQ& sM ngRǦV* PPea^嗃Y*hԢP@^1V,&~AMt2ʡ2n+|}(ӎ϶wO1ڠTK&qy}{d+|4yX`,v͇  ؆ ˏ0FGp\SB-, ns:NOpUf#M`NCZdR(P :Ts%Y=Z%qz6{1,ROmAPڏArS$lZ6\. <pt{w*Se/!$Z&*SQA>ϹT p< a{Qsg1xws{ A7VD@ch f)!<XɛЄ'*#Qw5ezוK^\5Z^EZ%J>3UΣ?@͡u-|ygCŋj~s. zDZz{:CoTxL54b,d!ԗ5( _y'*О!(-.J9f2 ѕ(U1MG+#IUyrx4F~z:>^klDs".Y<Ρ\^.Đ 8X3'b_,-{~(AUYǘcs^Ocx aXp?Z즆NBH`$ eBvL2]Uaj3 $HPdt΃scU8?ξ.g߇"`</㱸Pж}Zw0'Ms7f:Fͬ[QE-"p+ V^7&Q, BZirao6ḩRG%[UcNGk NQ^@>p'Ւ g<$x˚:g`r- D#hřVCxxo⿤.v XmDs|\m}j342=:3 w֚ža"嬩V@PAt+-,K9,DV?w Y`VHw%lraYOM~TtcNBl'v|n`&_WE Xe3t/P}ߎJ^%~y٪r"=i߰+(æl8e7W 0¦HvU.R=n%WػRqсY OEd("C %.c֟TLR#쉄'S+3 gXY]0s 2ZŃW 9 ,zcПĜN X%:9m;EoaM+\1`IVq仒=GtȆy{L)IՌ7HJcy'AʰzJiTbH| ]jLf F_"[HAYzڭc%[̛> FPu94 'FuqMg+u~ 60劝ݝsW3@ԖAR]B$AC| ~Dx{YUlPXbGl)pc6UXWAfoZYn*(y+ oa [lCq'(~脂cTo(7sT=9uٝ.tOj)dŇB%EXnex`'CY8wq; s͋^|m639D/ ̺0qj Y;('QfQvDU^R#p00Mw ⑓ v (*)ǝ "}@B1 ~/-.ͲLoRjO6Jʳ%ɉ*~m*C'=7rl!l'zO@Z3d6+YS&Nk(M"Sexfekt./փ75-&)k|I l_R؈9p\3|RzVKE"P\*X+B 7I1[ BmS(cOkՕ,ܑ\g&b )م*o`l!$8]=@nB|v/ g*Βv*l4|<Y]{iC,+kוw(Ǐ>.G.p*~M5,|_fo,΍sNZ=V9. ՟ ܑ!ĺbQU?.A|V)nɲ$b%)>O̙ap(BwVUGkRꛬEc. ܈Kyohh8h?%[uCWX*F$6Kd&an> CFl }A'P*tW$`-ks >@i-g JMS.vjh3.Gtt( Cn?s7r nMb [Hn[lFn3^}Q`xJ^T,:x4dִ=x?ᜲgi]UF`V}a}mM#/Rk)cX3r A!Vͤ 4P0g8Z!ABq0}Mtf:K8t|8ǏñR0fќHRߛ_|-a+Yw#+hD0h ~67DА0cxƉGv'/ { X RRbt3PuOEUH"\!fQx*܏cʾRob 8j,+kQneg^y)hD|,*?9/P9T*9)FIxZDP3)(T_è՗τT!.;qCE*q¾Ŋ'5(!ֆF]۩-'g^G.%v`Np 9Y8CשX5QVmn>vrQ؜YhAB%ܕz CpAssqRfwxYHe$ X%h5}Ot|޺p/_êzÿ+g۸Nd@E.+Lio']'AW H(:cUQy^ZЇ<&&出gńv.g[Ze/+8+ 3Afw|¯#$9, A'efum]-P_!(Vdr qjĭ?E(ъ͗DP۫G#9n^pT22`!D'UG!r8 #Nu 0 V4V;m FrS_Y##Dhk!zDx;\G0 鎩0QSbb<~ΖZo@435[=>Mhl2ǰ/ٗ"+"d: aYF,|/7ߠGf^DΈi+|c8h|LȬH7(1mMM&nX?6O{zO~VH@_۟Fhi|؟#IRǻ85Q Di ~<~#MǾ}<}/ۼ?kΑ<\q;I ~.%o_?u Os}n8~LK0Pokw=ԩTぇ8}n'w6aSpڧf-,IPwk8}Y,t8|kƓIeI0J>xR3#> =SVTZhQp|f/|#c5=ts-F|Lo:#JQ14ݦ?ZS84 1gWo'mǾ2C0|t<Y1'άJӟ.i:?Ne1ҬzhWרQM&}4 '#Yi]1zAoi:Lvap}1Mz?.~,`77{Nqx0'{ϛhIn3 hEӦ:[q /򣐼AxyK0)_7G IA:ѕE۷!,sZ#OM:3ƜhίG 4>BS)XC3e?ޜyRH mq%bU Q3޻Ǥ A|)Ov:\rq~`uߪHh[THoԴ<`c)wtdpŜpQgG{ƥAqFlXǓ8 .όs: ݦA`GAW 4)A4lQF K6a٪k4mMIТNϘ~Us d`2|v-pBmӺWy7Ŵj7$ !Lkzt!Xw}@& 079a":pnAhcvt4q,hVΜ6%D,.or[+)`r@4o)4-F,0 \^bTQhdk+\. DJ(Y<$Iz_:P1kk .M')xlR5Nފ_ ȅ5ˋ36Ga-X4KZGB!sjQYr $DG =lhRۄWRuTVO-3=z僪6Ws7替.~)7Xt1ꮟU' :W3>1KMeKAee:.g %_%\0L{T7W_T02%I9̞K{-TMy~ݘb~^cZFd$Uk*i ylo㔺90T(Xu cakAC+n"FHB xLQ$SE:u)E2-mߺ2+dr=}J-,YWR@j:x W0{vylo2Dq%5w^Dfirlֆfo.x biB^ :22F|V=HfP<{o U1]7ZOl"E3ٝNq4,2ؑf'K:(\ti0u1 0v$p`9dfŎk@W i Cu_[|<(҈ ن㣕 }`".n0αXz^^iK^ pU?.Y ̉,g'?H+t4dzq [HYIKH!ɞ,vW7ESSwz_2CAqtbp]q6-BƑ* иyL,|(+bcNhz0@qjc@5f Db E`T^2r߾%8$˘>!$wٌ_Ρ{#  %>鷙)Yi>R.:+7#0$hS7Lc ҪD :W!fmH7B׌F! î4fyѬ ' Qy|zo(%f>#}K,>< nЭEPV} FD=>9ʉq&ՔiizzOm֣Mr`YוxUM=ښĦf( 0Y0HN_K=ۤd,7X( ]e/S=ȕ>pkVۉJE`16`rb&Wle3("fXܻL&xNsP?ll̹dKRZ#<1p?uSCӡ<#nYf@#૲[ 2޾˜RuڀHJ=uM( *_ /VqGٛn+H]Uh[$Rj?8$,qr&)Y_ @`HZSWʈ_9Nӳ_ 0Zq+VnPoeش H(V}8wok J }4$ # q}@gf́2H#Ţ (+pW['1G˖w;fӆ3H9&Tџ>yLNg3@av6ɂ-`0b/;]ERbo4U1ŎޣiQbI&NٸgOj/ڼIskhQ W܏ EZgn5菛3eTrO2x{dz+O`*w{2v_ DZ2BSx/dio˴/iw=6X">uJ?0Z:䙙6 ѭ7|5?߰<FfQj_4ɻE7~ $FOɘ.O;gqׁ׆pH*+lkvl:Qrc r |uǤ¢HQ,kԦ" D ԃN'_CWqK]\s{wqHUw~PQICDbFS*,=T`O1$``DҐ4655e/Xm,P 3q>1[:d<)Ki"K=Hj'8d| t؈[w'~bMGQB"$'<6ƄO xń_^= >|CjAaI*L%5w|Av|%+WtVR[Cܚ̛?>6Ar{a^ov< .# ɦ=:(43<@ZAHEN .ת]/٨_ԗ_~^=Mn lޞ"59H̻׎Д Ծhn_GhA=U[M_LH괮>5 @9Hl`LN\Ww-͔n ^Fq_U?Ӌ$A"q:7v3Fm0\2UVOg:ۻ~G7yaq2GR||UOqLeO@Mf5aWy%)$γ[m=TJ&EW:AnZiCa[&wOwpEƳFK7.Zxg3 ~w3mh?,Ԑr &TG~&!d0Ra抇SI7LsybBAbD.""/ XKtEpa:(KųIo⍢,;}<]t;0Q\y,>;^Y;?/Mhh?r!]N5[>(!?l$Z8$,,njL\ v݋%" @<KP:)EwGi;{ y)kCDTo$S P?~l`',ׄن}A ǿ] p#Ybot`1$UL(KdOѺ v)]:B j:p UF& i?!(aMZl$S%@uҘubGLG.v+|O~o3:g X3[]\:b_'V#`#\IFLwɵojԫ}1q#w;via zv+ MoWf$nH̍ i%1fQI%Hygv_TJv+Ň\׀tifֹ<!JܡVhV|,0p9FPM!Tv~קG'B:WKtm4j&!JUS@ͶlS4$N st0ks8V?}n\`L3)eRf\I@HbQ~1 z֦י:+κVګdER!\p k:J4+ј`H匂w Scgl\Grk7 $.T)\}h(Rz[$TH?3eӌѡ;ʫdVBD/'%΅e91{vBǞ)~1 nvfqN_pdc #%`=4Ņs« ~MkC?Pi]S{8itZY}ipJ C@Q/9U `/z@^Vsg P $vс<J@zcMlZ %1\rLhO=[b_q1¿38:|׍,Q&զxe&~=SϬvtiJTuj:ˣ^/h UVNĮ I^JH>.ereFgċBA[ O̤HW\8WՄ Hb $/XeV:nhk92,v<ҼnX"L L(nSHdЁtlzS;O6&LM .WH""&Hp02dojC~kH[ ̈́"x0ŜJp7 ^[D>xJxcoR 6(Ƣ#YO@}n/=:dl j8T.4u:̲_"t J/bkQ ?bo&&6TA Ar&WY8!5]T,ݚ|?{ a$PMxK'{ !5߸E ԁO*"0 rhH͚Rw+o`Բ&XI=^Bufyj`^R]9T7qk.),CC֮͵ukq]Dh$ϰkF%p>ec^1 @?EjTK> vˠ:g|kuK_'? )1?&f&`) e- ې i2O`j%@+YQV#N_đ*VSk gMq͙ +4fBzQp}DşiD٘:_Hqy vLp³_]AIu>6B#CLRV"F \L G"^*/^EEiX6;A`#jݻʡmiU@9<ޓ5RH5nԍfX+ea?nt[G{VVOu£NIYB+vJ AE{!lK8lA>m<5_/?C4?±fҏ_f꿧ǿ?F"}rtN_'w&g7w˗ONQsʳ;ޏ\?RLQ*0ZpIvFw/3H ;V\xݝ/:P7Mx~ۻ县GK |wh]]Q˹vVO9&ebvm} +6M&,fҎX;u;hJV 4SR_LB#N=1Qtmsؼilr};3z )|q25x63Os<|q+6Qas=^\ި)Z0!*|OM 'Dd/=9 N3~6 #ԥBfj>ct.>*3sa^Hnv5k5Aa4 cPϳz^î{&c28a"NX;,0ej/!4(CMR**kJĀ@6aP HgC2̡*<`NXERFBTp8ICmBˑ$Gh]r(_箺pN2%M>|;YtjJHa e=Z60έP:g`x6 mJY?z[iWf%! UBеIqlyӹ&3&"_0> &P^2(C'm~!?rÑ؟ID;eD @Lː%Efs +TSܘIB`{tǮgz媏tRDss,KOA>b$8kl f7]X0!9m&zX _8{wʉ?-w^# xxl*5=+w&/-dK{ft8id._Z-wXdQڵK i7l/^M7d;Qo;J'MIחGvHj3w2Au>=x,I{rr~BW{arf@wܑGA^̏-eȋ`N:)6Dщ8A)i]$XIxfz!%'ߥxqN9tL6l!@ g~&{=@|_OGG X4= i -BkbpҰ*aN̂ P3CB9(lj) !nM&80&gZ+Wô,%uPso^"o% r1YR,K 60RYEK{x#3ҁ g1JG!zRzɐp_7cyu`&dLƎmx/*.kPGF9n*_ߨOoAdd_$ +BE B&/?;{;'P``"H\-7ҧg47zmڔj+[}Oi[lmߖ[lmEyn -*B4)]>D'#R䭹)fN=lTjIr mNRiTX= С_ ;>_m({@nScw0.%|Ñ6Tb I yÑD  IgPG8ԗ"uj)D*cpk^kGl:z$~@擓)=O!'9IA2[.k$37ۥKf_%SdRU$,a$膦r4\ھĂ- DqX6\,1" <ɍ_cpRSL剼vH:rΧiGwn1C#%̚_6`>!Ӫȇ4F,qD$HmD˅0>~-}ܭ MYyIvPa -Mq9-SAQ͞Wܸ:^9} ,&wR131 9J2{\ g]mV@>wJ1zUM{'rdPOْvPHXQE<`퉦۞g"@J[qhaSde-U{њ凼hqߖ^ZDR?K8= [آ ^H6^0$8E"m,gH[Բ'F ^*2}0H74Skt3&<؍=vK73^.Pl@(6D]^*{'gAј-PDjB_@D&YrYZiBC |-ЬO3<$&:4%dLc*=j!,ص@-sy6D'.K<QXa9zH`SyJ#vjY9 c (.b@ђ\)i-lN[I8^`6Lm|(*\{)? P&k!N^ SmTM˗xdͶhmyp[ A|z`Bca($u~F\$76N5bMH=e#MRm8l HX+ 1ڪj&]UFU)V$bC"aXK61q I<YnAL…l$r<-"͙Dl §T'(qbh*҈s;Al %13rh=Тi&L)܋7 {VF ?1I ɸg7)G(_h3" c$S 17q Cd-9({+Dوds=DJيO$O6!aEˏJ n(pR'7q8Z)Rv!NcIDr=hu|~)q4gn)2EN-9å-"m |pLRxJHQף`K(Q)l0)aRZ$! yg@{KSB*PdUDTto3:a?l}.!r]7vZDB&{H@["OpCQP!8atmUJZA9Dux4i yeR`u(E - Q12(䨔G2Jq YhH|\l. NцirTZ2AI#[>p1T**HHU4쭩!+ՁpԬE#osW8":QV݂V98%f.-▦M#QBhoЉaGyP8flU60kA#ɓཌ4ZK< #riMw0ٌ3Kw "z4 5/7P|#jbL5pp=go5YXWiupG z8]ҋ|ÕE`2naC^zM +CI (To]'oûCz|Hnez;vM X`[j-Cг~Pr M9\wa$p`)5H*Q$Pr뛌4lbBB&NL|0 oʧM'lܺCl&c3nѡe;TomJQ :s7yU$p2 >T;@x4=ti_-WjoRë+%{$"''X&5W1^V82t1xyEdUbE5>-CqnBxƪ|@ojWؽoϕ1EDR 2PZFuY %9x]耽AʸnHT216D.AEXS5а_~K*)F]ҚV:S]$y0L⯠qի \=}"8E\ WtշQ]_Yyºyz!aCŪQ$=TNk"9֟܄t81y1dT .Nm$ $suDžuQG+ciO"M.%Ss OE;{2 x;&8_[<뜚ޟi51p;#A[PCt#̰,Q}D~qR>{س*#p4eu\& )>q h٣Փ{$(t2'QE ѱq, ? OiǮL^~" 1" $6PYF껙֟G_ͯ?ٯ|zB5mBrs?{pɿh&F ߟCD^:b-41|\q /wG~Aޱ#jX$N!vfFgX(0B̨#H8S҅ {:-V~r8 q_i8 6^]?u87%-s-8]$\o+}|C5;T;-Yli1٭O[crAqSÿq^-;)WlB~#F_õ=snȁ_ }G[g8`-p M:;TlX_v7~Z|(|] w-tK{vezٞvq:Ce#7_Ăn0:7yai4i\?~ïCo{K0'r?4qR)?)V QQAmhy+G"uM.h vw-:X~oJcmD0_hMCYڽNEƻVb| ;mDu^8-k DCH[aF9&nn5D>xa <>Ԏ;c߶UE^Qn/كm>=onKEV PS#u~SnD{Iuޟb\1I]7mqF+o_>O\شcLҭ.u{pV' ~Q@bڗ:*K cX@ 20~a Ei[365`nbV/u-:Z>Կ e1NX.DcZXZ&#F,9@ 0#p1!f5!0!sfGw0MϞ;)}_UׁɳMr:Xշ1vX:w'wsx=%{s@;rCJ&?q15k}x!fؼ%~KcZ}36-F}w{?΀|}FIfxT2z~L;i< )*m&4$h-F OƳ/- Ħ:-c{>4JM#cF774]`A:^!9~٠}yŃ?(P K~9ev[U!Hh"5RpgCCJ [a~-;Xue"ḙmEz9\j$<6^[:-vƀf,RmwGǔEqX@Cc.1p#jYV쏀c"{.9]"?,gaNܻkrʢR{1 L{.İ兜G09`#046 <[J.X6ԅ/%vWdfuޱ\P1S"rC ֖=WxNW@5EV }k h6fh1ẓ+5̉gьQ?Đ9Ƙ;N;INOQI&4;_2Dϭ8-An ҈qL7mJAcLVjľ>uq@Jh!6pHݹ\xҳ! I9]zLeܱw;1}2NKﱄrXvKbA*6 ¸nWZoTU}y}$)nFNwJ 7З.0|#&Ei->Ee_75@Ve'A4_m: (hhoy6#ELڀd5btГͰ,t){π ˠ-y%&%H(8W^ͳ.P]0XtC})\ /Ky .ώ_[ ,M0ǛZaC(;ҫ"[ˆ\P_vHvI$B.%}!. $.|R4&B41َ4jC14r8uNJ=o [Bӂ'A|cI㘷U& %i 2Www¾'Q]͠#D9CkGMt&-Dc7+PP$2XƱeE`p= >Hb>By(+1 1q|F~"t2 mgXϟ#Ke <pZ#FyaѝCuiAݫZ(%o}^tOStA7;eV*SP7)j>e;˰Xղّb!џN.~$Xf;T1y O /4 Р,/y# ր7jyMPǃ8e%GzݼU\.QLԲ2rb-qo $LNq ]מO$)6C$Ջd?\Ob?0 $ Bk~}!. ٪q;Jg_햮qޠb^ ?!oʼnyMbYKj'imk&48\v9yOr޾>!vQe9aђvӕ?}AD^C4{qՑ>E놎}*D(XoE8=oIhF/22+|'fR\j* %7'\)sHʟ@ 3RI.F7I3Aڡ)B84;PۭkCGl9w#*z'Oñ8OV6LzV5yOWJ:xB9MAZ"aǤ].JKy>߾/%Sh !ǟ-{Pza}8, M@v-O.\rxGOb1Ο1fKHoL@"?yPfBsyQI*G_?K_WmA|i'G!$h!ச-]nǝ  rpº\VD9YT+Ͱ*KJtUb)U'@N?fPqIݎrL2E=3eBy룰S!:2bٳK#cH/9,aFeI> L1y4#*>>4W$$@'20f#H<8E\GRu:~-A.Zxf"iGpj6J͹>sut[xَ7m+|BW=¿)؋P[*pfsUCCkke;%!a#k5{@:>5??7Recf>fFYLNof%ZƅLVttcMPpJ? 8bp<(# -! T0jVV8as{, (7qG8`x%AwgymAcKapy ;GatNy}~6>AoMI Nl<~B+*SkDs!%(1 f'K"7qGXW>¹v8FjrWOԠ8"Mz|ףknǿ݌Ž6ssPބbOuudlNibie ?8H>٠g 9LD_7˶vs<~>D/|pS3.M( {`A+J}ќlr8O% ‚\X)GXJZ w?58AR@A"ѭrN)؃Գ_e[2Ӭc7,J§#N"Hf%*:KňuaoΔ}XBD %qX w+8 lAWb'"4hj˽Kx$8E k>SW)zi<[M~F0ϕXI?MO1Fum19Z4?si!,7LQ7O+ 1,%m7?i$0RDGY(WM Y'ɾ6*8PUu4g~@~CaP#'^>H&-a ߒNBVTL*ߨ݉#zrl ܨFTn^9IO<6ۖ+ `!I CI$ }J[I$gfxA{~ @{j-`6WME4Us:PEW'lv^Zap f^a aa!O`0{!i *]n1Mr'f#iEs:Mryy$^PD/Nȅ?‘B!ܑ!U863M 3kԢVKx(&)N/g=bۚ;%E +2(! "r`fapO |;0ZCX= jŪebt"&Gɰ2 1Bb & PQFr``!+Q"v rݨEI,ɱs­kMaJdEԪ0 g 3ҴJ, Q #n9\20as?#H @buCwF6kHcٖF*K F"S3(4DlȈ'CF΃ Eai~T˜1}ƌAO>١]|Tgd?ގ1a{>Gl}['J}N B#-k">O5bW,E`˒ȁɁa-!@A4ZW%Ny94:DjeYF^*G\9#R=\%%CPr)XU%KXWzC݅ZCq-v;=H)DG(-Sdޝ=C~%/Q"Dz/aA81f"v-7GIDk5=,x-$\Kĕ|(.9zqq}?t=}< Jܷ}/Jdʮ=5K3KiO_y+|cH0ͳp UNTX D߾!gP `>Q7,~^?N&oT?R5uts`+}ITWB^fx!\!dU##yC%E _8a%~jVxXۙ+zc/d;74 0aF _dט?׿ sJl˒y;a. H X+ ;m_\A! ǟGO4s̻Y/v}% `{<x 6% {aTt;a!㚡r% $R_zP"F[6l de)$viɐڻe @0C ԷiE"3k2-_ eU-RmEVfʧ`f!#ٲi +âHf;z;a$yE0&+:^ *8+5aM~ioݟIP,:$ xP3/;,}'=(o.Oʌ`wU_3 Da\Ӣ  X`& w9 fZ߈67n+fdP;PÒi&"Ar&1@Xr )mw_JiuR5 `zi@MjM=’G*A\qA t]IU]ǯ< bӵҜpے1$p{]v˦oX]ktw\`n}S-mj)ƚc",kK&%IfA6Te:-B35dƐ#㺸Mɞn!zñz:ba 4P"OA'`&8v#ywf<βYf_MaSG3R2+ !WX2Z*DO,-[w OGƢ] *ZPU2#+HѫzThOP7RB*dPIr, Lct˄R%rݯӱcC@墇mZ%y8)[:/Qq9`J>r;y/v1 2'b^̑0Cؖ|h$W*!+]H)C>d`YEǍFo58!fף )<|MAQn8~V.T"Ə?DOn}@5٧&w|*os=8fǿs2u49j^ PO\ݏ&7(/C\BpbҐR$q%(MW?OG'þj.VtF+?Q=[ >l:l81$,ƊZB 4qjXA5͊ f$"rX'8mM1!~aE1H4P!G+CB:>L84P*9R&pj8;aX\Wj3'6!,ٱwoSiҞ@ub+kۊTu|+1pn 9{rpk6t:v֯לޕ؋*Pb9FIAWS; q{qn|g`͛y"8?gJНoDO;\Y )ѬA gCŗPIg?xg$XiL̻unJVF_`L3o >?M'7t+|'_d\ 5^C͸><ޏa }Mk$  G +$YvU@{:XѥiSu>R/7W 5f4l\g8\x6Fe-'v 2#*4@ns-A/7oGu^}ytwyĦRq:{hgwa>@Nna4GdJ%! 3E&HO_1J&>}&%#\{MoW-ѣ|6I>O<|2uߜV2x[gވ/?Epf=H!;&3m֖7Mj7mla-Q3noխxuKyVRWwV@[w8,t65Wyc,!r@I udF{<yrwNu s ^ܔtx'-K:8<9,!'g%J|eխȐP_{#D+џ0$ t]H/{=xBZ*7CyCJ]'@Yx:PL15bϩ.]{cih@Ľ/]=W%rB$&be8:Mt߈;davZ3 Pdd EP uޑHz|_OIڰڅ=Ut'}et$8?ʪ R;|[GꍽlZDtmHޛ !@_q"i% i#8tp,!;؄r F| O ^[o$<͞0 }zMF_'A VN/(]&6z߷d#TPK2J a ML%oV/n&GK& >SK~M q(~'RYx kx͏e.[e.`#AH.IgV!]"Gj^ln t娄77fwHFwd .6Qn}?7Z"c ڸOaO}^3 ˙Xj`@N̩ւ-[wI %ՠpt6M b\}<pEqrK{HݼʞMw-us+Kd:#bbE7a*Fْl'])'/D<`bdx  /#K~ux4-jn=菻'@Mao![ !r$#EVф T]ڝqeztn0ib 46<܂\e#XHj0]E$wr@Ieey^KE\(,]$y6iSi vպŪݓtCscպ!#%qsm8(HTJa]2n/n#$~v ߦ3t\¯0{PHEzb07Mgn8(RV Y{.GnOℴݨ OWtǪ?HM݊ wT.mY( .v]K_ĺH]V\btw2A~XiSosNɐd=|Pnujv>\{ +7^WpvE[ 8>ww7xjs5vw\.vm""Y-|<#[",n@q! \p)T{ a$ҵuRC1PgjEjH ōWh .HhH\OF3u^+UHWQ$4JzSMm?TL+R̼3ܸWf .pa$ *$ϢDV'IӾF3te)]_sebe|)kS:pzC^FdRţj~z]-QZ:m:kX5n/ ' fQ {/c=u `F3 DR<>\ꁫem<4 vt&"p2$'i '[8>] _չ/;n,sKv{C6oOP! ϷodMND8Un Qwl9s=sƽ7X!hbK%\~䪤sٽr"r8s;I1ki^#Hf5Od!S@|0xn6K3hf\,4ҫK߲ ݜSc1*/<bQoqH&h5Q]s\[+B*v]|TxۙQIqt\:x#쀋r$CdkN6X.*&R.pkWmӮ4ڝ8 ÷B ( F |XXc / 1UȜ xE^~8v-trz -ɇ;ltg<_qoŠ`} ,le3.W1*n; Q~i(M/p쉄n5 /IZXrɱiNTV'IDMJI@ l@IP.G]IMLء>z]"GP-^[׊;㽏X1Ɖ^&hXT; mzM u-L, NCM%1!HG?/w*DD>} !KizWG c3b+duw 2{Ƞ->p]dS"5rܜ{`b%0(dՅҎspVT(ҺvA!CyA Eh䄪|%B|kDSESûK v)e +aMt;A)Dr*DJ1)%{ EQ0?G1b 9vTo & ;1,hJjC+Y#U>Ek2Th`qO#O 8\_"1DZvk"lg۝Sr7 YGs{PxO}9aƂ0Hj;[O<$>ּ' O%uq p&eY  Z6ADܷm 8 "+ȈAIF}6d2[8У NĤCÞIMB* ,>jbVKHM|␗ш^ NY+#q4((`qg.+frqsɭ麆ߦ)]!V p{ď,NB>xKc' YhmIV&$!P~bpb4Kg@48Jjt^#V ^1`J6cBn8fktC f&lO\v;Z)5ݖJiMZ;3wwlVt46k |Ӻ*)˿DC?3rg1Z|Y)CT[{^RVP}U!uxⴖ{rԖmQݓfGF@0t✊tL'jM+D!k۠ACq.*{;-M|̢O+u}=OtIMby?ztJRy M^'n@sQu(WQHlU .ؕTlvb#>L`Qm/g* uY8l0I#=sCa^nb@@fmK{!\]wSUF5kdl%Ւ8Hm8i&JG"C$~o;_ $Z1bL'dl-6t<a ]#- l7^! Ay570h-uviS@hi&rN/5*N W@oCF,;d|^}/Z! o:$ሡu+ņsS$Nw&Dn{4lK\dZdHLnqmߟ,z`= %x,\bD &=OTw,41DA8N]–nog ^/eʦ|=ng4C~hiu]p GV&Q~g+Ql%gW*ٙU:CɔU{-s^0Q)Ϧ' E66x  }]SizBGjCZ~V jɱF\uepY{U;cqAHbSRZD#u9 |+Jɯ|7{}4{>1F_2i6z<͟>oƳw7'VIb~Sþ bj3b*\> .z]6n*,v5=?HNݼq%~Y PVz[TNo:~u%ҋ͆nMiSN=H\zs(JdؐUp]Y=;KDY1JDɸ`ۖ$q1aDW ١).DbQ=bKptR׺m_{;"9?eE]iR ТT=f bh+ pk1l%ռu{YSz<̻szqm`I ::AMM.'BJh쩃]!%͒; Gcyf- ; BbloJRfp"K3k*O!io$B&dĺ"\wjmVvޭAt0p-L+""MZ4 ۪VB6_fm1}/Bߎ" ~48R K: Y!F8PVDP*`pO9`0;zDZ\ne8OXqUC󎟀K+$4,d-O֣ꧻ_lJ^̪N_ܚyGGx7_{+@HxY'LGy. T#PԜQF I︔Ij'` &>8yg77VLMpT\  Z9z_&EKdӂ =`ǻ[$`040jXK-)|b8R/<I>{>81}K2ٶ2Mv&۷ \mowѠfUu!TÊFmRdb:i6s$jA}y[|͢/We-6ipr{E3YlP#5>`4=$~t|o* b([ΕܧyUr| x"J^v:kx K6rgqj* mU38>N_Rh]%PDln 1BBUIA3Kͥ$FϘdWY[ & Ý ̻*\)ŶPh`k48}cBt)^t,mz[@iFԿ+)i虤s&Vd)aOOd͆u\x;@];CH13(<j}^nn ҕHShHHlǙ~*t8F`~H*G#BO$M]8MDdi (iF/&"j ,-jMJ䮂D0Ƽح*Nux%F] '6Qi3-.%g9cCz"f!,#xmw$OjĨ h=Yp:EFEa9=?eJoW:9 [#^_VJD`O_\?\7>:-zm'M/ 8\r;½ഷ[nad8%fAUtp"R}> 3CX"*~i/Ü(U?*Yߋ^@]4]2Q0B%堮e rK7"f5PEAAao}X.'N(Htͦ=4aV7+6ܬ1\m=:77܇'ѧq QҮQm?sqx5~\t)ǰ@=4Fi[>6qYu=VB YG=/h?J=' ޅk7X$6olx4_U\Rz܋@II0Y-W7-tiFA<фU{$e"Dl>FR;/~y;/FX6]]c{vM_OqNIO=iT:?,:9CHDW ~*dQWmC3L(uvGŮW Hv !!6 Eti[rs/kEA*O}fٴ"|SX|+,\ a' R o@I 1a'ԭ~/eKMn/2_~ L43BJ}sVM2jAyKġ@j!z,_QG z0BA /: kP]s؞_(L7 e _Rg` 5~4{Srt0'O>Z wah&)V;l(iЃ\H`&/0H;V'TJ*K7yjn;k,`;Hs,)K7p?D!̴ʯyIu/YFj1IlX ~!ujgI,OCev~\8OnB?]i;c4.*[M;KWqUkrD/$T,ip2^ҔdW$ bļT hRXt-ª^m2iڕ!UB, G糔>?W|B|7F )%xZI # ED؅xφM3^틿7v +o̾yQ(,vK㼯kɚQۥ0&b͜)1=SKxYjߪuԆDo0JMNAA%8,X-S MDڥnj~!B̧Eѷ i` rw#tER4ۓx~+%eZ1B P{S; _~56F 5 cH?,!hIaݖO2Q"A )7;(T $H/}& @kT PoS5&3 K ;yCl+pV %i;{B>T7x;?4$B(JĚ_mtKFB& scW8'YGalUOP(TmFe7y_l#BNW=`t\B-Vonު:{8I78! [XwdKޗ[A1$L g5}pcM.~!~TN2#^*qg;,R0JW ʞAENӅ-K[;~`쏚mtWw0_^D#U P&@`MO?HS>=+Гm?&1Ÿt% jV,HApv%  K"x/1u, ɷ: Rc"'\"nĘ\OH jr6&cw,6v!GldeOE ]1CVq~'8}tIԏr#r%x?u|h^NŒ !mu0E&S$ -x^#Zh)nTW1<&3}P8- .Τ*: BB&N6m<,%z g{EpI#1XM" `>F^ѯ]2Z-8{@: 'uOlAugx8"^E $eEbt)*#aT+ALdy! Cێ}_.YJ & =I-\M>{,*  :'N<'."!D94S ;`bvͶ>OrB*S*n1KXYg/iIsw۝"҉J1+$~6'2sf+,Q4dmcfÃGa)^Ka-FA`۝_ lumC<ٱnmpDVuƬɍ${|pvn#a-M#|ldkѵfo2fqL$ `*SP˒'abhW݃Zm$`&jAA5@y""L?p#i07={#y_dzy}{7wcq:ߏV-<^2{tG৻Ӊ;ޏG1oh뛯wQUw5N\? ̒LiKJֿLs0~ĉzt_O9v6y~vvĻ^nGag$E5m+t64fs[x˪J{ŖOax85 bvǏzⓒL%|?v;74Bsh :fS>ߙliXF>?XD+#h\$dә\"7iqckAh28;;Y?:'_ovб)kyRl-tO/Q<9k8Ȁu}Ж^r8vOq|`R-KԲb0c]FmrQ5z8mb ϗ[ nKU1MiKLF#=L(OD%:#цӑlTot jrTw$8|n+(gXgImϚ:8 uwen/OJ4±<pw Pv#.f B?uxs ]z&MF~t3&Ow_hRtӗ~j_qFqhŁ ̓e05Dl_䃃4!īOMk8w#fPQ7+V6õ8{||9>fpX"'ʀ-ŻѤNt}= ((D\/36:iKӠ4 Kv@QjŪSƫ;^|^bv0f\_G[0,)C=)5GY/~1JBiSZr(k ΐfw_!l|{66 MaG ęnKK%b!{V!-F_ڦ/.7g~zwfiH"B{ 7=# ,i{^XlEg'=Tb.!*b[x?iw`Z͎~Q7sq?0rw&peF'y=:ϲ4MnH+3Oz:6\kST#4(  $~7'e%1Fi8?r<&S(|rjyeqGԷӇݤwΏxv|M=0m)陞TUB\ܙ8g#x [F 8' x,WS~XID\t!GɀgMv7LHhUz6Hp*sDMmED^ᲁ.$l!@1LfU{Jiu7a,Bxk`3,ZFP>-q\?EKLY&xM@Ljkh %8a=u}0 +6/bwzJq~Y>oLƝf04G#eKiKs`9br'\ôRqoavóhepz9DzenQ:4xZu g%cZ Mo ժc;醊AhqbsN/Pn/ w>7WY`d fi{_P)\]rJ#۟E[±GP-g*2g/\;" d~meuN]h{C@J ?TuGSZZs!nc3#b~kVVAqؑ5>V,+<+8S}2=1@DP(2܈!oz6sq F>Tmj'!(]PLBT͚e?9$6(K^#rڸe@쩒AHkž5`iu>C,ݩFϷwO`8$O,7ikJNR޷7vq3xwJhfy=BJ`g}i<)e'" /lX0j;w0㛻wPc9Dix>OS~/><7 i<˰Ϸ&'B =}:}b $wyb8Thkr\/7tmf7}z9oqD‰y'?\O?Y(`dF7bb}1;MN/iTF6]y0} _M~sȍㆢx^Q?'?<W1.GC{v.0Ǔ+MyZrMq;}~Lg8oOk~oڌ|o\i8@`lš[(u*\aommN2sym=2py9u/s 2 |3}ē[2<0;ͶZ-EM/ g~K_Dܧ;!dF!hHN'6GwɉD 0SnTm )n@v oDlJNў"h `<#q5' l׉l#8"k)q #)*l "ך j?2 WNJcC2\@V&3W$ana/P:Hµv l<7$U3=FmSx~ÉZ0C(vF!浻;(^I.HdS8dSaqV^H?qITw+OyTn~&U{$'g4i2n(W^ Ŝ=fIx#\5? $P/:N?+8N~k]C Z y.Rw d >Q*AEj(Kk˿!Ь38|P1vympJ$cr…=8-rx[bЗ+R\Z's,ԼpOve)΍+N4=J!!ci8b%frr7Y U 5 >wfӂLg|j/'VӠޤVOϡ`."&YAe=Shkj@4e p +6C^Y|ִНXkZ8}@.ESҳqV$-"^8Sc(? D cA|M0垡ZF=j@~ܱ=@t$zA!(9% ,kCot}n;%\@i+P4ո!|u|kD1~|@c鎱3Q'C^VjUXZ<)jK4nT95P,`rMЕ%#W B<QC^?jQ a"Y}Kl3:S2nelxؿZ`/ h࢙5 c$rdXU8O QĚǓy>>՟| LQk:8lѧ8Yc }NLvC, zv&0b[D?dP< _`r%EW)A2a^:פwBNqAdou̲!.X]! ƒHѺԝDՌDeȫ5j 9@qX]p-dԬ""_]K#ɺPY]7nAXטf^2U_xN2R~HS\Cނ4,]tDb-(@wKdF t+("I/t-H=!D@AlxR!3Il~8-Cuk8 8ϋ*h-zQv`KQF$x%$*e)b=l iVw1DIv)a8-hMRdk1]nryX/)SD! E A<^HMbX+%m ^}xAoʭ:KJRp\"oE8h~~Vĺ #0V7v{o A %;5Î4@hay4g x9Ry 1$:?G:ȪS[u fLn){M|lw.M]fa*ml"ls:̅X:t&VlCo47\6 "v~$Fb0)TNQuؐ 0.~B?u8 @7zA+~703l+"d@@Tmyh(*nZ)A>]d5xRx)o"+*fxz K`96<ƣ,s@ǧa0Z 4a@aw_pj(b*TU@ᨘ^fcZ x|OI"UZЊ oU^Yt?$D׀uEKs m#OFDj&B+!1:,Bjա]l5~;.;!kXr( qDifnJKo&Lm 6U SN#~boݧg/xc+->xi-(j?%҉/iX6gQ`xma:%H7ldX\bV`CT] qsF[RS:$w#,נpH,C]/B]H`/ztv/o?7 㥃 ikX!\ߪo$ƖEP%tcjWWJ}k\6*jS\@KN|"[NKMhױiHakXXKpox覷4y٣ ߎF,Cɝ<9H ˩1cD|<|ych$ YܘqnbQ :JHT9 3jl$v&"b%0|:F#IPDo}@ė)V42#ۉ1-}$MHe Q۫MNטEaB6" -n(FǽˬQ o[Hkl!ʄi0^ܨ{0I["6TpZW|QnRSۭ.~؈!uN odDOq;Up@P4tAy Xv4sz-~B_`X6s2ic-wň AO)ӴZC/.Pt5-cMo!Wn*(CSN4IcXB!ȭ_lI 8zMEo&;1706 Nۈ2 ͻS L xTR\F[ҩrNJȜPx4ۻӟI$N_>7q]098kZ`BL ^<1t_>}F|bLm$4ᐜAx4~<ؒhOc~O}aD~.$U @NS2R @n "*dRNX-1ө%|_5V" cL3F{ o5u:y "O"T3hSGaƋ#GAB+}>,-\5ݔH|< ۿAS ([ $ {X:{|&4Eb@ q)ܭFAtS׺D/,#*DYZa[Mp{$X8хZܳ?nk=߱śs2[&e( 'iZIX\[BȹFtBt}2aw Kvzb5- +iȏQc?#-1P9:+H^4VQO'E %V)O( gǎ*f-v(A@IGǫe:T} cyrS&E,b i6ٌOĭ㧈ICԓY#Ӵ?GnO,&*#N5yS7VKJC·Ә-axA[El'{5*54߱Bj?W^H7El66hƘ9g9,\tn|+٘,I5}P!&3Ol`'Vq?mݱoW-bqHR_Jh(s .S|C4lx ":7#${~IXYSn<'oT X !F gC O`<5 '9 pu'r&2?'~cD[NKݿ.ጊ=s%e*)/xlg,BRQcanA"<)T캸i9 nԃV?p_M5 O6H1l d!"aBQ[ufOO:}̀?xG bł&S DG<v 3.ԁ"6 ihU; /tQ}pda/Y*Pos$ r0 n|O1H,=><3q|!ơJ2h0n r8!#5yr_\ +~ NL;12N&X.&v FrCQTE^` boN\(e7R[pI3e1XP$@rjy[ dVZ^Ϡ'P.6ҡsB3 z,ޖ1%a80 !aЎb9бCw 8Åagx-vԝ Nx,q_[FC-Ķ YwVV0M q*jC*31sL9dJ[xYA>S3cuC?yBШ6>Kɳ,t7aWh)!S[P6ex;m @ }t>ϟaݪxi:uG r[HEΠ[T 6n~])( Qae:3]>e#1)l-óp-]xscmTv#oQwLlZ?0 2`jף|]V4la`a}0Iί\D%|EG0CL B޳3p ֔]T= Hߋm$s)1@v'w;ex2ތ74sCx/l!x;b3Ugs$FCe`"vMI**AcSy-dqآ[a~RO\dr aɦH_]`l`t,plEeɠ%$JDW.nMiOm&Ū%9!'{IGNBEjJS{dπGzr0c3o 8؛@Su8b$j7"l=&.}f %sqGgj\}e<86@K-'(*#2%L*GMǂ4@{*&5[V>5~'>?Mɞiʑr 7ɡ3 eB4Y.LjJT&`7a;NcTs%b N#&/2 [t/*ngQ= G-ީ#fƓb4zxr'\[JVaX^'S 9/2O=*h /pT3:bEB"ȇ%:IQ~k7I?"=P4GYOt),lHtK^W!d+0..d f64??%a@RN?M>O&8F"Z%x{ )b*$3@<^p$!Bc^Ը>\ӷj|f@޿wUX8yܯ-iVZB1Uy2hi(b]aX0#6뇥'";eYnOɬDZ=C"5Kf(iP! d+0ud#ǜd^]Y;ͭt0ē*CS0Cup͝,wm$f yӅ+2n6R6t滥_~M9\j9KJ(lZh v^WxT~,TV–M朒hhJU` lYsNL|Ng%_3W8Y(gb(J<0"lN#:Xwx*(؈<*=]s؞_ * ''XE ^GOx }0b&a@$n}4BLL[O u4e@ʀl<~x|*%&?}2 \,lDO1vWқ @A?ݴnCGt W | }Jd(=>?|]t>͵Y@BA>/Y$O /Qqe:wAIRN } /X7,`t0/ﻅ/8\ĩaߊg! VDD$fBm5oݼt[˻7gx8$&$cTaqOY$SIDzqD'ekJ;â;b;";C::e1xΐhNI, S)Dp7e#nwMrj3oB:Yps؝cu0e2,SeeaK'K K2)Rh8l)HP! !nQ=~Å.V=I@APDB I$얧vD`ʹ92l&!= mA @! .<"aw(24.nd뫿 $1qSӋx0nэF}.L%`y`]eQȐrO"&%a ,]H7IXۿ {c,#1=Ju2븲)Ɵ݂YaPzjlzmX-Zr6 ݗc`s5?ɝ.\E%J@ZZ$^!wp' t|X=YQ -Q$kCLY"8E) C 9 bk͌X ONr͊%E3L^>Ep]H(aa"y'HLE(\UDڗ]#/~.Tڲ^me Pk,ƺ"VI*kx~ @EVc~9#sn3~z|ȁgquāB! ׻9#GYȭ^۫2e크in8X!m &E m3 VB~C8N3d$@J~~D@Ik*!GJcunuԦډsLPb!QmU}!~@KHz+&<ٷ^n ЮbX `VȇGAf>< UA|sH&ŝai% |IUI'ūVY{"nїl'-RDa۶/XL4&7KG8mZʥYG5V׼*{u79 sD'c4CؘY) Fuj͛ZR0P4̖0KU4Yb^ 1 1Wx6FثAgj*ܣc튫"겢0K-j\\?wQAq RfhW1 ?.8U44 φBIBRhšiMQa| ANx _" 0"34;TqbքJYKb%H+TV #I`ƁIrbF! zKy<]W^LlA83R~_̫^9x&u= l4޵'Hj\l3v ! O_HA[Tp$THn vߐm$@c#Ѳ¾at Nݱ #ΚO[k ._vfՋ*~;3і\gSWxp/MSAԶrPӕw H} 0OQ- fAQ%˹rު@uxfQ; SܖeJ&JI>|0h-ɠ|t0K4im JDB7TvszƩɨ0WD8 aTd9&F Pn|K:k0 %ooӉ`hi3s(\\/ /\78.tLQ * y|) sw`b} X5J`@V 7‰&_wfr uGkd8][.r _&#ɳ9V\vi.P/I>پ{b; k"OԽ P^N9L K9^hHsX4C2=¿| o28GMZwTqV :~O~eqmկxkjJF_W5 onW'z>::8̗kG2Cɥ Q);ⓤ#ͦ8B`j\P᷻t&Yæ:)%kLQx$<fޚ7 -foc.*%qGnpv hsEYB( L|3227jH4ޏsRIp^r?~фioO5+)k@Ìkjnl1qs S4TJPf's ꑅLU+dHiSD ^Ĩ@s^N6Em^XI_ar߳1 n(Wy:~\eh3JlF\OsmL>A,st,& nGn`dHHAoA"jJ.&II/m!p1a#R$wmsV*(dZ}vcgc!IgAF I'%h"n2{ihE2"Cj5l,E[Bo:f'z. * FLY@sXG}^KxHVH @VMص|B@Si*q[o؟A~w(N6E |grg4vBR}Fsp))T9> =e>$ tST\< sk ( =e>d tS3 h*ދkX./k6 GSaeo1?>N1UI#:_pzYaФ lp '/\=X~Gզs_%9TM;節%Ydt%aInJ}#r͇V2r$8PqT,OZSHJ)դҤ8aP%)i%2& 0ј`wȏQ  882!$eY%TWʊ쇅Ma0p/F QfLpMPGSw-x4M;clk5I¬"K!tuֈT{ޑPTR&O*#lU(DjK8'Law0A*6Y3\%>iJ8DOLq3^:{Xi4Q ÛNtq #mO5^+pc"L `s@UTQ] @w]AB0V_].]|QO9Ma}rHKI6,;!d~Lԅ&(Ldp1qŔ.̣@,T9\)$9C PT;ipe}J/hQ4TtCG["19ԘN." 2{\1A3ȆI^x^ ٭pCt3]N 19 w%Z\l$<=p )ݯ rd2ӳ#4JWő"g;qqlS$I&"2WQcAϼ !]js\$㨊|s *LwX=\6A:j3ΊYc850 #ZqCLmh43~9$aƟC9 ϟȦ ?s ЧK'O|4n%]nU??'7]+MCwX]{4l`֕Z]3lACgD.;܏@ɨ˗(/nBtc,HwX3ƌFnȚ̯|eP!Kǩ 7[,E̎'OfRi]|erWH`vLetݳ 4sCTٗ(i/QԾ㇬0%>B ʌWc:Mh^<Mww?t$0egsԇc˖g$ q-r{T/)X-.؎|A,c0_< Nʭ^()=!R RF)a v +EJ)"p3 i@z-3O'Jֆ0kQ`Y",Rr9T?'O_Irç// :hY<\J6=v|FBTV|sL?=@R@[[ZS v#uB4U.IȋkD:)DWea *d".N~$dXGŧ[<0jWŹ-#I舢ɒR%h;j/W)@:Gu]FjceZ`X=լ^Cq1`MǼ}(7Ә!Z6/h0s}!=#t-LvE~}Y<=T d "DML뒥E#vԫWyˡkZdO1 Q(!qaՋb*+io'3I*Tg=qo(bv'@nD3._i6$h0|_ħh+I{\Z6E%\Zt>EfȇGRp>efJwC3I/Mvͱ-bךv-veқ6 ^0,L7 VXvK-GM|OZ 6R}mA8:ٽi9uʶ}Xi{)!FZω@n%%x0grP>8ukhm6+ۿ*sp^J:^fF7qDLRT0 bJ.9+wO9_]v[h+N5;oMy<}r2+z"<}ݚ})xϠ> XZa&H*Sќs¬):d~!͞/`zqI+4:Ln5 ,ҽU"`.a1b]Fql}z+(Fۋ8Qt; Eכ Q-xh6Bc6to{Ë82\eUĝmslDGfwվ>fm6RZ0c9E8a~s4n3ݯnW/̝9e<|1^mo$m*h%ߘ`I Tt=aG}wdv O_p<B`GܞI+y=1*szY'ܨcdd\jDL*ϡɔ^9^8ٔB ӑ~w܊_nXr,go?~9}12mb@S~__4D勣iyNgF/0%۠qo9KßF[\ZGFc5shX;z788E`2Û꽼ة|X˞ VH&D#QZ/ Iq́´.ͨbcd\%@,=P<BiΤPɠ=*LQm䁛 | j2{ Bɚ$I C椆ӎ+‰LH|^BWѱkNgNNdxl@bSg.YW1%Psα 1OfAG)Q8r,I!%Qq#~I KN>H8`zFV<КvґmU/sD?%aT-.#!SOQ~R[`Z JByi3dP`X@xYr<{ޯAJUݭ:" &" plt;CnXkv|P v gЕ$h,^]Ͽ%0? ?S'88q_q8;MIB%-ҡo_͜,q΍?\krU8ؙeu͸cs {~RO#bŦacI|z>OIC9Lt5ZyKqS4dyFaM_9^ryIVodKGj⧕$B fqwxL=Ϥޮ{7-O< XtΫJFW,艐0εܮzpG>t~D7< Zf!oҸȚG$N6)&* uN:=N\{?* :6$=Qױ GLN9 9C.)XS8aPbsصNAQ'EGVd+j3%_TJFEcbMN(2M\ g$#-)|iD.|*M1` ( ZƵ mR;@r\R#_#DĶQ U ':`gËy,xl.u7?SRdX&b\#`Oj{(G_6m2 ]:椺=lZܴ)~΀6"U P]Ԓ0^v?*cQ^N"a"IkGR޵mP޵6MQ(%z^⨗ɶMxWDSɨLj,^ 4;RN! yH(_ &Q`pDI:f.&}lxfuQj)7O%SlL^Gr?'>+fX] *p׬>V;1˩@VO5sz|aƐ[Iu52iv ;viSdqx1+{^*2!f B0/8C*@ȍ5m`;2Ȕ 셚 vr'" "qHsP|c4"na8E92 J lO=Ҩq$M57EGȎՈM/Aws8Hϓ]G:`ӤL3 6NX%>+ٮ' UE .MlE&If_ar 6Nǐxͱ՚^Xkc%܂XC e%O&x#)p]lyfflSp>O ҰQH UݖD7JZbB#{w>@B}gQoȇM#ߦjp TݢWݢ)n xFp)AFDp_Nkkf-)-$+fඒˎ?>RHq;Eij!cY8 W'9-O^^Fb-rHD.JXm c/<G 1{F^z)d=$ȍZ>dz&wL>"}+'X ZT*ʵ !W(0CD#{/Ⓞ>Q YaWj;Lkdmچ\L I" r4"5NЇү^@ReI"u +NC=-OoF?=! ,Z`W  IX%H99dEDrU瘠ZJ%*ȼB‚z1A]Լ-YlM DK&ɨ 򊏤"DI 5(vW,Uf"iI3tWE/BE`yjNA6Eʵ ?Q5.BF5c Ԙ$8%)MP[TFV+$.a1s='MΡpv/mH-$GI™.3N,86Lleї ht(ΝCkh?"ꧪaRe+?;~B$h^XUEeuh\Vm'ōgp{bu;li/.ۅ=X疈Kf 0⚲Pz?#Sl[zi:{lH>n6yW,ћKU갈 >Kyⴳ oP#+WpH~d c' ]}")q(wsU×{F UX-N,)qZ} Cۿ/j1m)+_,*#&НC]#XLIA lv[-GɷOYmH6)9[;s0gU,PWDIz:X)p9 wm-.Wx|8ن>^mƸ`Cӂ+wV)*j{h6+LM0{ =WП~:=WPZfiE85CX{ ;g-iam 5X{iڸ"u)vÇE%iI<_%!tDuj?kɩ n\Ly 'u;$}6]8a@Xk{t3kT μI|/5b$!0ow=s97_ ߯I2+(c|kzk_E( `#u^Yw%t9C~669Z' >KSDysjc y|'QET9ҷr͚#мἌX˱"pa ?G+@ X P~՝%FM`2S6D*ڡ kֻ jۃίs9ݩ2QX ѹ%:TU k_Nx~]ik uNQ69զIRӒ_oĂ~u SV V S[yiںcӺ]xTwqE@[kp4\"e9<5(s]6Y=PGCbf)8vϵ@vĴ1jc P;60k"]\[j|C{pXQJ6%d_;TUfɪJnpA!'Wy LܚhDdƏy"#Y~CoOE?|Ͽ_S[ ZOPT?_OV/?/Vcn;wۺ݆i9-mnujU:- ݔ y&-1%y(2_Ůur@@ (r{+_f>* g1l ׭"QĹA 6 JLqhG2R0/\ "^(R#0YhiA\< Fʹ۾BL&^;w`rts|_\wBWU*Fco?p% C*N4? rc(=C-,0kH'}$ڼL*k/xء%iw*7}~p 2r|c{B.⽘,Q BM%Y{+ w7i䏡䏏7WF6LN7~*K΂gyx ސ$ it4FCGWhr :11'~Z58YܡeagCFg<_3<=.qg7Zx$ gmBaO7н4'8Sg}\ӓj ZOA \ǧCeQ(IBpa&Nf?ь}n] Nr xWC6!5v4?)-3 Plj (_L릿ER\HBB14, > 'fOp%z:YEpiJO\r9ÍaxODy Ӈ•=D/F}"D]vW˛pacQBNz*q83$пM-2? >BޙX8" bRģEp8Rn] 0W`E >iܔEK7Q ] ތ\ z!0 FU~~$, y1Ҿ^tH ?V!muߡs!\;pfw.KJ׮ʠ ^(I!<S^F÷ףK ҙE ݶ \5] >~vFt@S)0]H%S@; U_t,@(9OpeF)CICI\=p ?V!@YT {XQ ډyf!սdtՕW/nk=ЏIH Qhi4l r5zH?`y^ +33Ka";C'~ڻF*.hK?\>. I6j|n ް15Xy5~×~L.ZUmVšwk?o|y2<~u=s7Bp}{q9"tZ{.ՂNi4|~ܠ=pO!iXBaq^wy[nzV[0%{]K֠tF8RDp.kut$<7loXPC9bBSE v |/8+T8?Hq\?^T.(zR2| liYŰ&O,/vzykmO'mBpF9(iy]SO(J'yq 8jkqIKϫs } L˛fi"iyL8M'JwN1^-}h('>.W V*$,N pT9H"73lfKGKIz9sH/Vr_ B4`q c8+lLכ=Y5&eGś,mQKpOzRYH^ng27ۼ7(D01\V1C'|3,p..fӮu0陦0,a?_rgZ,xDLgJ̔(OȝH e4iҹ~˅7>oq&h.hJM_0RjUb9D_7܄s2~/ۆb67e.&_} d:MJ$"0E  _LST *x;_-$T`>tVu2~OLIHgEєNcӔ7Cɶ;\{_Bp!zE6h;/4^5jWz">#d8?4_%{f>>y/W#dߨ~{B $lsS$hzN"ѐ ]+sGjyưP9K kzDȏ9ۨ)  E{ {;0rA8)XiT;#iSPSS^=QxTPhX^Slst߿Lʬ)9Y [H %}Jٗ-z.zr{Q{4I Y+<,K#,-SC|!N.@h㻮"[D%2_Dz*qdPQK- !Af(3m(0z-М4 G115C]?ɭ /^)?ֿ,0>J0)a+\m8FptĖ,;' 9X9 Y&T4984NvK<˵2sS:χL6"2ӦGe0)|>vNSJlӸ|l,MUjXˬëݕ ]yFw%Xg1R2:78ĖMJx[n&͌vN}=y?}T䴐g mѸEE qZg{YL6 eʃOck1ݶf}|N.,0S$$J0Sؔ[,N$u5 ة .ǎF !܅wq.9 Ěd0mlCW{ܳQ\~=*1y$RU2&I: nS= liOdy5YV+*嗜݄& Zϙ|WDFɀsiA}B9уqZζʷaFCQ L](Lmi⌼ QϷ?ړH_<;ɜ)D){BDXԛLJ.8Dl9%Y*/҃+4}^bs(I+],(Av6ixӌ\46]?p($!H0*̯M]''.U*+)bοXX•;hKH-MS~twGvtϯj~V QwH:(;4|5~F,(rGJ[R_KD6zq_>:2⋋+M@ZMld}2Ajq1:%ڦ[9g?ZCUp ً5TCNaR(~yj$YW`/{กXKWFV|9xsll j uKi1CVK Yܗ~!$9KjL[8rxd`t[4(.8 eXF}D豗{nG}}U/NNɄWNe*S4K!o4zG՝>!`\a.笰<@*y:Xoåf9oI;5!Y,I2E|V*&6o 0糞:iR [ۓ,q`Y1"TbTwχ\g9BJh,[+1΅_/ %η@PRqlg G+"+.{J_T~x5qj2A2 trp } GnbUug59?K%!O`rS]>x]|i9ƐՒf_t\y[xٸ C6NOSl,tUQY> ET5ci#>Yw0b-5:.Z]Fi~;e8N:P)f{Dw'\/n! ^AGdnx#$Պ@Og+cëհ/Ti[}Q%ܛfǩFU!ںa(_x{xx?^]LMSl̔uӧZ~Uy0wN SiJs7Np'|ۚd#p?[䒉Zܸ1E4 J@}UHmaxeO-tupqv/wZ4-F.}x+I}{{*l}?J&0u5 9ԛ( ??}#wع_w5_WJO&F?) 5e1zJ@o __('NjɭЗJm"Ŵ#N]Zw%Dos ~yӐ/Р.4EtuI7m~ϖpYA_~t$'5v3 m]Xpff-ax%4pj(<6(YUm* p2/YoaoUj{ ;dj;¯!OK͖o"y'z KOp:ݶrYo.UEom2]SmZh2%C{ 6}R! \>,Pc;C8;] O41>*/P è~+[˧0 )nhD t Dy~b;&~ NtrܐmaU>L4q3kY?֩7ѽ(=;a)W+=t㍄gBzD g,_&(bmKOYCJC9f%ͅtveWw.w'W 6"(avwK~N ++1֘jxhIXUbG.$/im38nW0MC1"hnBl3u٫Y&Q7zHCbeѤG6V,/RS\f89m0_/X*TMD:Q^½ä<p4Ly&?&0-6b ##ClbRh ܞ|G50^`8V_QA ƷDFt0|UJ:nxH&Pl }4r<^£&op2LHŸY܇ǹ!6 qAqAB5۰Fq\]wAvՏ+H&h38g .noh faaKqA(1&a1%%(.yhQp:]Alpe6Z|S ÄGGonI TlP*]Lw~*MR%<> F# |c.#=t;길ţ}+GOdk{93SF sfa.:wd&eP2xđ΅tP`XyH?Vm's da5CpE˰yHx3)ܐZRic*_JECIؑo:qp2SŒk/J8GhsUIvôm}~zJӬafC vzI<@a@ ' e*|.X P5B2K*ɏ'H"<@,6wCme;yh -딕r/zm>P¥!f>^Йf#hNk1UC>"jv oū=5Xc-@6}M 4bE#~ Ta+s4e0SmZ9e [|auh ]c @>׼RM^= 2p*:xfsvϐ҇9ÅiItl=~5.$~7xh^q߰p nACZ m>F 65P-2g?`yehimͺ,3 T))1Q@DP#v˭3-Ȍg!LQDz˘h+J9ީ֒x jN-G]w)wa>TD2}un3a,iOi˛Q5uDLydJ +)᪽ p,@%ÞI tgNjԼi*{-bN S:F'w"ݨ7:8F !qtY܎>HL%ͧ`xPɂ!_KJĤa+i&@[I[8P/(p~fDDsuz.^^_VKa~z v=)iq??>^+2Ii*0\+ĪRS5 λ}.y6w3购 ?G& 3#GHռGw:gs4T3sڟHK9ʗ֟{# 3?9*yv𝾍;tĝ%J3Ta:[o1#ixU>~l& b(a&(Ӵ v/z+hfV/Æ,7b|};;hb dCTb W%=C,M,Yy[X:ZOso oJgEώ3Z^>`8Iњ*g'pMnMFws< pebKzDd`qÁ]uĩ)$L^ تDXC^bl{JbȌCšj^3 5eCW;@0*{2<,jD3ZfVx+4).T"&Z8p}qEMZWl4$G.Xf*yAlx.nZFcR{YCV !}\tdXjHv:1fcSfz^T䢨E҂cEZ$Χ:vȐr:66m&AKRR՗kavv|`&*⌆|T%tqtۈCy5b5kpqa.+"S6bjBrx:+9cy֡OL:i4C9 M\5ˢ>cX#VrRPQ R>g6T#;XLMy(狜F4bqœ r)<0~( 2T!pbdx [R(B@=V!?,Y[ \2:eLYW)EʤN=[q0]Aek3q %CdCg#d)6ERL$i~j$A"A>l+K.p٫@m)i V{qX~OdEܒ.fR+A1G/Rn׽Ϛy8 ǰ'x:N@(Vl{GGxOþɀ d3M7dɝ (aO4I_8X9@C>]`舻B*+d\&h :t>VP'N}S)E}bRQɫ}2 LSNx2oaL8Ouŀ)_faG4n0ReE8"rLTh^G?TV=ޜʃ(x)3z1ܐ1#^k?0C>?GP5`@$q6#P̻9,g5XwDI`=5{)6}Vy:0h,7:]*mP4UiRjt] xT/d(q ϵ>M+ ki-X-T>n#3uQeS ڔ% DPQec1;)YaEʓ%)Wx9s[l"9hǭ5<8[;`wtplJ7x|,cb1lp=PxԴ, ԃp[ aY7 KP@ûZ0iͅlce9ZT&ZQP?5|(?B2pOj Fk:[+qqކc8Nٿ!*wb N~+WN_3Ɔ ):_ /89l8.? "A8]\݆, $ӋLJ/ FK$auH.k)+MIӥx}}i.n>۫_/.:C}rk_>I>Z*<{/Y *G~K?>_6&^rՈ\;0u3(6l.1N+عT=0\7srEwε"wk㖪'#E i`:~> ~mD:r6 [ pg 9GDgwfg: G-Ǽ*>?=8=aq\Z3Z9 FU#`dXpRK˷,T8++ju&0Le< cwЊLr]L,[%< ڄ:7<2gŀid8޼<>Hb 2j^&LLjͧyA#ߐ:\njHQi}ڈ &lc Z<9v N׾_|$ \PvM=قbtS~4.~j|E=fܡq@Khg* ӌw1v?qGbsqSUm+ݯ g#=*jὝ~j|꿜ldHGLWxlZ !S>WS0q!2Ouʾǫ[qH>Fc;Ϡ'ٳOyMBr~񡄠T?w>o(̕>|y2|FbKn^Ptqtr?xv_+7[;Bw{_]^Wpp|6pfOKt#-ʯ,0xλNdFd{tؘn9vO%a% NK|$D,#:0I؄xYogM)ITȷ|b.q[ӃW#P=SB͒tN(v5޼I /de'CB[o~J9r;\Ä/7vhR\s aֳDsX1r;-?S_=:taLlXoݺhu Y.;E?ن[8&*r{$XssgyNDV)9!:8T'e]خhCsUm<#s_(g.:+ ~ }Ic])⻠S%ۛ(nƛNLN] (i:jvr=p[G)-[ɷrbK5p0Ŧ0FDhkW`a 1FOu{9$N}Z(n0gRUt(5Sm:?DѸ"*ח U/ +z,׊Vƻ.dgMk&si;a| NRa1h^Ј{$ᮍeW!{|`TBjK!lmX\$r#R!wB!s[kT88c8_Z[ʓq=5S  b5$Nz9-f.:`H#/ݲ|S \)!TH"{_.Ɨ7pu'v JotNk";żaGDUu?o8\-(ca?f6*D\AfÉaN74JQ>XW}a:0)1` fg$žAU` ͅpڷք.װEzJ-pjh4q&>h_݅TVPWVŢn'x۝,ET=!q7z 2)R`586"&@?lm/u +B FV`DIۿns&6,߳P{"ŪoKd=♁TN}͕ˎy9 ĭD|HZ"!o#Q.bK\I82{fv^N1k!.l> z\> jN>^?G_?=?=l`hXkLFbdphx D}q//̥NAQUyiΧ`K{X囘@izүj?@hM_ ftѺQ#Si:DJPfHvWe:~k|pZx͸rUr+Juf9@,aYY%&ۉ9D0(}ٖ6=#B5Y5<48OEc%$DN?ML!695Jo_ k,t}`r` qіf|UJ%SD@ׁ)kDȷ Qe J Pϗhq݅tG؋e?|W 'Pw+ȑ휠رXLG zu;352KHte]}!cq#Rh@׾ZS H8TtMn KˀmJh$bJ2>]gZP+/mYI95>'u;Gl ^J׺tyVx]pŁ-\@O~P ;~oUV =^.^at<j 8 Ju"=NK:_fPl?xlת3WmM̀!0g1c>/8$S져Swz[~Ȥ+2T&]ardx>ģILy˥364Ì:k.!q19,gy-N9:pܫb?e]{3t"(?6N]7x\HFw?}^D$I$F=p}b(UkF% 7]P0 *u7},f=.ǩW٬3Dĵip͡s5uvdb$g[v|SRJ% ޢmUi%tghИ-'mVβ-S*0+3L2"`tu/?Z!<oiW#T3JPz̢N>V֌Yv$Eyxm _g'!nokgxؙ#Pri`P&nuÂmߦ=덋uwqUHRrT/ѽ6)H',<; ߒ ឈ\( S6/$a{UoVXɓҰX^ErhxKm1-ũdL$\6[@uRA˄1kG>v- z9ڰؓIXpM]7HJ`傕)*t.Lv+ f~B;#$*ϬMf7 |ZC{IC- @DOKRCN̘WY#5m=:͖JnZ7b!nk"P*I$;`/dZXY& Y(A҃M#"It mdzCfOd-sX#9,N[YGf If_?rۿ j7חϖ5ҲpBOCAK6Itd& }Ex`pB77.*/d8xʌQdxy:/O:C|/a%jJ*[!&E7[G\m \lzƺ ٰTwpAӶ%I8q/a EdR6SpR~̥ {TkeD-#z22lfy&j9n%oM6HZ]N-.VzSf/a&ם(eX h(]"kQҙ|cQ*~ב&qlksi]AbQmb6`jhgݳipz 3&ʱi'5:Lplz:K&ǖuGW`+tn%VG[E5t`_Aaj^W:^~b/po5?CxnwR>gDxٜ XTc99D:m/vMM9[$Lm-HZRւׂ'W[ D5)eԎxŔp؀]@eV8NM "0öf(EO \]|M/c!CcJQC/O{ TFD{u[3PB$h5}|Exoj-z BTri+ lM[4G(3ẸR  B6a(#ËՆ$X,+I"ją {)V}xPX*@QJՖ|=`t- g]%/B$!H4>.>b| e\ÇޞVV fczߍ7`z/CY~-`/ym _aB2|>2CD% =)ya i츙Q3o\2¨S<@ wbD{m:n7$֐"EU h9ֈV$vfW$#|Bc7"x)xy M]NIQͨ <Vq0̻VJD=vv/"_JD1h]r0lOI*yAqg 2 }v|_>jXM SD`B3(>0 k,<9 ډ `C$<xhXqLtxְ>Od#M!QU@|۹yeXI ad[=>6@d= $q7lLg>/TPyɸLXq:k*d@QK9OĕY7KwhMdp+y#(Jyܘ{ٹ2W֜/!$cbbAX|~e=Ui|Y2(ȫ%lS9"ׁÎrvI0]E~jWN۸؋~rlN5ʘev~b"Dۃqhwъ}jGJ`0@;˘t!ƖwUL\ɴ b:=GhJQaw+1`!d%!;wm1HbkUSҺ$vrϚe/u!m &x06Нq@{XiwnT{pŎ0EYuɇrE ne=f:PξLi0t\{B6*4>KE^|r5P+l%.AʢXwp)a> ؇m0{ժSL Ln=wUI\e4@r:}$ <\٪"+iT (ɡ,}^rW;<%/Ο"BɣPżF::Hf?> ?? NCd~t"C뛓eHDG[L |zqs}e:aG *eheb \QprW&+BKdxס͔"|!v"__Q4|YHEZ,le&]AuSvFŮ6w0gH<##IRЅ 58[yѐ8B/M@M+16ĸT#p| =ÿ$]ȇwpmiԹRXoy =P;xӜ t6=8j?:T9yKKuHSQܨ5Ku" z=Ѡ^ [,f/4$oy%]R/'`ӊڕMuz?Q/l p\8C;JWMBC`6 "BP_㨹BGfts|y Y^tn07_r|~p=7>#CHOd?| Xa+fۼ`J̚v{ +O9I)z O y8['.Dyr aR|)L[p 9N]&rR~2!a }&ȣ>&eϭP2ϗCb,^?#\>)UJ1Gi4#uxF4ZNf4Ͻ7sZq"O -wgRc Ds#'s/[O8=* 쑭6pHQԆo'N`ewCwҡ[/ _]{/b̼[z:75U @7BtGL-J.w6$=(mr-,9`ΠR.rs}/eC_j>TvҒ? \5A'_eOL+pk}a]8 FAE .jInlk=9wNؚg*uRFf)[`R3Srp 6"]b- ;Q/>8F}A215Wă[zvvj5_S]Ҹ!N%Ll#ЕIHOYv-u8OѹUZ9Vt/3H6owb|4a+il5~ݙ,AxYucY[x,,u|eIvWwQ{lM^'])0H]SA>[/ι#O(N'%[d4rBL32u{dqXT 8 Xg0&A:f]\%=|^*~'egwRT/4)]2sW[?{7po:tGTRd-k:1ԪD'GY{[U:k>r`:MQIV=v *aw̜#RȱU&5|dvN0йL2?MA=Dۡ ϵTyG32h./oF窷@چQm|lFEyF(YfqQ48pHɣGζk5Κh%"=ɇri!dyP/^ÅXf \WsyxЮ3B(!@ ۶ji'R)DM)\NH!H ^S B]QbbJ\LVNSvMv*4Q R;[b^7n@Tr3ǥ:ԸJE:OvG\7^=v0ȠːDB{_jӀ20Kzٴ5t OxewtXA3<ޖG,fAlzw/6!qvJ`=ڀ X"˥?.zxV&q<d$ Lm&N,Y[$ڞV#MfԒmxڃ6xW_D"LB2Dӧ9ȍB<|G_7W szl.ŷ QO[eSE\CP+^Cir6ՃhO"+=Dqt-tvTSkU_߾mZ@ i~v\a)t(Kam%cfX! x,M+Pn  vsrrqo% EBȘ!f0d ąw,YP} k68I&bvH+.0n-5y%ޫlXT9<`1'$La +~HV5M =C!3@ VT͑V[a>F чjBVtWXµ. \W}z?6ŠAI!&^;:=b~,-Fevtt6Rqf -q1Fן1=ZS!\T'WJ}Bh>st-)d{"Z|6J ׹,mY#"u8pဘ!$;x glr0$ЯJ7n,䊇0*:Q' E(M:/!)@$Ħ%(K7f]ha'd!)զFH(!X7et(%Tl~oe/6xsZ SŔ)&Y§$]}"y,~3"vP3OpҘr( =SUy/nj)NVF2=תzM*d%`qMDXt&oϰC/MΐĹpc)u|z֠pū"I1 gi {gXG9* fF)F.vw\|ÅhkJp&!mN *)1+gԙ)3v=VDꍗBO~wpG lZlS(?h`z=U\r4ŕM3jG.Id?촲?D 5Be!}\a5QKPH 9'aCs@+lgdt%%?{1RC̠Bq'ym|qM%H;qfQG el{v=*o.BDy2STĩ$f""ru d.b"?ÿ՗>t]lʝͻ[bQig'!QR.ѩzt)U8[>1;k#CyHV5_ORZ̏ǭj eA.KYHUfؿ|_0RȀR+J!꥙N>5㷰„yɰ$O*|)qp5]u "<2$yڅRjæ64+o[@wğaNn>'zۯwIj?Wߩߌ:Ht/nfR7S?M Ty_߁ʎFӁn^Yh~IL. Z̕yzf+KMB2kn٨OgZd) QNV:.cWBڦ=c 5 QUZ(U BlR]3Gg>k{'iz/3'")! =NwiitOPuFguJ Jb&bY]٪x*;IUd CtI;G$8v4$S~sϤz'ӼN7ϐۭgҞ3 Tz3f~ZǬ_#w򧲫hy WS6W$L!ޝ'iʾ>EgFRz4x7btmӶ/FG1/QGQ'*@5z wH;]M@Q*[M΁vH6t*I :=erh1nB( Kcrqi4Fx^S~rks. nR ŦN"FD;Yڂ[;~q.5YŚ$J.~jFe"A*Gz'ggR7%TNC nZ>V!W\>􉕻ӟY(NSiv:OW;{]S.3rsF3 4I¢Ou$iORw;|IwOz#f68Ҝ9ZJ1guϞxOQ٩N Փw8$-TqZU,/,(`9L+dV|2mS_IP[ɫF[ 3Q#sYC}jSLql ^^Ɏ15%~ZkщVApQqT ]6Cx(<&+*t`J3?.ʎ3ljl/[XGڔxoazV^pKE &қ(vQDMGD ?(nqHxQl]x {1cr[j(> xaPn,2wG, 4% YzT拾+ccQJ֢́"TwĶ[㊫C[1ۊM<ט%!JڃbZQY= MYe0rXt{Q#"}i~kAJպVܿߗ!E1RE{q () GC!\%Kg?u3J  tבA$MbQP}\P@0,,;,yЖ֑uimxr ڤԢXxk`ڹng6g&AÄ^<3}/+͢(kS"zhuPu܊m E쥓J4tU 1OUL P Ӎ chC|%}`(V6U*hM@0n:kFED/>_\Hҳ'ݞBysKGkkA 2'-Q "DE"b77 U;lJ ABEemu$5ǚ@.*F+FPn>6/FpQuՏ0u䑺CN*a5[.0(=N@RaD0 H^MOSl6,?JkrSռNWۖ UP "ϿU82^Θz<ӵYP03&|\`x"B?ؔ—oz{ |H8ktД411&s44),kQN|o!cFq8zg([5ѭ4E3׮r5y(tm/Eܔ,ٰTܕq@](&ߌQ%Wr EPڪ1cs~ձy+9Džtpo=n+O̮LıQEEFGlZRM4㊿vM.xy]&;6X!|Ć5x SJKWX%0ʩKv\3xI[HaBhq>L20BOe%)L#VSqY&$9/|3H;^ *7=õDg!_rGǨn]%w]ax:ݮh+yOD^udc)[*pzvgp|{Db*L83P32eip/ȅn.K<aÎÕa!D?wSHv;9]D +W$9h$`]BR$xf"-\i¥Dn0{/tw#0Ryc#dJ<'`\Qv±:wjPQ- Ý5=) K'ݵ*೿1[C(%d{qqoAxʒ}xy|[q)Ï#cqݐz8>L,Fns?_M?.|¯SoCZCn]gY$v)&6=448'ڌSxue\=s .((O>$Qz=xł3Ox'[xP o>2Z5.U\W~fLI@%GZCqmU(R4:hx 'VFEc׉uYTv)YrMp ^Wd1B/g` .sKuE4;Оv9 ?fC"RtL8{8#0)vH"}m!bUeڃ׮,3-ͯ!g#7vmXqĭ[pV {r02Oq/5Mzٿ>]kSP8p ;޾D8m#zp{ GsHӥLeX UXy1r;a?ܽ#Y)u GͿxq&xߜ m͹ A^,X""09*oS7+y". Paȣ =H=;A2UJ0i}(oJC4%?`M~JzKPȭ6*APrG+4521jCbv.K`2ur#߇")T$OCa3۽1a KʞRPsgD=82))kSI&e=CqJ$z Y'/T>v\yXջ4I֧];JkB"$z<{]PۋWE'f .ML0'k8EHoܱ&ZOaGpS"\K5sX/\.2z QZ_ o/ϽniKݰn¢(Z_*Y)-z Y͚^0ZӎV7T/FŖdg,槦3ȅ؉=_bR=UdYrq~ |MYAa|zFK`αھvyS#9F6F:O9OXQgSt1hmXHv}QBg%.a7QFtΏJݘ+N+>N3Md( m>'kr'nG?TE1l#BD`\;I5W2uP>>y;C =ln,J݃J85,@Z">M0Uuױ'C*@~ҖGnfJ?avKE#Nr6&%&4G161 ]؃yZ`΅fJ!QD;TTTvޛݓ=jv2J}XӅ9HAnGMG[y)scW.B"єz,k}&F^Nnĉh}"\XدFCd[2"ݴ.\lm$&1ꔘ-;M$4r-%Rmf){P^{L>6ϷXO{uָ-1&@Vͩl IEqU)9Pb'Zvs@,I Cú*>aĞ<k"}!nl (leg$A3YNw-s3Ǥpl Q#CT:qsPdr#rJ6>Ijm,uP=stJTG dDcv6;WՍ#h]WQҠf>C.(q9/"lucc Wlj=nN8׾[~P-8K^vdTD0pKŭ+:(4=K9jpu%xY f` JT5%;-+п`fIlxpOg3Ą|h$Je$ebDuŕ@a4*ElPQ)S?o@CcLW{.R2Wl\4[ĭl}8 }릐`ʾͲW)RfْK00qђK|)ߏd7iحU, LjlI75gt{-y0PKqg7̱lĮ#g.S9̙ՌHJG&8m8^4][amZ6Pr [Ј, 9HY'OB?pڂH/2c`yywü>"w~xɻh\<f^|c釋"p ~hvC!W@3*&xok8n\F)%v>9CXc>ט`E!CՋe|տ@B@Ek䨲d?c[a@u:y45d(SԒA|X-Ss+Y\6050~ETr2tLNdmŌ<ÉnWmMb2D/$+&oY 2 ^}65jit!S3_a4\,䖛ONdIx[3`-w`y2R! 0/{4pO59,i?[ Eۘim D&DW[ĄYZe -gPm;]8mcR}!φ|!:IApޜv 1Kv9˞n>O kY[ͤsXf>Oxep*T%M=^m9uX*¤ ٞ' ==E?!dh7;ࡁtL篃#vB~0)*B"B26֠/# |,DadA 5cU!6,|ȟn.>z%~oo/O],e#[mR2mT1/Sg A[Uɻ*e V^_:~jG AC+(< %&T;Ww,ljAcGa1IBًnxPn~ 'Q˿^zN WưlrN&`OA@aSo޾d=}%cs0Et#+V29p)$)0v&>CT ,#u0=bJqã,Yo߾ΚZ'CN昪tW(}erp{1޿J+H{o%Fk* r`QD-`h>xۛI4(d-:X>C{O"7]PpT-NFnu3$f)ի_]3\Oauy6hι4Yx3O_Ain,zj*\Fǧ "dP0M '\n6Vƍ>^# bY08- 68l_~xֆNP[H;|:.7%9vO2:*ֹx$ tʺ@n[bbԵvM+^@H6x$6/r':n%.9AYR_kc !^ÖKٽY[Ae86} `Aet!20+PI6oA 5&Zt>=YIڋ]F~Edw\v0f ؃\^Am=zc(U9p05QLJyZpM/K;GrEXc"(lf]C(8J6)$CeG"P]Ef i,׮R {,nzeyZXVld-*ζ}\M&.05]oA5֜9}_zE13@Πp<|0|yh{g>hKiOph?kS5a'Iʣ5JKŕ[xBKH4g|*ˊWmdabskG8 mH xQ,4(%l:=FTt ijYVVWM@jUc5bR˔%ʓ450SD^MiBSnhG/ G Eoo"EH_A.o.\Z#ųJFB "F` PfU?ݓA C \͉T`ah$^ؾޡ9'sԺIJQKQt?/|ӜEȶ;'r+LB)DTIlQ S#u!z13 ,wۃUTp4fFG7?47oNx6>\_`ؿ|G~j8-@?]ez oqwՇp1u_0h@25=ÃPn>8_F? rs_>7WǏ7XJRވ=3 ^{ap#5QO=Žҋt0P?$a~]=`F[ek\d kYWF"G'G"~C0T?0BaPLw1aMPk4nE:;[[(O(O71*"Tj9(XgX`b|}J;ˡ ͮZ1oM4L~ԨFe"vQ3[= iU ] nN;neflT$G|v`)v`<="@^ ԙézĘu_!l 'v]LcU [.\)WC fAp:hag0H`HI` V!ˣls%JG3ʧC(H҃W`/yM3yǕs.Z֕#Hq=Fp╟/:.}EolL] |ݿ=ImC']c :EHnƼWm옼b41ڭo.hqk`Y)?X߽ Elg%[ !C  LqxFAxTc_oHEm}n z33j-pM+$-5_牆QȘ^G~"q01+ Ddi}"GG 8!mϷ62zkGN9UJ t, "'o"q6w"v{yXtzV:I|0wf^H (C1pr\naRy e0/ozwe"Uw+ɬ?xrԷlQN9B%u}(qy]S Ě &*_\8f_QV)=EK }rqpɦl8X1{&Cǻ]O|cT;RDQ_r҈ w~<S ԂLJP$NKĜ V 8IH5p(%1Qy]VKS|3=Ͱ׼!,wrHZ>dX-ЪKrK-7xsO׸]Gn/~mhg"jTYIn:@>f=,;Ѻ3ۂz # TPBaz|d"L (~EwBrqrX\3rH[ ƃ)ªҸs;p+DZmﺹ}|وbUy0t)ﮮzH%f <`l2Wgم#@*賖 eAl Pՙ08$ AbA{``\C u"t;?l 4"BqpO ZYy`=cRC绨J\˝O ᣸ʌsLԛbU3%P~?Q;X 8[xxdfE'Pvq虀h /`FhUJ L,o̢H>Bλp__zDqz)JTK)1!'P3,;SZgg$d 2PuN."w0Y"c;7f 07Oؚ* Me.Q3w#% 2?ZȁK %#Ռ{ kS.g2Al8porc*3XkZ֪,'#2k)oS\NPY9<JG}I#Q;BdBHzHu^P@>OH9F j,z3!(rN!ޓ o:(d*e,4C9Rڇ;&vTF(F-HwMOkhgТNKfVOz5OXn^Q0NrA]ٌ=\6]8*# ̞i(cC$Rct8%Gp*QaTSO. OT ws1zYJBABW.77Ëkܱ |}8g،E" 黏!*?B$ͦE%Fb* "jPP@AR0>"CB >[s& %-ZdJ~LY&u|)rvB`PWIfR6Pdߊ6n:_G2]3sxN Ad]I,YoL̂5RK*؝4HR"(HdRx\Ӛ.[𱵄0K+xC z,1E-qcӗ=ѴH- 1 VW;g ?+ۃzGj&v6ӘnAi׬\F+cXϨܻ{# HL==JPFaskKaE1hhxL0y<.̖*a?~\yyn-W}䡎:bZ,#O(nr˃s#fA??Dw0¶Tsȴ6Hs[$D0*vHնKāBc*(gLQ0nIyBXZ"ۗ5yV7Wwc@cԢ m#+,'֫+v88$xfn!.ww}¡h}(ht[\ *5<͖n*>͞,-7r Y eΨj\)m jziGhMfDگM>z4u[) J :-Dh%na1-ݲn7Cc9VAK1w V9lܾ)a)yFFޖ冾y(dcPVQ>4(,jYhl˴ :UܝZ(|Ur*}mq: aPĵ&66}21M~n 9ʘb [HzFu`%@l~)Ɇhkn:>g0̄Dt@] X BZR6~[!e7n5kBSHP@?E\yVb+J`.8zƥw(2>,o%pT1[I]bqz<\]%7W>QYw@8}OoZ a|ja=yR7L1ÃF-7;z]$ɳ\ǺEefvbQ#=) I""k%\zOO~+RKrۤpIuk>< k)ݠ(9e >&u`hI/19,BkVӰ'tђPƝWѲbpJqC옡d$ ,AriOEHaNk]Xtag%*ˡ#lFƋaY ru{|7kM6yT6 OR,aA+G{?]";=ZtA9 gp.'Q =8p,U\!CDkjcJgm.PW02~%-1e_ ^ւy*_ȿN>p*yr Ug^0%thFhlkth@$0 YS<#ڱ(H`Ln0Wa{ nܕ<#X'/ gDm\wĎF' kc >du/\_ KN֩鬚AOL ^xAėaT$|!9"n ,ڄ@o?"1E:콭MJ曆m<dDbcz v D܁CSJ$_7y0h|Q5:yCeUq9-|c)UZHWuecRBw `UBA\]^mj9ASV FXkXc>. yЗ{jĨd'cT6yA"t%j_P3l5, JUCz QaIP:Aa@F&Mopd/u*d.OKO.|+cCM xʼcuME*jv4;,/Uj6"N)N*O%WRJkP!b.qCZj=FҠiĝBnjq#y1Ѳ!PmډqD B?HbVԠ9CKJh)`٦Rʺաk *0[v "zx8Y;S8z?~D>tuB4|{fUl7ʼ`L;g5=7QK1 fzEnhdh1dF6@Œ(/1ԈG$\$=Xh<(:M ^ƫ8xXE {otftNjSHU+xL=e993AF`]xrl2,Uc_{G㡻 SKy|$_E:*#sS[߂@и?!2ʤbuxԊe7JɢynWPt( _5#^ב.t0KAԕ3%E)DO,cƔ{tLiuϴDI)a!@WF?-V 쀬1l)VrYb!a,1+5A,DUi^Sk׬")gXگX\&4 16 {.U CXyн Z)Τ EW9D|{/Xyf;dŰ PȂ#$yZm'c}S6!'x>C?7E4-DTgD$ل#2r jjNg4\Տ$ B&juL¹'=m]OJ;1nSJGj>`|YN3/R-N>r5qcݬaQ1E̪!o\E;'ډ1"p[s λt;TJW y,/)wvGGbxjӮحDmw }*+kWzbgx'ʖ:$w:и QNZ=InwTiCN!6*<2(2&ECc㈨&DL|TI\RV{J?AB?uQz)+F|R+n6Or%ʌ27J<ߥ>鐾w+%)T:BAU㻃Jyn\hD'#:9E+)F^5yeUͨ6ԪQV:=gVV[<Dգ9Sפ4'y= |OW?9|\٥8Hk0Lu$B]\s)bT:T$DWAQ,Qll=d!D,ވX0p@>TP'kx y[BsW?>LUX~RNY%M],:^] a_{PP~J۫r؇W%8TER|3Ƞ)n{&yFpD`=2,2 a X|Q{8ϜGpFgaS ݋&Ieqµ)9,1w˅x6b7Y{5TYo6#<3k)ʵk q& #j| /6Ǜ E77([L=yhe"N0EINM zR#c=_WXzq}wmr(&\C+܇x„V+x]g6.hSq!=-%{ꤋ$6Y%vYREE.2o@ Y0TJ@XL3wtjrF =#~SƥMS0H]$3!S^02Hdr8S1TT,'M&Tai# )ŘaCk#JTYx\P怕F04͔Fj UOLjXFۆV"2q5w5 ߌf|ց2#M,-?깆BTHFDW;Q1ŸD]5s[gbe[Gx+I9ohtaR ъHW5:k8|;E#F80ߴf x^R 0%7+sELFMmaP~ڪ]kBvoL%fV4hj^6/Z>lg523ozH.⨁07c!]n+Z Sm#zWZaدn n9G:(<~ BI%.<[ɸBo$[3~/WT Co' =ý\SE=؆2S6*R =ʐ@hy .051N/)T%)ifHՌΟ9`UÄjΖ?j6~ilg֤;y_ɜ*{]|y)Լ}# mu45>v RFs :Nal ո6߲[,RiOH6<۹;drI"n_'uα ;DT j*6#"ٓu `R-<*"/ڍ ɽqL2ﱟ,4bDTJi W$n>c4_gVHhЌ`ѳ޾,{T5/xt.S89UV`ѵu5)fI[}YkN|UP c", Th2 D![<ؽ"ɲ Uuχ tS2ɦaɋ%|3'/m8'Ĭd~_L󴁛m:l[N2XXԳ=w%h}(mUQDhrKqj6t6^-:_ፄAFCS|Qs(]#7܏wn& Pf #E]BpfVW|΂#OSEwjWW]7"s$%@#䭗X銡H~Q(02C>M)+Ec(qVN](\ t1/o.5ciX`oF^`ֻ{OOl)0vɺP| agid֑E_GD "JrWٺq^ﰤ `l+8l+{oPpmu|M` YRQꭗO@MlhOƒ42ůsenHx{\Q GxL:^k.χ>C@hsݡw={~|wkW&ڣD#2YJ[\ꈛ '-xAmǶt~oBgՓFt)\iR~dT}cA!rwh& @2v:SbCJX6(lCIv(ڑu9>Q+Q0;M>O#OS\|Q@Z"Zo|DL`pRp{a'ܶr,fF:k;SB(wv_!T1FS?wtPg֕nÉNIT\֋UOܑT ]ŧ"ԋ iVFc–kh-egʰ3)yU2r]j@n3ͳ$gT%J;/!^9$E0lQZ؛2^-48X,zsZB;wj R/U |_9 ѼZU-ahp|PM(f>FOytg!\v^u*!v-w{p+5lnh4VO$? #ߵp?6*oYaj쁥,`YBR"X ?Fil{v$|SA邧b nRK^JPcx. ZnKKo=Q{rOd1*3Fb| ao*&!~u<ӗ:VWlXO̡AcWX} GWc 6e"k(\Xua݊*V&ěM AY7ux4e2qIW dOW[ b~eoURdj ~_4sz^.s;Ļ;TpUTg0JzL]1)T:9"}K|1>ZuP4K,HvoԌs(N:o;._K E]\).N $n S4-^;z?ч1\[Y%[('9 .QeJH ]O4_!",*=_D/ZȆ$&PߧXz &Št|}qXcǍ'MXxƻكݴa`hut*|)*.Ͱʬ<_)F׍L)a/ܩIَbnZ&[]q g!n{>1pU'8ʽFI3Ոt*bW,/BeDO(gm[THhۍ͋TEjIҮ4*HR%%''rY{NR 㠆.g]K.`uqTy~< [}\gqnbdtRq@لbtI;c5Xs]lmV($=]|T9[8QbN4 CBɳ~1m"9ʳ%mE¬ k9X/ _FkW}"d|5&dRw5]q [\ LŊ$*fN 6fH Z1xclbJ0hTJĦZΌyD,#6?5ooFo_\5|aW}||d}oy]|77+jt8]9CnF=z =9}[s`Г %P>'t"hlmr/ŤИg2 qS5"6Kh`sJ@-:2@QO59P*+r@v&ER]%'8ƈeVsA)Z86M)m/kN\ 2D~#Byyь19E7CtS.>ݤ@k,TL![7m#ǧ$gq*Qo}JCP 4ՓhIDO+mK{zx,qmIgb2[vqq2_@- y$?ü b}}Qw Ғ77j/n l%$w6mhN(Ӷپz~Ҵ!C'ocy&@e1MB6|_Yv|>+Ʋh+LſW[.9Peyda!74Zu`=|r9+ G0QZi2nx@g n =^{ SE(H*loi:T,GCDzmq1Քq@8iыN0|?/Qs}偖]`3jx/|I/XR;`i[n<.҄txp:߅j%5r '%8=ޛU"iࠩ#pg 9CcaY߶Tf@Y[& >Uz1ySz1E%JEW&d4㮃ݡ10qҚgx7 N9WHRP&S;13]PQ>k&1MJ)ku6|*AJ(K1E1I#id A &B6D1ZQibSEVO8Op4@,"}Dᦑh8!{AIqf)|S&4+M)WrF`6I14V?T.*3 zS?ꔰus;]4JXN!ƶgy ~1F .#8}L(6V;ш!j˜'bΏb ~1s8ucL+x@49q3۰o1-qne¡S6b%PdzZ@|S@_Q\qWM8)\Iᢷ˱G:mC S ZB="guw\'N O \rVNj<%u֙$(yo?<32AL45D(a݄c]2>딃߸MfrICo% UT2cqmpY漿+6zAɂBњ}(}=.z=γj4AK. SH`///Fì4E5{ss2b3$-Ά`Y(j8Kp7> m9]a$nCY(U2d9,5>~u!vV4"fS0b6SCX $k2;ngS[vIKdEr$<_g|au#OqxLfM>Xt` y#. %ܙ&iՎNjt;7+eoy뚇-?G { k ]2(m@5**Z|]-Zث, 7TQMŊj(>&+q*:S\z{2dhEy;کP 쎗u7CW ? 4D|)C*!VM,!Pv5eCw# }5їv{{qoqx6& 4>a1(Mx=Yo AJ3l %g!L@Jً߬<"ըg嫴Pǜ>%`C N(|%KiUo2qK:H*8Aյ`)eh40 wQ 6ՃGI.?:)7bAYrPg JZ4]é]YۭKܗy"rI>TRq_ mf1YSL< "9U7։9NQȋ᱾.`80ݤ%!5VC Ljb_nv4C5~L] >!ܠ {fY@0No޲ώ>{[؃$M6 .h4?Fl`5x1KZi;흺Q<`^#a{Q>U52_ಅ#% P|>  q[Mπ♌i tq`fP[E xM9%Eb:UǮ,f~j*:u~(<|%CIΐXUBd !.'8C1a!a2 %8*.q11WTy4uBYs./xN<)*Y@mvI3PgO&y =7.xc7R/*Pth͎ JAz=߶sTL"/TWpgTDpsHl'/WY=;d7׃]]F3z,3!fU+AL -NBڊd<Ps OZX[θBEq(&()kaf2G*$ӖݩJ3gٗOx ѬSOf W}'avVe4-8bX"|Xp 䔲H¢Js%o元WtGe'_Bv}ik "?)h&Ţby37fgaQQiC[lsN Ɏ C37Ʌ,'Q*}zsQy3?pk3(wpt}sqp)j4܆ k(h :"axJa+æ` bZW =Rt*z ʷQ_0߂V)lyPl8=@!?/8'EqOE4 wP6lcE809N}\S%8''p1+kla ͛AMum*`z4񳰠Z1/)~  O$ `)ہ~C.QS%.KLw4۲G@3n߅P:@u£F:'P$1S95[ *˜~um@኶/Ng12һ8Ն*V ԑ(^;S^b˘EKTXROl"[' öo[qʽF5 S]|6Sn;/m^f5YuUg:ZHt7m4JSPڍT{e<1AgNFSBөGV*&#kN XЊYˡXRѦoOX[c)x+g=?Qfc%fVϨ# %NͰk~񄛂`0v4S]PL[ Y-f#v&{X +vmzO9'Jan4sTEI"\vaS8%u+IM8[ud+l3Ol"~cM04rqcYE891& #>!FbzRl-~m%n)P)pioŠ E iՍqOĚO`:[𣒶%stJӡ4o;YԒ'őq6؀$? =ē]>5˸Aj/Js~ڒ76ʉjn^ƍ~ur~@ ؓDBY򴶥uO}<QBrtH|q^Okp:6\AULr+maOv=1yƄ`Oٛ,79@n"Қ$. MiL@X՗U=ƅm84HciWV yH"(\6K\ rE>nP_7ZO^M )̟iU">~5u˹ji NЀoǍ$끔N-ѯ4`OX658eQ z፻RۼM(bZ=UJN}g(;@1kBL}ʍn|uҦ1;=GSَ"/Af0'gD |iRʚ"] ]l 4B ]R͔ïgDƯ|*,M^Vjjtn"&j/tb.İI?,>\7~s??\~i>~~ {G/D{iy<ӝ@ }1b҄>}[ɅV'?gڝƎ, f's>'sҘt/ߺJKY+1RUIkT*VrFK~d[00!5 I&NsC1;ĢT&x#)1UlEWxeeۑ&He(O1\O-qƯ ůѵ ]wn,>cA;׻aL,(fWXaP?NF;|`r՞Ņ|68A 8ͦ`2 k9s d?,n8$E}w89֟nwALt ' xԱ|wN; _'/<9Qk1$^Ott3ko{>T\%$>N,F@5/\ymn ܗ@j*YYB JUH yq۫>OxJKBݢZ &rhqz#cjQ9K-O6?йuI\2TJeBN$ P“}a2L B ®AWO ,Go8(QEjŇoy<lA''n(]|( =lнc ~$k+*Ye]nOVد-=.G5ak@B.RN.[6ĢǞN&g<5s,"S -=6flOf#r.*W 2Uuj+fgfpOceX/QE "dkw@xK\VeJx #S@GmcL2VǸuڡШp4t\φ3 N߾8(w8QUH8z[l7G|{N(_mKuTc O&KLBFdO0@RM:Ȑ}.HVM(O ww;AhT(|ȡTk?~ UbUCK5y|W7o2^)&ۏMyѩvȂW6ҥޜk>vS)+Mb]?Jvӭ2D ;%3>s _#0b4y\aKw/8f J_ e6Lk+48u`YbD6v#g*[o,v5 Eb v4f~Z|,Ԡtɟ硅,TKJ_/Њkp1sb<ѡJ;b[zfZCh,(j<{]Yl3د.`OE%$#CH6%:a]dhe֫.ooG~€^ht'&}MBld\S={ܜO~Z-\|3$m(63$V+ {~(S>9p#xt=*=t?|so|#4f6@#1ܣq9̚o /. X-fa_[Nx3v gܚ 2FpS ٫,ou3" i_rKVjmQy KEO?zSݼ F'L p6 'uiq!GnV-dqq*{ /]qR CVPi%2̓/%:|9FU=M=N K-l!a٪xzhYa#Bv߆Thc;z zr{DJ}=rojp%JPؖs˳52|6q.꽐} M*"Ƿn6tTQkY\$>AKO/W‰r}0T%, ==mz]eVGRG(;.59*MW $צ+\`ec"(5]Dv> O@zKmO`ݝLh+Q ݼ *$m[QE'D׋`fb_{0mr*2+OY4pvFUKʺB<[TμvOXIh/r o%9 `.dC7/FT)t㞑<!:҇ fGz8ϵ$;T=xx[,0#F#_LMdW;*bbo+(^4N,_.'k].1je*I7U{[/rD zoS6aRp;b<ň-u9\z vbTrzK!h|m5zDCܘMII вʿ%ٽxdN4f>~Z('"`lǴhΆs&*WL7r5A@W{jf J X -f)L t蓻UD:<"O:,J|"x4F+c0)2"4,QKj gqiK涰/4+ɐIX%n YBbS.c1dj^o &C1 ghGT0Z9/S9^Hk Es*m{bEm#9g$+* m2~|pq=~i h-'eP# cBj ͊WzOaK ;7޽Eݺت=1./*I~Vxa=VJxhݼ"ל!@cL(|suh_1^("I5(VHyDE1ķW8 rwC7+k[ Hrv բGnT6p5#LzC9x,g+4Ρ˓qA;.Q'<ʌ8ÎLaţܖ 3Cdrj(g-ed(نX,DKGzXH# =]Rw^w\aIXRID:+i`iQi;(;_ ;K..t@CIsVfII\>ԲOў? ԏ_l㋸5ÐxgkV-E9 ֪#ơ*K:}R? t~ 0w˒ck`}ecV@*և`Zc ]Q"UI.{zYJcxb}1~-B$oDz4eO{H׸aHo6eVW4-߯&jd̶vJ);WKZΫ9GvtKVę#itߩ(*+QP0Ƿp_v` ySýZ/_sb!Mʡ&*m/oiAo#νyt3㎱u`!tye͍ k7\.PCC75l=b (DS$ $(ʹM]V3ϽL-_΁~*c/ZiI ߐ-/>;9~|̊\onHvU52=Mo߳&$XrSoAUI^MY{ Mis@v!@>uǦy#;Z憉 xqdcdwΐ} f (?=e`LM| Dǹ+q{Mmѡx1wP)RGaMz_j c):"aUjE#&ىs\G o{Ao{0>.lYyBAn+V2@CO:s-^h72 %wZ. ]8H"{pY>ch"~m($`qCUءtǹK2W|wqznb쬗<N`|Kc/wny5f.ymĒ K,U8Zw A<gFȏY؍W?DEOvt!q-Ej:7uD8Vΐݏ,RȦ;eTuHRdrO衲j]^̙lչt+K$6#wN(w&E%~&M y_O< z~$Շ%I_~%B1z&!v[ Dg~5謭(O+ރ[KDGs~gz>U4%ޢg)qצ5ؽiٔ4Q4ꦄ{Luo~/Vs%m3USKLwʙR̙ra|6Ev]3gҝ_qgcQC3Kmmn#drvfwSv|~][ȼofitJܹ&Gn}i`W_)u^;%t_ᄈu{O_چ禛ݏ_97];VOȧ)s2`LI.]g{v~8ϔ94g~zNJ eNy[Hֹ?Cu _> #f8. "3#dv=vI5{aNගqi2C.2jmf NMK!s.63(b.t%G;Led/nX򍏾UZSpP(}$3!>\\xS.U%I cFhh~(TAA a%-銆F6N+k|욏 {Mݝw (S,n??]ɛоaup|`?c/uQۗ/zpPAu}/| RxOnpBrZxA5琡7oVwm]wIhCV*CUl.,x?#u71+CpxY'hv,c 6VZa0WbnA{;&*Ksluq].Era'y"fѨ%SB) `A@j=e+qa!_8Ή|14Ȣ<Wiu6S HקO{ @7R(Kdܸgx&y{'&fֱpeYz3-)9otͷ6x/pɅsi"G~0s{\f7% n&ݿ{bZF^K`GHu[aWE޸ zXoK`~tK@ru 3s ?T]13ֶ sñ nm}6Zbs 4yMVSo@DQw0 |f#\y2Il\o ~rVn h_Of{wPgɜRBHn }I7B#MPGQ^uODNP CO8.3@[1)^ZX7;6K\|N{ؘ) 庍y%R !ZD,šcC..n.px2;xhdލbQ2'''9d(G((l^#MF]:$6~=S.\^$`p4 2AgZ vnS};uO׆)l<]flQFZT-jd#43OaOOscش|k1N0ɑr'E:>Nv 9|G|̩{L ]2u^,MWrA͒Zkƺٹ/eMpl`;L~M~8ߨX{u|he. 7*tH1;\NH< \]xمa͋(cd},;99hαGbsvQq A^'^X, 3DXTVec e4Hǭ3Y7&iTQOykvh<%6# I_DtMr(s^<٫it.>QQ}S\_t*/\pP=yK֙κP -ȇ.ǽ\z>Ȯ#Yw^XLe9*8G{q>>⧋n׍fi?2%K^)M.9c3M;$G{yh@{JrOi.`Mi/]^ocnYʋ_ߨ&|Awm26#N!?Yntr|ȗ|clO +qPPu8:tunSvO!O囹87K9)y_\:FtD8' A$$ *͕7#Y*v6&d{٘ncc\kR緬`3՘$GN=);v ;7ig|v+1m@spܶ92 0̘".32=ef;$wqx@uLʜ5%;7$?W/R`,H$^980)Iv73$$ۿ,Yː%b{ z1cEv&K`[F;3DK8¥#%{K6,{v2کeEI*jwo͒ݵ!Lνyڋ*x +n+&h%l:g9ܦ*;)tm)B!E%L{n v'r5@d\9g]Bu BoD`s&$F)nuy7.}B7}RBw'BE7}"lrӧ+"1/k{9ud!l@)E=LvRt@A85![C᭡tۯe33?3|S24t pݧAב,9|U"Rt\GQ%44;꟦ܵ@wbݵ"]n|H ZܵJSt*;Tq'~.תܵJ]9$7;7@/QX/ٙxo(s+\;_g}LZ݈gΠ"I;ⱒ*([e*뚪VY tvVwQt>Y cnTr)R'ch[Zb9vuDwb%zzҢ+J}EFӏCS/˶`޽0/jeJۖSS PI܀J㽂Dp\^>9c44 hM<5'7! 6S: ^K7<|$EEO+CaHx N38S0;)?rlxnA8΍'~y9Z7`daJ>N~U"YӉf*XvxA :jRd"\7hkqdO^i}id`|4Ft1JaB1 :r#{9N&! ndpLEԯHѨUmFNgq.%;/8㦏`}#Jϖ Ba8̫(3}~2.A{Tf$5c*q[#oRA^҃c?hH&ʢu2sSZR$CRo3Gwk4*1κ(ۀQ+/D$VrOXq.kj!1eHR/Ae#+76l!#^p9-k&i Ǎ":.&$jNZ Rur#O^ CPWˊT [ Hd5"T39 /YLjwL7pLՐTk f9 !O~V,sdDQ\dUvۄ"UT§`X]B{?:AvD-g(o>q+)QḰ7$PZ^nA'#v4RO%0!q>{:<1 c-ib7ubTYx 긾cf{0)QǨO >3>@G3?X]4?ڬ"OYd'Oy35ӑ~y.ă2!ba4twEdefNzD`GvN7ylW8Ay|qNr9*rߜ'͍B̙h.3j]!=je \!0p6#7y:m]2*[z &Ei{ 0d|Mj I>1׋z:A(<% ,fE;3sh#ҳ6:5y3ì"AC|Fh2E<̥#̭qu ctdSu,N1>3:HAԝM4~v:1]2jEZm9/h)Eb]|,D"$QHDD" +DHc$G~L9l:T@ pӦaBZީ3`^ZP[# t)< &׃̤0/ᓐ= HeFd$髪LXmW}ryy,ddsN(df2v/~ >s`hH=q꩙p[irY5{i&UqDԈqs^(jei`d ZM#Og$acX/UI[vB8Oo؂C[ܗMԁ*^b46pF0PyV4L׵x<Lp!xd 1Rbn$ ҏb-7*`,a!!ae۝2 \gm^oy;O!')Ӷ~-;~k`;a|}?k|TwidT~9S=zOkO#s>U|+z*5YvW/PV^4|7CQeEk&C[NmI<ҏ556ޖXom}8v-PɶMx+Wxk\z^01-OM& P_"M KP"떵Xs7ڋS;0lД/̃ibNJ23 S(WsLR-#JP2qFjq.7u_6'Nx-) cqH%-_ᢧA`CZdrSZsE߄"8qL5Pņ[HDmZ{]Py^1źwAdBhSގ4!?~tP=t -A ޶)_-Db0GfwuMdȝ彷yDP,_Q/ݦ2Im}$⊹D??A?(3Ȥ>ݞztP G7gxmԿh09P/dbE%0'&4 &DE"C^"nHĝ iG 0\'}@! aA˸0C/FD@IĊ՚zڶ7K( c7j%̫3}  .tZAtiP<ۿ+4 ;u~4R]>ފ(ϬɺE8sgȷ洽'6ޱ͔;2AS.DBa.DžXi l۷ \NJ@6j \>嗌iDנּ, MJVœ?'t XKc/8BZ>N{GYk,e!3x=mvR6:rA 'bJ 5 "@AHgaUi %='ZM1a9 ]JzREJ*E KrR!FTHծ,~JRK!?S>Nޠ {·|sy:E@U(IhknqlH#WX "[55LN-:TKzum2y[?z'3'oFI9-e"6uCM%+Ѫ ^=CXxf.'F[&.p_Meo]Po [D'BP<2 ǦJYH&[Ezh^#~ ?d(U (p~{OZ~B$fV]!4_?qTB:y- .ww2@Љ X "H+MEl07XeFi+GX! pB)G(5 ⒏BU uS/h~yfD,#ILF '&9E(.mz}z߅xwpCu3A-obL@-!`ǖǑ0yJ8٠)z !d@5Vy((W^0*rC7X 3DחŲӮt*af_p;p+uS:;ȋ Ziv.r[ʧ˺{ P)PYҲ< Bl[ˇ0W2 Wj`ine'XmK\rD,A U7Srt޶x#0&ɶ둚<КvQ =)TjIGc6q C%eGp5ow>?Za/9# v Iz؊/:K[s l9$EЊ~J@t"ek|r"[iSpf(BɇHk[l>=R[^H^&JmvY\\hµW|a7+xvh0L:fD'sA9t3ڵR 9%kiωN T(H Y8KZLvdȅ$}4jٽ^G2 jqX  z1`޼[iF( tt,He[rDܫQ>Ne(iJ<۪%+"o00mNe0`@EdU?5æZ:(- Sj|;T8J_JٯөHK$YLn4O&Xrc9MMWf7=BҒRB=|Ȉ{O:ikG*ΩwIЖrwq)P]hB!rrьur񦒊[O/k 0|yK^ <&$/ê&YOT=,5%+dFqbpob|\1BxI0Q\ *[H7rT:ݢ2H.*]@#a3TՎOJ ;W+us\} 8GD d$ ·E&H 9.b$idp-^8;ߘ T'=^KA~g;> AlvCzL{0{]7-qSA֑,7a/Itta`ZzC]3U$ 3 `$;Yp+;lh VB0h( 1Cu-2 ~C? _|bٶ"E"HPdJo+?IPpھS4b/bH8/~qR <t@_j"!0{1z/~Of>z3ȏW65ٝ9Ę)dBݏ2LAQÏ2LA7h}y=.Zv f#uE?Kܘ1L<&P q z7 41CnV]` "P1T^ # P &5GɆL{܈,a賰~i=CtHᕄq@̣y:P&?ȚU)H'j ``Ix ɯ@BGQMVnQz aD`s33E>?TAi,)_\keChшW<<ĠuzNnn|ݿ.e˶5}K ;LÖũxF5I.ԾȔ|;)# rM40_Sp Z,K0hw:Gta鐴2WK (FßEBUӌ}QX߸>+tn> V-Zz)[IӾA9qMwZUK6WpE ! ZeMw<,%35.Ub&o'TKGC?q.Q4<1TaT./ҍj6:햨`Yni7B %N>k3G17Iw<~kxt4Ne/I@W.FH'aߨF OR[Dַז'iUKF@XO\a|UKY`O%2V 6 rcK^D I(ɚ]!)2B@bZ4DÈ$"txbk%~5gTl* 5EOzE]8ⲚQ=J Gb]߽ZOVga9B܁R_2oHCwRC U!B.Dl7@A0_AcrAa_lMYUr\*N .W5wNcH ?3Rmo4;`#b,B{g`%$C L UZn!S3q*ibə*Np<̿',|.Z>C+i8_bûwOJF9D@rN>Xl~= 3}\wa0ʀA 8k8  f2sc'B9 `#a2ۀ)~<ƣ5$K`2\Gh% `ǞRAc)87jN$zB"a rJqb4G`'* ^ ʰW8,#~;#љG2ԨmV&sA*HfX/mdBoY`BqQjJ@k+&g[W2|xƒO ^Gq&AjZQI' ֔1VQ^*VNsPV`Щf"~_+)\Wc Nx"e>/e5$nk=o\S U\nt|EJ+2gXwpKj(BmJZXƒ, ,}=HfFrG+߼ AU=tG2jϗhx @["xA5ar\J%MAQ죍k|G!9H!M,T -F{ C%3I !rMw0  ֮7>F:P*pE+3_iwmxa/Z ҃1)ݱgЕ{˵y7wﱅ,kU석14^[*|﩮W˕>" Γ<zx‰rZu!2.  Sh4o:ᆰ}D _=a*l f2"_XׯGXUk^#O[6:1q{A6'{]cn{K-\ -6_lB j$m( FrGes7u22 [)89zhOwuv]IY=%E;\baU:y#: z)q99mP| n j V"[W1EƗi;:Q `Fx r,q#4ė)+iti@|]_Z&$Ӱ/rz-5o ғDDFP=sn6h>nxY@ExKMsϓzcl5ǬՁ ^୶=陼xaCr= "a:ož^ui* dr byRU^sySC`6SK/U[A5/yz^=eD5iJ &Jz VRX'.97ecFVIӪUcPp2jtz=yyY,GrZ>vUZ~ٌ{bV/&ʦuҪӗCo)1Mo 73y;jAVP5ClQy R|l\h|3 蘶?Fɯ ?ˡgI vg&z7x|0`:5bZ ȦuYoςV@nm\O]]|3ߥg5 Ȇww(MAx+T7/'ӻ!B4Fܩ" 坬\Tу. 5p (i=LZv?|5_5%yJxV}G|]Cٰ7gmp!=%u1m})s D9K2o:NZL.R\UWN 1 c@z rhF'Uٻ:W1-$2,'@|'|<7ds3 LCJTNj|}gpƍb{kzUR%i-\|Mx?: 3T/7Ւ/YU j61%wYE$GؕKU(&1N^@RɇWm1Ω6 |ml`P>v m6//*5M++!XžM㊕.]רGů [:vd=İ,nrL"gZqEF-*j.a%Ɋ֭UiB[vz*XP;n1pot^Oc]>Ns iouR&>{/0ӒXvӘ1Wrva;QCʐ?;n۲|N4/`@mK:M؜Bva|Ww.YFU?:8(y =/9#}PgQ}a@r@>nP| cIIX`eW~M3 Y|P\'`aى:8v|9b7fmp:h[C<4_ N1c>>'g4B^mԆ볃_V=NTP_a'Ÿ1/Oĵ7wPZ/vkޕ1hv@*B\ynQæ\\M^„ʬJAB=Gwf2 zw롨"_xX'u[s 8K'ih"gJclEQ7frGcs"GU(>əFK'-iA)Cf}굨Gzi9 ^,Ow ݴs FO ZXk[{)2#m [7P0xSD8,&٫TzwZj^#G|jıUĥWZ)_=` ܲaj|S0[3F )QLJa}!+:VTcͺ-jtpWb/Y<xQW@?]nP5o+G\"ldU!c f !, u9>[*Y(_y +HFy/P Ku{I TH8)z1TBV , ޸EkG-FPǤ=3DIy((h:د zu"-i=#\{&oLd2&⭷;n ZBoo/?< n0t}7(0Ίٸ`R:k> gf=J8{L ?7 #<>>1dž֪j.F_iOr mWhc v zyx+bj1cs` L !P.2Sbk '9}@\ &znAbo+`nJk뛄UxOrIVRUds|bڥUVACۑ26-Lc H%H9Fg+ UFR]|/ ^;Mm4@ET{A> .t[AVeH"G }P# - 6u_emAzrZѶCD .V!-瞬&QSpbhc‘!PԌ9Fvjrw vgYѓtm8T6LKu~ݖk&.`*oIZ)]8Je >' Y w'N^(ϕ3i03JbOaaыQԛwNӑ5 v2OFaI? f(`_@ɯ?#ͽ[n, E&9JN M&19u[r4r,_o =D2 m&$_}TOև ϔ F&;XtED=+&"»1[2fI4M,]X=|={yi;r-?5q 5Q#S#x;NBTF5q?t~%x:Q}OKbJ(2B2%4/> ToY% q۰[V3Dfqu!.AjN0T_(<,֣'eyfC(u4feD;r>$x"?N*V=rBi-3;/s岼C |W[hC's5y"\nx~kWweޟ+Z0M:z ,Vq/F|RyqlƂhu5nL1bn=s0>G_~~޿b~=FfAU ٺYRxɀ". .rpٿc|?&>><8 ^u#n8G R[!:`J@7N~sG ooߋ?]!n[uě[2O}o0@\, (Z8P?'$(;+Ł@,nt B- Zd솝yId8v|xRx4*ԥf6АՏLmDn4ӇkLƷwÑAG&34~DZ+&}fp?Ogڍэ'z Aq=Z [4(ً~C>|7Ub0n|7< '>Q6_t#des7]oqjZj:~lQzS8lh05ATC1C7ynި]GfAppZ< T!ו8, ЕgCW]93uQ|, 9UrseUu,۹ UG 8Uj~@4G\exr'ʅ+DB.r\*$W9Vr󒫔\*`'W9~ru8fq)+0*;as|*b,ZUY2*[br.̲-WR_bIQl&$Hfb>s0,JX)pW}|F9%7CeQ @wf*_A7~hD+l!nH3Ίjg41^75#}6uן`gxZeЄQaHPdya zP<&#*,K FM㈠7Hk9}-_,_0dvaO{#l}R>]*逋+w\hI;2aCZ\Obv[8A'#4g<"m?NFПFtpzrη!ލr+Gqg`_wIJ i&#jBrL?j`4 ˒Ò$%TO^/U9EI<W){ pEVh n5.Qu5v]cktW.}t]%K+\P\=30IOKuܿQl/Hp˅)3 u]=,yT[q6Uk9?~:GKJ]sX{|#NԲ=g?%#ٹdN5 ) |u͕&/Aa{#LPw0(E_NȻtBɩR~)wR^DtK@9/pߺYʏ΅<^3>0$V@kFwsGB{fIn9.Ef]Igu6\JP%/!Duzg/ĆXy99;rҌuA5 e#K3l&k$k&1ɹL1I ĂIxwJG)c}2Qq^T<̓2 òs)_8~GO}gm=Nm#]:XӞ!>iCgB)/4t^E„6i=:UL;e戯OGudaEʽ#<㎈:xvDe]:Z̤ZtYE^tstI.*w#N@۞!S<66&Ÿ);fIO;KEQug;ϐuؘc,#.>W0a]eK:˪RQŨH_"C}]KuٴRK՛fqVQ$U@B2]'IHP$]a ϐtb  CdVWM"%i .BU$0DQ'2ԞWu X.u̡_#;^@eZ<.0U=CѲ] !?Y~,.p]ؿÌ jňCFYH@>_< KG'v(]v1}@eem.]Ȫ{e˚eZ›!u"I%}:#t(gv#HIW&?]$0i,2^>2^;'qY7{K{lÅe#vqxqjWO)h>>wɉ}Kc00X<]aEkyφn6fE p k8P@biLc1o`"Q [}2|Rؾw]? wy69s@ ȑjCip+}+sGa߅iJ]҃;gyZ1 FyuFm.m*pKJga&brr Wly78scwRkW|>xe?lc1(mp|훢PY3TAb=TAH{x*T-:5 ꊗ?ߥWszM7 Ookç:neQ$ąs <@T2Zﭫj͡X!W󓲑rR"fù\OiK*[6ҾW3BOn ߩ_1& kBsq%sF48}č۷8ą.J4} d }sfmn e$\Ԡ]FpQ6Wu h`MUο7>k^Bl$䇷m^ /-KjХ$*VysK3@xe@=8& {dG^FGt?f-ޡ_)vG4>O[혈76rq|j=o *B (5}yg 2mC(83 Ȉ=I9di1\4`X. ][Qpy !7n.'?upe6)NN#-:K9G>S'N 9_<4_,$ +I@}҉~( !ݘ"Ps +rA(ٖM/tk~׮iLspf!}+1OB泉I eF|,sYfcŲ{!,I8O'g}>qV|^nL!CS<<ԟi 4#,sPz פbѧx(fu>{^gEUUquUq;E㣗m<<ҫ54-T3p:^~BtRG.rhYB@Oe "dx:A0v'[y5Ef ? aN/nF);)?UVIz+rTԬBq=; GWoկx~[+ު_VJW8S դ.c&@޸2(>-a2s8~&]I7'X$bVSd_eW"; CM@S"Wi hs2.#xchQc :Q&x@ds;h܄ ~ɭ{<*vDiyx! ,&"\fluNm`%pW V&_;iKt[Q]yXA{Yrƃ3 F 㩼`P0XMƖLOy 9Mƿ::m͆IO{g-<6׃L9M0*O҄&93 gz< :Pwum3m+7>qF)w}Dq.}݇~4s?7v҆+y06i ({ >X͂t9 s" oW ^>A %b;OU/8%`Rb9v~[ -.ޓaQxj % ="޶I$UGuexoD{$*>mOsyŲ %&6sA3UuJ.ӉT_faUo?Ł}rv,5Z0ڏ)P E_,(p?,pn,ko"fDpN 0DE#'_:Z1DY w\t)J2~@DSe\񺜢M` "zRFLMe2^_<;ߋY ?MCvˍU]iMĕ1N=K.IvldE|HI0bC$wv܎@'dEk MIv h>OBmE 4_ )$( D=wDBk?W,r.+~lA%8U:EYUT-3mo[p7d+UDE WP2&ZnO..}Gh,<Զa.I+&+L`0ȏ/:]"Y\;N蔞ye PfDș%/Eܦ߬NJ d(kMeȋ b`eԊ PFNmii~!5\|id\TfuOX| /dk讘Et&Eؕl oP})y8]j?)?FHG8k`3qbf`awHF`CC9pg;"(T&`,DWZfih ŒŻP b:ex_qGNM_wȹ`!wtc&O#~9~@ur@F7?BD6@jT+SҐ١ sϔBa=Nơc !Nq1pl1~`b1Ȣ:d&bZ\ C„1lD\ΚJZq*eI\x#{Q34F=(ڹmʹ㔱6̓ y, 0xL@m|MD[9Glt6!xRhyr_u^!9Ǯ'5zSω"lciQ)Qu%͟;7$hhԞk-u`"3ϧt92~ARobeJ1Mi-XŒ鮲XWvOoۡ|0E/Ѐ #hi~1#6>TXou*=܎/B2Dpnj[XH! xڲɇ@vcjM\?#LRxkDA{p%i_`W96]BE`F7ToKNGllPh蹋P <1P5&$*$n@R0 W)4+0Ԧ~z|0(ՃV0p rS(agzeiΤ0`F},E.n9F]?܌xvi 2 |{ & u}/?|k`2lA6% OOŷU47׻}н*eS/z_|~Ѥw8a橄bм`/J`yS:EI Ũ}\xK$ژ8 O Kx~>q<#w@oAџ|L}1L(z3?@=J_q ˪Qf{Z< 2{?WbE:oo ,\<*ԣ[gt[1Bnbrh &k-e{KwgaʼV9gNėK|e/ߥSoԷ >! -^sEl*` LgQ~q *4›9HHc6NR#uR: YۘuPoBӱ-w'0[Gw>u)5>eY|TՖE"޲jafPFts(m3~(mA~P0H`g{Pkm'>ü2Ď}NYy +OXtv!눘74L*Q̭>WY>&9ːrUL] T«u1.nNfI]4 %VF])3-Z{n;يH1CLXƺʷ"|ʨ2aHjS (YѾ!<NMs8,H-F/Oʓ"8_?tJ:ò2)<6?HVr,9tR0:4ZpjTeL )0aiB+xN23y0i3>; +MIQSx:BuBEWVNAql&`|A=%q"͸8dnaB r좖"-ԗ'uVζ?.o EVKRNl\gݺ`$H# C1?B.ma( dLS[5(E|v:83bL \_ g&M(+C1Av %D C&-^g3If83ad|~0lWLN\ة@R#x$;r1d꜖Ȓ7$: AYV1h(t ]T)]-ymJ&DE#j[3zqC֡Ԋ3֓Eg󇤅;T]:iL&1IHb)H(.s|\< AȐ7gs1fv\v'dtO(9|n~-ڝdMNݢ["k͎56ɞ9[l0-2HLdg ډ(7r(w<ƩKn. 3neOBک?eJ8mWyUaa-E ƨ4E~.T)r(.J/$4S4O~(X횡pxT_o]oGYsv ŧ L6ѶDѷg#QL t0) iCQ|#3$yJFNc:[OOOEBO:/U39(;Gy6KIaI6m8HTڽǣEঠw"B׶&hwx],3u'xdBN` p((%=]#萗 E!>);T%_%Ÿ4*MGj@W)zOk@Ѿ,n 4RyZ nۣ.@u/INq#LBCjFҐbr\|l.y ,Zx 300"tfT"V[ hе-_BvXamٺ"aw,]aK)Nˎ R4qGݰE_5,qv#,0V6UV k9XO 0K%w\i,0-:HH 7@v`_uW1*T^D+İl<lnƝr涽@Ear~a?(a!h0t/{I:| Cx?A0OB0>olbɠ;}l?Ll0]8z^ֺ wx ? ~Pܷ6GǰWP[G#^J0odtM2L3YRpk_|.u'4SD 1ķڷKB[;(pZDScsC•O cפYkƹ+:v'Onw"3Y ؜MpAt7`t߅^b܏cz@ʠH/) ]["WsSX- e<@ qD7eq:EH՞)9_ayԉhS .TJNqՒʉjCH[v?9߾[XϼMIrewy5H)05n~N"bM~%1|'?דϘGBN?Z@@:nU-DN*nuW dF)Җ>C#^ADRd 3NJ#̌8w,_rjeяÉ05ux I>gi9 LOhI)pi#:Չ̐δ݂q|]Q["(?3bʷec8|HwwE];^vLb e.#>׭d}q0 ]e&U_O_ y%F'O`AE,^}Q0Pd#lyFLPӥZm$O|rm =?` M =_.\>A"4F7g1ۅhx#@E>,\ՂR`GH/Lxʖg5S#ekMRఅ=HUn4wEW%lbBԦE@Q9|q \oN$ \L'#O" R["L7iCj[3x{"8?6}75@2kXW)/^\^T^^Uqx>Rv!'vri. @YJa0|8D >з+Yz 5?ਓgo<(7əN߁C?.u`zgNpx|3gA7ٿޜI-rM!`X˝Auͤ`2;IP}'ԿH˂`!:;ORgV|_}I%%ذ{e1'(S1y%uDr_E  $hUCf ZVbćpWd#KYĞE(w6|Mϯ^Լ㚝OBd >_D Y+GU8i}!3Q( C5Yr3W/T5,ӳufJr.lE?pǀ>,0q:~]OXGȌh"a O,co"E1WXk no:=:#q0uH .pJȇP(X^eMU%c{HFH܀;-`=܌SR\R1m}'0*~q!oj9))r~Kp,ƬdE>E=㊾̼5U 򐳷iuyw`b;^ua4,fVB0ҀjV4ƍ `|,-ɲRx"SM;'(OQ[ѭM+`;pw1qR=ѩGӡh;U k@I BH mofj)85g$QG5F ^7moOlD+/;A ^ң,0{doDCA[D!Z:xEɕBwb:57QL&LăqHіiDe +qMxH`}]\ȱQ EGs%\lg EyXi3PD]ospS V62{,^446YhPy ح2a7V?.& >,0Ye>*C/ _\ ,&`b0TBz41&_cپ` d 4 7Ie*ȗ'9Oq- >S0,8Nq'\>'dn=EzA 2'1l%ܛp3Wg@$|WcQ /-vۖ3iݢ 3[K/՛a {Z=CFS q'!FTjScެIpD"/A6b&D[ *eE{K'ʋ'44mK&(ein(D1@ l4oi<~s--]ʶ?)eY"VR&+w}<bIM&uCZg]Qh7 #/@K.K&ոЎξN4N#Ѳe5o6&qsc'nubN΢M:6,h#G2.0+f9gflQIF9Jvޜ討$qc ?L/,`\-e6Q=i(_[\72 ĕi2D'vsi)[ۛEsW@׋-/-Ċ{E5@H ߑ`#a_pnGFY4ѧ@֔ʝBH }_>͛r/V0M %O#*(jK4jpw'vt 0oC0RCU|+SlU &5RD4JawtdAY੼NuыNC\.*<@ua,Uڢ>%~8 ]½+ER7EƸpH9¸ j-7ZTRF(⪄)wҍ4% 1݇)"5T{ˬ-]x'vh3;+aIz[O@Buܑ G:L\sq-k%Yʍf!Z@)r{!qU?#hx Rp*j اk nu~#?Λ%`zerHKj RvU]]Hm+AxF;"TX=xav @l$&sAZ@MObM v4&x4q4M:~qj+Z\bjPXwaæt"[E:xYERh" R BP ]-HTU#˦Zdr0AR6v˟EJT FydmҜU^KXpAجȦlqbnU(ڊQ-xjO)C&r v+Zg1_w J`!R4as+j4M|'톸DWQ^st$i^=м*~6 a-vNH ,uDwPDńCr_2^&,kSB  a?zЄԁM2@'913HR>"FZQ6O Y֧ lv54J7b2&|fpRÑ+L@*|*G(3 GO !j}b+%5}MӻSAi  Lڅ\B>һgHYR,ޔdc5F'[h"4/NKb^6z6r*2y`HASHc1q F3)s 5ys&{ b{3ž¨o>},qzaQA ?8Y@'!0_f!s.L.6gzO ۤï|Enmq< }cݯAjc%hGw iI|!y†#ʷ8J 樈o5wL΋V~%56TB!ğ4 y"-IYAmߞ@B },]I׬6zh>yQTϡ#^Yj1^^ /ӞFz5/xEw<ɱGT5S bF*C%xM z 0IlK&ᑼ]pQ3.-5=5B(L:xH.1h<NmeƉv蠥XAo n72ΚInpr7Gw-Iq;Lg&~8-m\sPJ\%57(4Jj|Z_W\0YʋdNe7I |'D*|?ߩڷ3cChAKYʣ$(C+-(1KVTKN_s*ߊFӾ l`f@,@1Šz04?X.p [@ljn- |AP^|~o#Mv3ݎ`HaE:t0fABIWo2>|\aƅ! OMf.maɄGGl4~d>d>=4yih8GGlv}ןNĀĐR>SREZ63f*>c8&_/l)藋j;͢iG8~κpsU dsQ*W% V6޸4h¬?s}#S2r')sPAC>U񃯒R|TfSq-. к0й/Gr'}a Gh;p|r(0~]T ȯ~:C_&O_nl<g,PB~= `@t0܇P+zX`w?Ga:~{M7lU)LȀ`t;-4y!>ϋH~"jc*rw%$, ƈ MTKsm@$ynb.+:l"40B9 N=n[ du8d aDa%;&8()MI0~ٚ:P(}T7\Lq>.f7bbx37Z܎+߹贱}8X=g1a?G)rg *)Q:zFK~ +ɲ8ZJJ!zʬN ?ig*е߈.GZ>3 C`h1¥I3]c"b|*P2,м, VO9*0M3j7j؀)f2Z~b&&D7bZt6>ɔ'Tge'4^!K!KB9O KjϢD'.Mφ\JKΣj=JSvNÒOgX-8YD/FwcsݍNct|&ӷlp,g1؟ rqܦ9u0-5!oc;;|Ci?F8]f,/Vgu-<#+kR좛Ee 8^mqL gqC댴N]ug?Ԯ$X$P.,1Κ9 06b+SX,iO"s%6>#+g0Eg >Dm][XC`S^I̫,*2ce4v`(3`gsf?\hVhf#P;,2)ЎdXo)EiY':d|XD ayP{2,,1v/Gm~$m1<8tvvb4ۜ1ViO(WF\`R|}6z0t˨Y~|viǝwvvt?\7Ue d@Y1\^F(VgTmֹ5ion7ؖᥔ7gZ G|pw^wǤuei01=WsNGpO~,k }6O~|>UG Ͱ`/%Ι+D?$)q\6 8pJ[L$1~uꉴ_:w LWmה'\0h0L([OS8*huMJ///4|| =h7!81g|MZm|sbs낪a nh// od"8r$GxݴD;֔ E-?}5`H6Kv6&vA⑓u%p]^ \cMtYW>e]ϊe ->El>χ2SE+s?܂;}]j<,:uZ\\m~u1ɇ&$6tTOn>Y+Owtݝ痏](P>Gw|8";?,?}g~pH+{ /'Ć\LJz|K E~c,<HG|2[!LJ$TW}1G pMpQ8e:gg vCa쿙vX~cln/<vG9l o92[ĕ=[tJTG :xdtj7oy>ԊYUjL 9$S =i\CfSƛ7ҁ M3?zS𶼮 癤wC FC)^/AMoe}x}sY77lr{ Z}]?_ͩM=x?!pz4UL}[E+g1YpKL+צr_Zrgm)E {W K8^mUnWY]'!CLwzK$7'aNǦ&(i{{g5>,qX;Tp& rkQZ 7`-цi/ 4T+6خ!?3h*zxhbcP'mbCchT}Z:n~Fuvpբ8y ~-yG`uZ\yx45`8*nhr"ɿNpA ':⇏Jeq\,f0M[q?@L;0YMw*n&V"=utS;[:pڠY3: òx):dҎ@$l\鱙H?@5"Ӊo;~: в,3Tz]g*p= gM3o6gY?BgePɐv;DVv)TX̅)fo ~ROb)1[dp8q \ @s}@Ph {s 6P'8U"`8i4k`O ! ;a,M $GBmF{iúPn$nFp֏[L{3Asɰxz;jA") #J%7wɷikFi':EQC{5_sxןt>a)mhِEY{@8:~D)&ak>?7CgtWt_cQ CVz+菢> zi[zt_E(vIS~9\= 92"-Ȥ\j,(]D5&P вM.o.}$ SRxNLrqߗj7vaR /藍t\lUǧ qى{'lH[5oMX $“b)y?j=`J8vK5蒒^ D69j`,:LzF '\.qZ6?4Y.B߸)v+r N8& 败5RķIŘn/o4-=1늰ZaS|76jާJZm(F=f Uu-}-Jٻ8gS2GW>,>| 'ȫC>6 ew}_նf+2p3'z̏HGsCy*ק#?NA"+3K=*kwtܛv1O[Cw۶Ћ\H G2PݢlhRIЦo$!z:Yާc3*,:[TN {n`9 H%$7]"Ie'0A0*1 K1!Jl_[`T^Ifh5PG=Gqb\j 03%;u8qLnxR0'- [j/k>'Mz{K o\l8B y rqF0|b7AVc#xp'#I(< è_{SCȀ{NIH^afПks~KCAjCG5tZd_5؟v$v! $`JrQhtEfTp4#bFP80Y~}Fu 1'%9RmXC-nt ~2ސSzęØ(Q,sRs*dKvv\zkY#uUq|D8/eĤq8$JaiScaceG z=RgϴD& amQ}_n-x`y^=猫4*XރMhbj-xXf]fڇ/@co)A # u"s k ud9&,<B$X{7:M|8,5F{z>-vLAs'z'bIR5awX/x8V% qij8 iڪU{ܮ1򕳢 0=LADAȲ.T9qE t!am!l/{ٯ Y*5OՂguԁv P#VqFATDՊ@ȋYݢg7z?8=c~EDˮAP[(6N)PO&w4w`=a4̪).GL{]L 穡1O*3pu4SuO=M@Kd58[hMNj]Yt131ޟSs^vEtzp,8XHfq,X8-qy)eøvCř]6# A.+6o[ K7\' {&N~"Aa/Ll\ /8OxX73!D qUPQA/̴D0pDK }bkSX  lgMqt\HX]e|8h`ꖙkMm5'E~XX"r_Wba|YV >E)ਘWpllPu iQ 7s8q &5Ԭ&nNOz`UV-"S$cWY~χ|ÌPj,|O_JE! r,<'o9DJ0$0AS8ڀc2\ i;je%'Qѫ/F4aR &P*{u 4+ IcwE*{fԼr˝>OA&;3vHPc OvDIb 4 i (OVЙ&m)Ķ I$`,jK𩌁#Tar.EՕ٥h i/ -BA W9ym]Q,+|%Q +5:^F% +E|@;8e5Z0Fk^>)[ͨYw iݗ\5K=қ>_VFCnN\A\N)`[_wAX=9C[X]lkl])h.w軐bJFwuop@^Xq{zdq];G7BO=RΫov)*s|Z*HɱGS{}tNV^ ܸ~6c9Ė/P7z=%9r)lJ^/(u:5}_E!rVRAgsWCbnl*ripV.#m^yӑ2;mr_R;H kBjU3x ; &),LR]G;VYph7sj XKS`\_ojtZ/m'N P-RQ(d :_C%*[!q|MN,=KzpWWNvT ء-5z*<(_x#!ܗp[EѦ\sj)P<,# E8"#ȡ[t[$ 46imߊ̙{qXt\^ܨ kPm_**!$xz"ժ|׀=j0Ï@5 1`l0ȠMH^򊉑U. (*oba"A t8r L/5r8r_X |R [ηO_:;ih[.uc2"o}{ u2⚃LbH ~ /ƙ" *īt]⾬eRl"*AD'RhȈ]#k (-/'?B jDwۧ}>ɢ2L/aO^(7>60h I xC'hdwv'_)?%;j(NLK=PG"푾GoukᄀwzAu_ó; <}p_COxJ)m2d0 0$M'V8Ǯ&xh=kKÆy Kр?1+<MG^mXJ?L}4TK'<..-]ekә-FƒP3ڏ_r`/܎s 9v *jliPnbfp;Aw &y` "` C|flom:6Lg&ƶ_vfpGd&6 G% ?v70QVɤm׏#8LcB%ߚ_r=i/a~j/64ꗴU$%Ӯ_҆Ѳqul~►xG * EԱ^6b!5H(,$1Z`dmn =agJ/O[~hK0 NJ>kș=XZ_@;jڨM6T{uE5_nG1"KoiYBՊPڋ|R._T*odC_Zc aגJby$qEڏAO(6"7uKaa$Lk7ߒ[u&ߤ'ͪ HoZc-gtQJ`O HA7ˆb`Lހ֍M..o"[v́.֧햣0jpZ~IRvtQIATT{"q*5lVx<63CƛUS헔`[! -zC\}* ɭh˫ 7Lq+}qaltY=zL*Hp`Z>e}R. y{DZ Cu~Eoi7%mȮ*NŶ1Ad͑zKjfh ^ ōVɹȨ`gT/lVz+jG{PHÁL܃ze+w`Sw;5UNdɪ͖5, QqCE;B՚{UŴGV͖^e 0?R C%W 9BT:$8qD4C 6=m"c*X5ϸ}+D35$m{V94мhx|HTOeC`BYY喛ڢ&v#'kVV;K'2PD481fڹ^|S!iQ GƟ^WiZ([fs\@]MstiHvQZN#|6kjK0S(YbJLjT 2+XpaѦ~w^ǟ0e eŐPp }=DZVO#OQ8a|\Bhxm a v/[%Qйfo? \ph2`:Cb 3>G7{1?:(0r=pqo4YL7d6YMt=e|tq_@=?:M% 7jjVl3N(,;aՉV2Dڽ܁¤8砳̤Q _v>n͑t1ekZ=iƷ^Z O.z)+ [s=W%wMp:8Z=P"mjSy°9¨9qQ`d6g:p!Y#N8[_ PHU Ai3Û?1|CAFW|:&_F0=6\'0BI惨 -|#DMtMMSܛ$a_P[$#,GTW۱ RMT;~8²} AL`g2ÚJ{bCf H|ƒ'>(я 5jOMRAY ^ 0>Ӡ{ _a.ÌuLLPf9~""n֯KcX`dп #ъ'[Gnr̕bv呕 p*ZNG ]XC,hVrgn!O#>2/̑AA!osY6%niz /wkQZ-0L󐇺5/w-!c[:aa] W ~Bsb:g=)_M/U y!cA܉h`I..`oGpi|x/1XG~]aYx{ZHae$ԕ!N3wn%jVNZ.#'GiP_t>{8٤-6,n/ rޣOyS)|G"ԧhП, 0,~!؟"1#De4||)=D{XMx 5g6q:N9?`;r˓de|h]O1d1Q! XqsZld*#W)Ǟ7!5GTm&~em׾Gٯj@4>pksA^;r$BC5nH]ҔTBRk1=;[jŻN5\n[_ʞ7 c3P aUle+nDqoi=,]V'}qjCb <xů|)ƕ\[nGc0<#[MZϙsvJ=c)%k7Evcͱ!Ur]seKSHuLo8V|FD]?HJ| @^ }ݼ-V23rѲz_pM`B*50dh.>oЗL&۳uTP rNsjdáܨy:!}݇6raVؒgsFƢ ^KHǪzgļRˠLvlkbǑɈ$ jHXbh)cFH gFB9:H M4"ْ?|Xl6EKul4.Q|F9z $3+k%UЊ4HFAO~oz1-&Ҩ<6&气hu p[dFC/cuÑu!tю5^t$(zunE2f9~UnAV&NQ29,Wֶ_4juܳ8a2/7Q_}/\[F֨z#RQ8(vl^pMK )B0 tj*N{>IX1ݿh*zQuѪ'weq*\ք\njpJ":$'T\Bgg ,6=A􂍡{hbN/^: dZ?ARt<ȎUZODu,Z/[FFSꝍHEW|@WjŽ9xSyN'."'ȴ!˒{P&2;EZ3*QXKĩ 4J~j8 ZOb *(Ul K[Q:\Q7^[H<̐V*r)ҔK"Eʼn[c }EGSeDp2y"BKJL|0Z)w8)%hƉo)3RTVnFhbB^'#znaME;8GnEWFBC6& >~IϨ ^ox8[xo{+@P׿.$Vb jʂ #7vAS fhSfR=Jhpј@PcH 4aoGK#$=R䐓qIY،r0c]ҒYfnR19'yO Niv}U<({SRXX`Lgұ{,X`mFʝ;aJ|*uڊ94 Y6 JBi}CIr}dؖEBeN3oᎡ`6,DAlи*2 Se1Y5+N*q'Px:-"GF3p XLrn=7 Q(` Hw@Z?/LݮpF2 qZ Djc;~'>ռsl=%o 7$hU\:0OOC"Tje^);je)h# )Џ|^޻?k7AN3ԳgX*BRhx0!T?[EfCV:25zǕ#PtboΩk Y1X Yu#MhN' ܟ`Z}^k!˿n eqUd"#zl[0i 6˛ k x>J+pp0_V.xc`| v,W(;Au=ZQlm2RH9ws촛;)$1N&ZMQgKA$%ѩD :gh> # xKX7E[&R,7^b R;j?r XSj\q\R8p&C=$z~4#^KKqjOIO">prn};Pn8ʶ2òWRlZr.1? J>zi&q+~?@6)ZuIܧ\@^^5mSQc'j;߷[(+TPa{KR',R|Bs\?86|Kv] " k݋5Ce,] YwRh1*Ɗ720v_V,O6y"Itܫj(v$P0ˆv>ɭHA[:XIx޹2,1`\#v5lWj Oԅ@Q`Tdx7Y?/=:yMpg 1m`ErMbJW7X;XOEzġ2+bcu:wnw s`%2T0x@77m GBJ8i_"_k)PџA ȵ>15r *cHFލ@r̚$Yx Ǥ;Lb r7R/.H5fRyzTNAqxKՔxjw}Rr{!TBR?ez@iA"ג0W7q۠WSY$viLYqHqN@`c7X=^i3傴A,> Լ\C'OAdp &:ዉ5GVU.. 'ҞsѴމ59mm3 ج6fy"YHSYt#~aɛ~T;#raxR/$IWE?s$Fd%&ϐR1J(: TbCEKjw$}xAd<K0!>ww6d=gŐXS yB`d6aٵxW+V_HYN\I z̢mqS "9q|zuCrx Νܾ%2gjN7#4 v(?|v;j3g_'ؿT3`k@W'R &ԃav|==#ug\~{ʳ[mYr U3U)-\$"Г!Nٛj9ָ]P$l7Ef F801՘^u"jeEe.SR7j z]<^tq}}=k22:t u۸ ?<+h),9o7Ƀ#.N\  G4l܊e!;"bS6 Bw ۃŜEJzn݃ɱߨ //粫;2AIh;v3GT%+1;;P" \Tz- . L7L &!pŧ }TS n*vV(nٽ^Mȼ+`gf"; ̉Ls8:|%a mҴՙ`ANj N(4'xzaOMsJYz 2] \ЦtҶ)<>Pr )̛7ɿCD|zjI@\mu#T[^ 5 G෹rhM@v]up>ZjQ(7ƪZ n~v)Άů.]8ք)0LE&NmE{v3uCBZɬY)Y0<<5 NlHS2`#d%yn}n^BL’(GȠuu'W^~*A`J;DaFqk`\[hYއHvKrL-!N@I3:x8 _MÓltuԧ'0bڣݧ[wԴ>&Қlm"~:#s.vN0a8xaFf헡'?p%Mm:טזTOd|Y5[fg2dXwfԃ)c!\cj&ixcn tρ~&>N 0- OA;F& owkH\n@4&G8sűv&.U㖉*`_Z|v:GE\̃ +@|So>((SYO@x灶?Y7 )Xy]k)l@dmz',Pl7+f&/(=@3Rjs/x||SW՗5IC)Cׯf DDTG#)* M-Ĭ KbGzli v|!Ig~V>Jg4m{i~ݧ|Z&WkNiBƷﳇ'Mކ6 o!W6&,M—_!>n_9sAb;Ag[.y@^Vfp5wb>^\>!'hTη!iJ"^7ЮNTʠ%`mrS/Qéy[!jҦ8]0C/>$}П9? }%$6Dq'q B8(#UۍnEOH[diGM)G{ %Ⱦ{su ᕠz!<(!ۧ~$5GawzB0t!E܏-=?asX8)$_@+Q}S)nQ?KF\2 J">5jd@#cGxaEn΅kWG;n^ d&\"\H3߼B[BZbtcNU>X25P c zCJ8!jh Ct㓼vF&Y󓸈b^B3͑:,/N)L9עI70hvHP/BMLQ3p9&| -pN?LOtH> A_%G{셒AB 6/wtp,-ߴ ۝jny~3ȇu lĝ3eYI&HZxJhFuVtbeYV]R]\fՎ8nh?kB.E;QPg 7}zBb"@h>M]0_DeAPiHƦ hU`3F$]WTuARR q1,fP0/_J `? ڴ."|35hQяhp61'3hA ZƄA j[z Gfc,kN ǜKG1%$˷ߋy1]̇ybD_7"$(\V=}$BKln?D`aqRXdK9?Z|GBU!Ҭv"x0<CpϷi{mmf<"bpYvI U݇;)58_'wڭO#i{:-]`1Z_<գ x|a7O3v~kz=@KLz$wW>[/Q:u"bYl`_>ao~ȲOqBFóP ,}l{ ]\!30gRKByIgc+d&M.i s ̀Ac^e+ Pe,e6J9'j(S(W: q5e0أ7na[dza /Lv'BlCK ̇w1Cf* P!Nv@;dAI&*ǔT`hj[r():)$~JZ[S0^oa}~@9/q _:)9͇zKR7Oz7O{{;yЛ71[&&&&f_ ,50-„QDe#rFh1-j%saa֙<4YNJJ/&>b8`^޻=b$sm-jCn榁v} x7Njz~\-O|ϐ/hV\!|Y4L84mJy'p"tlkO$V}\v%Ӈ"p\< pZ=(RtOj6)LnM2VR Ѹ qyݮ/n|{Y1;{50nlal %\,}iH:ŒT*Nt{g?2?*j`9Pg,j'$p.蕣Yb?xKO'M4;`CХ_lŖKyt gv̎d/{^m2Y80>vg5yzsӅ/׾tm J, (v~Ǩ쵽.m km/FVx`>*bnɛC֋ !5`+Y5`+O5`/4їRߣ&x;`㱓*s3rEֽoա0! x pw9'#Xr0?Ь^1'.UMߨcwb-x0A lvE',?Yj zh\`]?JN%]M^Y;,}d({ P'hvppS$, ĒE'8 Sq0p yh smS$=7\S;^?0 /><<\njn_ ă i$р@eޙU%jF{Qw8nC#Cӗb6_dXÎCpuV5:Iϓ~39̇s!17l[:E\Mu5P缫ܯ6 ×PSB-b;Z5rA^ax5bw|v@;@ 0W+L9Sa&kz`]3qNGP)0h\ST;ց|ҐI0jhj7!9LG_Kb2%xKecR=~ʚ\<+ˮ34h>X-9d,26=9K }j1q >?⷏ÈH9mWe0d<)į$ƕ$$Εt'q"qH7 4;<`eוW</ ]vy9v}o=s &Qݲnns˿f`IntīFdJdwL&zy,7{7`yw<`%Cm]'T?- b}-\Ώ?8a)tz=3Ìpf-}g԰x<pn`Uhm^jwP DM.w?FqȅR*椆e wr1U$ W:e{ᖣGڝ3TX ,d^v2AQnN,7zk8S{0ÖrCfn`FaWw~,` tPYS _1Z0?>22d(@IV WI(9k$iЬvmV*'KF%eB| ^*S+vӲE~RtM;y8!mD=wg='n݆{w}s``lv џOOɁ-V`[A-0}k)t}qd&0>roh\!1 B,}dbebB>^M׻j2}ɬ}{ K˺A~T+WaQPM'Qό4tUw ܥ$vWzg S­-ʝ^Ʊ8QQ }Vy jz At(WppMdG16}(nW헱حA|R]_ .(n_aktv8a_̜*|v+P*$+| [y .ivY=tk(NZoj[x=ȀgFnV|˿}nP;"qLvfԊQ"_VÓՂOXj]h  K%bRNm'SVm(帖)'ތ%Ӏ 8,Jz= o9f 7gV&fpdpa4SycumJ2nB0`r; qyM,'ZurMjx" `rojjMa]_x&t'yզ*;[DT@c\|Q4#`Ey/$˪jsmծ k).͉ixcanb8f},ۥprO6cxW&CcSvںQDuԇ](bQ8}T_0*8NѝAVriwc,7ʰ ե~%} Q?}Le-Nbsݘ4ei:祁-J:;+pQWӮ  ,a5llRRm⡮.FciheHf3j5SpFwjeTr!|\=;"qA@ = 8 $[Y( ~SxeOxL\[0]c:@xGjLX8䇚W- yفG;uRRGIx=T<,^uh^<M0~? ' #$L p%Ɔ(Z"rSi^q7 ceP 2s vO3ҬȁCgթJM1Y,L%hF6Rh֜:ރ#'匹?ـ]Q?Lh#FwLN€NG}*0PS eBtHo(x ԰; !8VIv|Oǔ-U\݀ ( N`]4"iRI!|caY7eBE'(rob1r"o:ӭp/Z [Goqf!H_ 2`?Z.ᥐdISeӼ4 #U;w:=Wr^BV/gIؖHQd-*_wcL;$a#v߿PIotêZA[rN M BશmӇC ʕdW |:Wz<nC9 r,a5Mg7Bxd=H"S4>=\\'7n$wW}`練8P= ׉!"sѵ(Q-AnIÊ,h>nm`@ xp< s~i\:BDsR#%$D.ř2c8T5^wHF qܮa\Fc6!^^\i,v^9Քr8!Q (% 1ZeE]\Nē *|dǥm 0(p[p_NI@UC ֶш$l/)]88qDU2?1Pq:ܔ|9CeOTTc[D0qz(iXa? ;X*Fl9PS p@9ɿ qyYO2ia~r5avn Ă4=|==d=p@~R^m~!U Vꭊ9ʮ9ί9<9XXo%lgaȦ ;4+@{6(aq~^lG$˥a4X' f# qBscJIc`?aV!C*H(0} { "ۀTr{GmfrDgx0CNpw;1uDjqdRF l?n4B3(łP%>$IxV0׋tYU?Kh/R *G}71auTbxr]"BIb9@rK!л^ F@dk/uWHq)VF_I~IvJHij*B 8G%dNFHٻR'Hk:w mFB BeEn\{8(1n`U/d0q Ui \HAA v+L@@(KEi `{ MGv2pZpT@0>Xq9@3)i&G <I.п˷e~/m~>/O~zء ;$J㸑xh؊=ՀPuL@,S1wg{ym7?FsR&W9B,]%,/=[ΊXܡG*n]s4؇H eW;O(FL9TZ4ݷ؀]CH9^POٜQ{,lզ^?"?$gonԠz(}VPt|K{qGPG+A5 7`@=n t7rhd 7bXwM> V>Bv}TM* u?P>$t )$sT^#(≺o=srl(~YdIׅ: ͻaPl,KldD ăSAj0LC E%rm<hUmFTV3!09FL1ce[WqA)j ;^}aqR'|E!"]c8{ג4Z0:ŞH'nCqVB*2W"ԓn5_PSXqCf.`Y#|~Iq^IhhmN`"6u,p&ʷVx {ax QTg} +{ :A jx\kS~*48 r< !'~60 CuşLh ;HkS^ӟ4a^u?A6HGC1И?Ӯ@ly;uR->!۟u!EqWG RZu8# s=2zU|F;}t=mL;l vꝜp_7p8WTUc9MsFa$IV&wPxzp]SՆE ť+R@L/3 j፦W9emAyjgRNa|k.ƥĦwټFu-~(%XPzw`ƌtA˥XP1cD9 YcS,[zfE)޶5"3Ynf&? 2oAܐY`<-|[E,W/+  =ku!<\dpnTK}n[-9.٘E[Kw.,g)5(>*Djο Ң`TY|MgFi;K#&Ƴ- F}0(yDWw3K=oW 8_Ct nzĦ]_L'?{1X\CӺ \)to v<?ѫy ly"73O[沼 Pc&q Dbc)(:-:R Jrn寞(5_ysP&Mn"3׭E>IE! z7_ro>OWa|G`C"r̆r8+O[ϠˊZ({)E$4(O1uԖ^ ,{"'yb 2*Cp>wa0EfD}KSǏ$(7*&EMW_XہQKp]rTǢNU9˛]""[~Qki˵9%pF v\<9FO0*Ë(6Vāac1!c "Fja)Or|s4Z%(x:ZN8>z}Fl\\\b0'Ʀ >|P@pT1_)-S8(eo| ɭ./nY2bgFŧ<2u*5sy^/ /\Q/I¨i/_N@}qXj4;ƈe˻bzq {`jjTLooaYo`Kx|˛~^ÿC~vq3#nfe⁁Cg9xd͝k0'ZO&29|s}S3th(ƾLFΚ50ʇfϫ3|mb/fBu,PES7!{!@[㪝qܺ ? _vdiD"_WnMY"%AR o#}ru[PJp÷WmsrxiBŋ8'_9$7 2.XN|.~Fh[m%.O;cCa($#_4rs劆0\λ⅟ #yA(1ZօTO:(y" MEUdQ.mSYwh0o/9vt@%UJy|䛐@/ 1LACw#d ӦҒ2gdžIcApǻ(4c1 D?& ҁɡszk[Hp á&sP6k0K.C=\ 9]@]z0`F @BNRۑ4`rӦ\.G?kUd,}Ĉ\ 1 YISmiE2$,'|KGl֖>*5_aV~7т)laجEsfɅ2bYO#>,?}52l5`'c]juƳyШrvtɹaal%ɒf+שhY, )Y>8r:Y#?'?YׇK`Kj"K!){KvⓞyNhȔe'a9IvS%v ?ǐe9pn!?7j׸L9dzWkm~a]B WF~&=^1~ed)VL$M@*BL⺝b?^wHM$l+]{5qeQRSx #8_ti^dщVgng*mJl>R{xr8 Fh< |sffSF`$ B˦"^a0?{Hߪ3~D/.E*%O}n4$>X;n(;e򵶏 ;j0{שz(ͬڂ"o,:%Zc[N DsBsߒfUxJKdy+yKGj%erf /wg/?DХ1<B^<&IU8ksIN8rL̏]_3Ӗ.-1˧%4?mL=hc{?mZ9n|!KCz+BA^Y⒄ ܓТikp>[{\bFW]sa~ "Da%hu20z꿥bsJB " (* s0% X5}g 4i9";L]``ϣxO>6 |Eoݣchӊ aP=邟!Cܔd":P 7=x&r9͓ :QXWbF/i X O"LkQu~!U RE5܍u,|Vߜh# I+vpy)4DF9DFg'2:;/'2DFt0bUZXhyj[gД` !ٝi},&Q&ip>%/D$Egu:;Yu:;YS܉: ~(Iѯ/?/':rN>%uP"N\&J$;E$ǥM-7OxFj$muzewbTxT@אp +\NI˾5IvK{ZZAT.G'e߂p0Usiۈ}b*?Q5@/3bDDHRG0OR?Y&ԃ,OBG@1+~L &wp4P0~\h1VIE Yd> KS / !ρʚ0:!䝘\E0iϘ,HR^f3qix)͖SaXfA*,MlitM ֆx"VX8k2HSkbZL2;l"k{F4N)(d}.`@{0dV+P5y8B qiZa-vlf>G>/.X,>ѕom^z//n/b/2q:e@Jo"*VIb{c'd& y">|ktRגrWI DPC?+]n'Wˎ1q;#2\? 3;LU1Hh>)RoB>CDJ8>@';'W=ûn^ukz[UEMe, * F?2MPXGӹAkrsP|.-=+|^}E`Gjt+!F3뽥U_f0Ө R3d5S:.'פ_ɸ #@\ݰHe(:Fju ǯ 7ll/1i+[yFgƣ#.] y"6JR8E9[me6Ul; GS2D)BQ1g?Ӈ #F͛}+kE"xyzofC9iW6kr'^G86hh<4d>HWtyv \E1- G{NڈHQ>yl Rpc{u~m9ɮg\mϻlĝ!Đ+89 D cP*،% ֫ZM>݋B_>w6hL/<_Y.3=OJ6L!^8DƠp &1V\|TTnoe1 B[KYCh1´OY[!< hX_ iW(\`z0!ڀd-Ε8[Ʒ-iydρzuSRXqW&q|gI9a#un_ fv{זMA4@!W$8~6Z<H%-b3;Z(eqY$f=Μ X$ºSR/q.?%t&ugd0ԢG7U6o0T1T,V'V"xTh| EHKt7)T]P 4^#bk.#pOndv_-j.7}ie{W/#<1Ŝ^A3jb^\hlJVoЦ|;`S7t}|V|ClGxoXSdg:ɉ-S*|^ PC 1}_jZ A #Y7ddÒG1N(ήҜ*3aw"-owj4tkyKvn;~wK8Ozr28UmMII[/ '~13>7& fM§7€/FXpg.ǡ!9)<ضxJ7\Fza7dDF4FnZ@bࢠ%zuPT;fP0ZyE!ϽYb';2^(`64vq梏Lxùsqms+,{U$V{&qi2ۈ+Y1*GB `Mݡ+U~7^M7;{ ܥrh3HrOtσxzp1 mOo%,CC-&KAjkUOEd~~MHܿT1 E_f4"5]o߇.nnp{rz9\>L3z~?Wwt|h=֑U1w93 \ '˯?@NC]n7#iA}nxůp96zzA9U8 ]Z^Ȁǂsۑ#<9}_Gx3Q- n(M"#Iq&Ft[@6_ Wli0Br|^p,Z:Q |7| V)PYYr(.{a_*yX 2J"M.K#%gv Ja+p.GqlTȂGj=bKqmGR&r Ln!@80WDpZr'gN'{OVFQ^FVn\ xus' PL~.&R掣-:Cw-aPITjf>]Y$q+O +["/sY"0li:~*-oXw-7]"86WUݛs"6)h2b&zPWK)D"yMnU҉|=Ax44HGgͺ1L+_O"ͺKax 2),DD ;˩L<"Y:/=w&}cR [3n.FKm# XQ#H₧!+%nc[n.Hxƿg{d]xˉ[)hA[7,T}]x 4Y_1+q-C˅8dI((CN1xK*>U/i d!q; 4ɯYiA>v a߷le \Idi6<Բ{eLղɳ_VAݕ+-N+s30E4c}n9ˆ~U9pk7<@|D.Xg&W#}ޔk XƗ3TU'cD禄(U".iKR:aYv'O&oU\ҬuPEj>lqĶr QzVmݝb;J6Qw4d._v%DsU! _ܕl=G~ 9&mi~><[}@gQ¾cvwb1>ٙZ9?j);C3} Ũi5D+aԣhzl%n]Y8HƏ)t˃I w22ku[:OOЏB3Oľ(H֙rSϔ,3WU'|'YܡyF80&8u (W7 e磯l 2nRynrӣFǝ*/Ͼ8>4g9>L|K,*aLC[d;H t[~G.,b6r=F~:JVŽu;AGuSo{=MW\8ӻF'7x@!7&~w?j)Ww ԉrqEra&.+Q&.]&гn_."{4[7W)!1#xq ?hF-۫_1 DeJ}4+P?"UEt`i1;1J^&:lٖ?|i N# T4(grn??S(5G_2|}bz\$wͤ*X6z#/BK⊈_uv̞mr7Pŵ's 3t|Af_C4@"m4e 'TPy:cm킣FE/a zZl $~x[39ǟbmbԝ$~3=bsݒ/6ACpX65FD"-Ww.ekQd57fa;>\S/9@OӇX8o-JېRUK20nq1]2_@2%;3i$Cw>FIj#-׾*^B`YzjnT=Ksg`%%}9)孪yq^ F#9qEU ]&IwrK|=i7N3_)b~K Jװ`"(Jgp!pg|u"RtWڱuI`O$;uzYF;Ŭ]12`"C$ 3Y9L%@I:HׄOZ`8y5as5 *#N@& ~ܞͣ*lS" ^~*}[d}nUw0W o;\8Ł`u/f:rĜ'^<Mn.U"2e $#gM_Z庆үM͒e~# gtW7nn>]LF3ڲ)D~(Qa0i&@+m9o^nĞJq?c67fM9YcqVo{t5!1.+mAoq EA~t>+D -K V)7YDB$ⶩuؠ>St[H\4Mld *põ?tcmRxs^s_=̹cK4"Wx tpMĞY`07TF?CCN/PS>|LN`)*GH_,Ate7 5ޥ,,ݾAƁ^68N#!9+LzBȱ8RmP-B ^Rqe-I,."8#0;/pE3͸ѬtQHgtwCeScNW ?3ٽhr!w#;AE2f.J Rcg׾eRl-Â㵛ת[#̪ 5OknBJ;gJE?1%U:&vkAaq?;9?pbB2N4A2bd>4i9p=Ul]'FieB_O`e<,UEq]}5ew85Hg<]GhaQSDԥJTv{KP38 I?|⭚Jnؗ:+u,)dƾ3wvT$+rYHmđя}1Dbɿ7l(_ywK&luUQk ^1YOf^{&YWzp -@6?QOC<<{3PX|:r10wng+k.7M>srM">o{B6[VO8%3m%:^p q !q2 N@iaP~eE'{Yn zxe8F^0~Q`b v Ar/q@W,SN=lׇkNaV@3px9QՂBk[YN>?u=:M*I_7u q_ly ]A2}XQ9+Y4p[:"я17 v՝a)GH ;By66-yVa3p),,}7(g싍V|qߝ9~_y.)Τ~!c`,kG@qm=oF=wxυ0I={xg%"כfW5UMQyeC<.ai+BKmg ѲNctm0yppՇh:~pOS Hv7}j!$x2>|OlT-[nu|yY\M.b2 "-p|uog^|_>NG%[80:ŠK}FZ\q7G_F pl]G#t>Z&Xd}Gzdz FqBEC$.!ء*]"1~Q_wlxè^>̴x>mc916)}O--5@ROg>X^G`wp(sс?E^~m܏^萾3At5z2 FPGU 1^( wp,yJXq?2ĸW놹;elvp[$փv{GsFͦ^{5J)dzqeT~[=OI$8/|vxQVqlE+U< OSdw~AEFd)+AN A#f'v3ߝ Ygd\˛ir=}=̧a>>pkF6z".mԇF,[ޖ?tAdZSƩZZ6Xg9X5;-FO [!(ޕӹ(`D;H 2- KIٚ^s5U*0: ]]*d#ԍ毳{xqr:jR2D/ HL\kNI8Pg?iijg?M/wTY=~gلFݦ!g#,-`(dCVYV{w$Di!7'v}x[lqG J$[EkKpnS9Ǻ\`S/Gb M<qѝjc_ aD{1RI0/"/^El3 - y9=7shbQelZFiq OgVXJ۝Pv=%.lZ̕V'\{}ql^ȍ_Go*URy'tr̮/j7HE>'oZ.8f!/7a:Ay?5Ļ8klᝠyj;=ϱ;m|Z fE'6:s#T;$3V.G.ؗ7r46 Mx- ښQnqjix!B4SrƒF`CG%JvnrڮoOab>}If]Hփ֍O4wד1 Xx/-lprh֭?8(;xyv9*E\.25.jsTlKn3ضs tµnS̈́i9 BMF}'YxQ77rJT"P.9?$`2Z2@ktK q۷h014/KW?'|LҞp <+tFF+a);z\,7Þ }![mSɴtr vS NO) kAܞ |VeOfJ2ڧqs} T욝 tbrr!TxO?i*kwZRz?<Ähtp8 `NUtp/j88NؿQăjtpxqv̀V$&\>x1` PyBg;39 7 6tb8)m~8NKUd61Ml"c_SxYn}A\n_B /&#J"͑XnIOK=F!DCޅv,fjPzܜwtiUK}kn|/qo'Fݏ.K4B`^.Rdz)VX(J_O5MGB+axv?orƒڹsTW(]GeAq纡h{5ӣyߛ~j#OjvVEqۆ;!p/;BEZPDEs<WwV1B%zΌq+5 JB Q~Uuq?,Hbf eo;kWbLZkAwyM%t#ЏsWXh@dRD T%|Ep*l_z2H]V765G$&)a1mb_^T(J[7\5P5gq/C+T7?D0UFv%w<LcLH% {7xװ ޯ/ 흋%/x3lC= 23dP A@Ã'8k֎lD HE=3<@zcIyA. ptx-VG7"wn/zM:-HH=?ozWӻ{vn/v/lo[u|{$SkY*\aSgBA'w/~ ko+rĝUcĎ;X+Yr'AWgҌ_|s ǥ>|AO8`SlkW[ZaU;osǕJIz"ri}rʝV/aA8ѭ݄ؑjiˬuuQdcƒSvlP ԟFgF4܊Yj{gϑ[Ca\\$;Fg$>^|P jA+J5VM<晡aHNN( )Vkp5 @@ S^Ŋq< N@50;;O`HI׼u_|[(gdY"

D(wEHtܝJ=!*2*qKAX)lƆvz3}qC8LasQZ k:jf!"ڌ|6CULjh[-^Z5IeTϣʳ$(@eן%(y?ۦ؞c-W.#mjHL<*x>4aDPl)b(3Ӹ`&e v:7Hls `=o谔[[k{]bۊa=}RW|WhbJ5<Đۈ X;iX]L1 BPGkKn#rBة="OI9}8 XW<2 \_,,k_Žg*r32Ձ/ң Bqs^7#4d'5wօ@:7 šI *m8_@xF-S3w-)!R^yEL *牤na+ܹtcT,FhMxiH|7kФz]Y͆י呼}pL kf S[zX}؜Yje~tk2ǗQ0\à 34 c`:) šBϯ) >\{bÅ \.l?Q!0Hp:h# !N4- {)?!YCWCUCHS54\MC 0>,vrIh'bIZFwB{i>C*Qt5z 45@d7FƿU攒eOkuCn~:jzf(+C$0"鍐bc$õχտdb,s=` YGxe{jHf&P;`;@p:PP;ؤa*z}9U@270)x]N0=m(A$(+ L{v\-R*# ٺOIqrcYv,oa Х>v>5`~/aE{J8|Zu0,Gr#ơh]|t< RM)@~-\eF1ڸ#$^>,}z3*C魈eKB4H $٨K.M+q@?H>51-D}U=9aSc+β/] ]u!nRt0r㛘o[=#R{dZ),]bSvi0j¡ H.v5$@b1ٵcC(y 7Gal29]Ѥ+ʻތAfٍ./K;Cj~'W3G}0LwrN%2^K*?f7}f|'yˆز+/UUeKD Bڃq0!LA]@ {A0u4ݓx[VCg>9N;QF9- Bכx)pI5SjT6L@Jk  YM]eb)[ * D 8,@ d7&yI|| a \W2 dk++RV35 -[Tj6(@PL`"D>*4{%&Xo[ZR(@.B\Wm.^mVi`*Dm7mkZ\% 蘒cJ";)g=LЍڹ~Zl v@|30phj_+IhM:q(&ƽqRN,K>~PlXh]M!r<@%@6.DdVF_| {xƝ"W@BC;ObgEw[w6uBWCf"6rHQp Eb.{@T+pïDw1pN>Hlr -lo5EZ2c:Da`[I kJQE @ydraŽѤ: ]lbQa4 F>[ŕOC?XNMnSDd O:ځ ]lBcAÊ9mF]jώsrsʭyt;w Srگ<ݦ'1pکQ| b8ZԭeݑWuBz.3'z,:U'#֋^ bԁnOJD:gXŞsN Vl01 ǜvp gj5Yߗk[ݯx-#F Ie(чZ8/jdcKslpumį2!xe4~mz;q8zpэLNjj!aҢ[[m!@N}㹒/{hu)`mzͭX֙Ⳉ!r0;+!ղYBN be@fڑvv[Dqlk?񶩅}!_څ[lSb|8 c>΋߾1Ϣ\OyVf6Rs)Z1swc* +U&V=?}kt>+f"outG)t,Z0Vf my{b}!0 (_~/0*ژn8T[ݑsc\mBKa4 ׮h>a˾j2O_2#_}`*_ԐU$˸\_#0} Fn S_/`znz5A녴t:mLqw̘4jL17Fcʸ7q M q-=QH>v#&{%9\xSU0 2dPwHPS fp:Pԇ1@ w3zyq+HrK?P\\Ln%5LG]<(1CGMe xBGNŇ[Wϟ#j!9wF{,GuKi~z{Wc W*d,=VdhhQ8<}/r' Wk7RORq=+^Oa [e?Ąø[,$bDTt\<ʫi% rq8FT2+z6q=~շ ߄s;w]f2fSmvTUZV[Mw(f0n̽a+QunϨ[s%8mtʎ_9:m~ e1M彿k[ں. )}5,,B ^ڿ?W4ώyZe{F,qPhR;*+=5nBwIQaBqLzɸRbjXՄ(%}njnpn8|E֏tZϫ_qd&w;B)ɍm+PND-Fzhh-!OHnQ@>Ƌ[F읔JޫWՁO]S)iF} (̪ܪ*:T ^p+r>ES2j]`J8.y?.:9wxz\Bp[m?_u=%J"_E]LJ JOIN+`Wt.\$D Ny)y̲E.HNb[9M|/T@{lG sƉ&DP /H gbS  bZx'oX $^g,kDÄiyau}g>@B4uH;P\<;w앒nHc{$"vp6*8e%Lsˀzs|d4lE CܤFadZ["DU!V<fuTX3TqPAoȑ|Uϊi4s!ʘq@#u, p98`#QlaUMJ}j/_(IQ7b@^0x1⩁ o:F-sހ2#qigI!mɋePB!qR.?ͯ43%R1d8@ ȸ^dqVޝB`Z,HQEcgʰ!d 52 xχ ֔ĩJۉ-]D 2:5J +!wC,}xFF+I~%6  (\yXgWw9>FpSX]e)JU~E~uGܓTSNRF_bVX[< D`@2#BBn?$' JOHlpq6go4{7P{7Dq?A"jzӘWCWdo^UT|v(*d(=G$'h4&(; 6<9y/3MW7*.^KE7=%Ka_ F#;{6CAo$N@|0sӅ։6sx2i`C$]"%t@#,vRe Twr?_Nfzbfi>ۇ~U? |!P8z7}Hރ)H#Ջ$}7cZpY^wNu5<}/17a>-v> E ^}Hs#1凳VGw4g5Hȃr8ٓW# 큿d52,5X)\wWی161^/@}Wwܺ}~R}Mt~Wܖ SLhV5Bp^cClj^ұk>{ ( Oa^Y#nT,F<ݣJ-  9/!vh#<&7;j=@6Ďyր` V]BI*+褿^dpTo1wkX^^ 1⼠D&ǝF v U R*ʗ⽂jCaBN{UcE`:TEdG/YSk`j^DGWߖj.I;CU5?&V-cgM1f_)kv/€-w5Jr@q 5^RyCSrlnC61eї[{Jx@ \ۈL )QXs6GkhiTޓhߛemC AC$>4_$9:+V+K)UKdB{d Q8 E|фvL CͷHaH-2L5.Ļ)Q/QU9@Evr7qnduy9.sƠSB 𞪦E(jyhkbyt^$/̉Fl=݉Rv5SN܅R_ѝ*7)^qjQL n)تJ*y2>i =< 10ZwH$6 Bzqlߋ0ǻ$0塡27ŏ:M76x{5< 5Bfw7zcǨy7BŻ+ *}36<*S] ||6IG K)iCP>˚6#4Yr6Ǥ0 )2!HA0֌~Ќ4lb~d%<{} UIԿC)`#9`TP=ab'~P$Dh=b`ɜٰF.df,RR7T Nmlzºj_ʶ.w[dcwtrL7݇3m77޽z2ͳ_ŪW``~zw_W>kjδ"t5ȷY ɢ&  l6?/Ld\ͧ$>t?|g]$6}0oINI\|)Ͼ? rs>hw#/kQ]}@8?'\ =#1ߣWȃq{W_nțR,oe>]q{ܫ6G)UzX D7ؕl($ 1˘M(_WhΰaLYAhK{PmElQq/S-PLX4vX>}:te䅚hjkٺB#F#AW0ɓT)On,KRK{YJ߼ڝhbɽ,EViEW+c7'⎹=cnoBPnSپT4>5gac:?@8R"{mxY3fU/~H2z٧/tx_4=S$#ª.Yvcl1*ed۾h`4y:vf3آ|y=1*0\}Ƴ"݂d ӄv/S$?M#+ oASn,DžoқhG菖`=( B15?P py*{rc (I݋QD(k'{m'7/]>No}͟V0j/Қ*įb\\w7 3@-\ qaLoE1 >?g+m%P8R|@r+ZyT}:/Ξ}X~(ٖ}fE rU,/cN`|g*ʗ:lOĕ_Q幻Lte|So?r\b)A/ԁ3I*҆|r_/4༁cr~,"z}['4JE_m>R)?njvE,]ŦSl_>eRpSdExxĻBFN ~uBsW,oqbr7q;}˭߼U x2,;{VƳC* _@/~qrKʋֈ'_%7􎩞?}w[Efdoux_JSٯ5~KaX@`8R~ۯ{M?g$n3J3$$6Ynhem(-~x@?VjѺtsV `U0[0_Ř?YBq]^WA9?a'RKڪJ,t 쯟+.o٫(Q[xNYIkkur+o0,7xj?z*B3W, `8nb^ d+4o}7/aUܝ6# wf縪Ecҥ[|Vt9w#G=%#O PڊIMiUo!Oŧ?[FDCb85p}Oq/5w4{uכM Qo=߮tku2>|qla[8bS M6z}lNĻ Xk=5r>ږ ZnkzP,WEO+8v˷ryFp }`}XQY\6+5N@+oxa1 t%z.j\=aTd,=:.má/ AeMxA#7T!ZxcS-A}y,zwqJ\ŁÝSīyXVĻqxDTA7b`S#( \g z}"/ #c|Ѿif* QW7> tj-iǦSs>ŤF^έ..|P<_׻?.fkH5nbxi@'ӛӱQ?1WVUd@]IS[Yf`3_On֣bRz[̜kd &MܐX5>" nm]oAnưx$-5+)˾5p23 }pb4Ge=DnBr7uJQ;Y0=aGiH>Wf-/l^! մyUq$Yzqw {: ?.pC@vBߊ*Bq "#R&+&(?X8w.W}w#SIn3'zC.iFO;ݕ{n 4k xr;{.P~K'4ĿnW|vPqW@'&!V FtDni6ԃ t@I܍G3TTdp^D x9gb82+IFSf<5l8&o xHp78\v d-ȸAB̛;ߑZk6"o52lLI^q}_1P]̝\]zqctYɋOczA@zkx?!k藶}QQQ<}\$[,(싧𠐖!B_.hh,ʸ2L,% ru|T{纨9n)w!zoG+p>ߏiD4 } f_82pW= r$ғ=cOm'1-bQOjWn}>rI#f`S-ɾT?""˓%JOcj߶+?g{2H Ynzeԙ"~:Z[IuF M6_>}fjVs5$z> ȳ;)F5Bzgb@ݯ[>5\c@Ռb|ٺkm$oxb`b!(#Td0tc?'CEeKJ8T0=Fr}.ɳz4V>Uf!ض?ja6[4"ZMvp!;-&eŕEW|_vռ]׬~p ݝm4?Eݗ;\΀pbwR*mGD2(I=\'0kݎ{@رGnG{R{!3kP՛kW+!3gοse&D޻z3ĦJ׉ m đy߸QSl[0tE;^꠴5a \0T7e #kAVyjAshCwn0B_ipw|BKօܼۃ_wKs]y3C 6ψ&<^?j4FWʬ)lSUk祖']\׋۫ۻ/iqs1-NW)b"\:>=Ng RՄs 4b}?.zPW:f"kا)V]zZWZ(w9aҡtD!gJwnTݤ3]mE 8ZGȎ]t`ytsPvض"ۥ!XcajwQgM0{R~-¢wN4j率/tIbk>6]whC%m~`R&לw``sVBIZ:4\ otqi`mψ>tyeEmq.^]sg BqS?EV%DTQ0`$ZR;=js}V7}YzJZ:@޸do R_I?X 6C)TեHVI炃X >X6~ sY,V# 2Ba ($W)?%d2)OI1Cao I$$ A qUv =û/Dx*(E_ݭ[k UƓNny8ˎ{!/ʋg()X),CekB8rnM|9&]qwTBܭZHbKOBdr9΀|Кæw.e rpчP~5zjŢ94blޣ -&і"P4;)5z_<~y8$/r+2kCpQ[rD "TTa YHo[>  z 73 {M|I:$Q$7^4a;-+Jh 0Ƥo4鼗aZFZh:z}wǔŒ4zaw5fh TK؟J.;.Z$u)'M\SL[W1o2`; $O|4YDJl KOvEpSTCj{2 +]܋\,z0VF\ z-t.P-)JOO| c:9Z!'<[]H&>fq`)9`k6ڝEJ T [g)g*$LSh.d2X#͟ވl^r2[@ႛ7,n`e2 mv"fCLiO RPP&謺{Z`i7:顙 ԰ŷ!N:Tr'Y4͆F%=)E +{ǔIk׉LĜK, PZB]xs=e6;9I^ZB]^u)eij4{'I 9!ڥ>UNy) ]**_Ǝ D0#sQG!/*L=h \ Oms8r^dDVFg(#ϝb%%Ny$P3YA6dZn|[w=:mbVׄpp !+H`j+/;azµy e=}GC^Mmy9D|LbŻv39"BWQZ%Y^DsⳏCkW䠱>ùIgFz⎼m'<rBs>8kNP|( HVr2:ENR$a5UWl{ʩ&d?ݥ-h?LHV* h\Z(iS;S2h0$CSM6Vm\]&o5&?41`#C6p^ Z(tgjK8F}XslϘGUIRRpD'b /ɴtIfOj$[|]Y'ć`Lr0- [F͘b\fOc4ϛncAC`eɵCwxЅڃʑ.-Oh--~% (Wgd qjzǽZn`3 I%oZ/8|!xi1I&fA'Rx6_o"r-`!zPH5 nXO0jb#p!;0,_}ް_[Ԫ =M\ UnjTSx~q: "fjo 2=-ˡ :ט{]e{w`u'Qϓ/j1}\i|vk iKk: %=X0OPxf-%8}I$_P OPo+ SA`_8q9qֈJ]t]SX-oUXíGEWu]mB'x#䥧I-=P}`0)&C3srPrE<DmZDQ]LTFP1TԷr)Ʒyt$] +\ŧmtWX^xGX̉Q.5⦂4\2XhPZ7 aaU:ws Rghe#Zp'Qn1{0|F9FՁlNE7R=OɳIUM3KIK0%bv㫕Q~% job!RunȭR$jU=~ڧ(9x(A`ջNV[jbaɪR]/BBtaCX_̀ptI8 G ss[0]zw@]n=WЖ}U j/nXt0Py٠>SSd@}>l_f"XVb:%Vr?| 8!J Ur~Klmbb o:dasdw>9Grϱ..ceL6G:=DZ@R;f6baȱԶ8`"VS]:S(ԯ=AI<(_l[Eqvg2{%[%nBxW3ֈ>upJI5ҝr&IPGU:4C͔ MBgtm, 9\E) -b#P:fHq1M }6!T-+ySgF$`|"Cwoݓ tkOw7B/rPWwzY3%%ފ9y`0U rhw bBT|E_,s YR$o2meY艫M~ S ` r`\B US :#]{6mHM[ɍf]%DIay>AX*wO  #Fv:qz׵8Y{A9k8F3(B*(BZ4bFHBL>Ȁ#$0)N$<œķA۝wX_vQxdW󋨲2m@щok7$YAHnTbP}5$`ΜE)e<*zyk20EH(g]LK`g8=+@(2!_]\b5 Sp@yW<.#FeZ8EƞiQwWopA*eI!4 p=p(56AӲOvʿ 3F?MOgĽ[O)cKa5*=6RH(p߀C)up @,{IVqS6J; >24D50׈U_$A3|63 F J 0 غmZo<A7WSR/r橸N,:@A kW$f#?{|PcAjGYDmQI rPU@J@aJ"JyRYC.5y56H7F6ODXliY!qtŗu$;|n&00=r~SRȠ6-zDV+}#'Ɲt|"gJL_z02؇KrtUwE," Ǟ[ar۸W I4:8Dxg?ڕwQL{ X3*k&$4 ={ϘR tZ8.6/(FBQ 'ڈ#&B6~>~xCr}"B[e C~5Wg1#>+_XW' 4YS>XnA~hQSԘ]NNZAn鞌U4)z4 *RlP.; #9'[%e1mzlV^Jy3 sb ϓNhM,$&;Tm?PKO-# e7 _Nij p ;y'^>t]vnp 6{oy !Wo(H4ٌs&FH>vl3Y<. ׍ͫb!ea[;;J+D\O}xzp ghtx[lfĻѶ[k D.V/7܉wiBo)>O9IYP|5`P "Gpmiο\d3Z SEOfpz,[X]zd6E2lƺ,ED?ȷ1pKnRky,T}P`=S0H(D0We_e8xe}=BSS\hGRo$#҃jNWqdzMW>Akrsw5..lzn0bi8yD?6Rv;VSZ*oNs:AqbxjpXX|/ P@6b-ڂ:ǪRQPu[4 jiz,'Ijˉ YN,s7qN_iWb)++=})ZL&q*O8^bjSvS{]-0q˝<`1CސF O߃ڊKċjk3T 'CQw.ZM!(0\5(CrrʓHL_qˈR4\B\c&iJ,i K,Vv]F(V u7d %$y|HVLFQt&Qi\`yߵM,.|,,41NT]z6;!aEPĴi0/tZ JSVAa$rwRMM}"[lyf$ϲnX1ṔuZ`#ހT(4i姦Hwi)\Y{mHm~M&$s2 W.&I)=t-:y)}ou#P`"kqhKMHGWYe4L"I`sк qQ* CBۅf_vQ4&0BZۍ(0uf}5Sx)!iNaF!SA''$%# JM Lش4gSKMVI%N;d+I3V[wulM] c4#J~N=pQ򍟈O2xT~xjq_^]x0S@pʒ{@Fx8#O 50D `uK?=mϚylˬaL@JzBtLKVF4;93eD 3v *ʬ+oXxl:Oxõ/=>_^ln{jv1A\s^`Q4n/nrmsgpO|V< S]ҍm7՞i'& vkyM='`*gX̜d^2^6B[Qf{  .O;-u7_Kl햦؎4L-?@ڤ!{(Rf=ҹ0\#3:qC2y.J";HBN[/1V.W5WWRz":&̮_ԩl4ԙڸhU U!0F}Yp[FwzE(n >S#& ZǨ-Sx#utEm(r#F5%x++R'wi3iiċ0sF8`kFb3 <8 Fzrz^D ɘINERC73d-u&3 ;y?}04>Òt<2Xh ]C8!{_"cӭg#wpkuů #IHo%962bi@btrĈDnkd!% Nԛcj[>^N˅-W[ΤJ r|C7JvO~Qcͥ!|sBW%l۟nzՇ^ zFOE_#?_Kha O H^BRIXM39m4A&ߑfhֳ>Q|bܤ븍 ]\DPyʘN*("X>Bxmz쨼,XS]2qlg b&i2{7PQbt?4,F| |(~A 'i! XHen8 ]A|oӇ\ҁ0T'Ra!m Z0z=5u/큌{oH1Z=I3rTx]'G\5iެΠZo!OhPwE-'b'p=yofo_ŗ"ZP TIڙh栐eFYwhx]ek6PYbYXހ3o{t]G _ƻ*{LY&&E}QM[5pg2ܜTK0K#(650[ӴaDlӴz~ gb?/@'?J@SbC8W0B$$[7#?6DfH4x^.9Si,\PqG}霤k_2}RB*:[ 0[}gTe̦qf?"}';/\R8kIxCuıYߌ )Mx辞޴a83Wo6Hf11:I)7bmL3%S-~JSQ>ڶ:]>7jTЧUT݌AwiGә@pw +*2pr}H^ p<~ri5ϡ9?qLq]5v&FH _ZR17j,#ĩ.YЪ, DkzW47}MY? b\G-epA(ν&%tPcxy=@7c=1Wڨ)np5{䶧0_NJG drJ/dǞD|:0مg#27IOy=E̺8$!g4 I3s=5~5# d+^=ёxtG)RtYB,Q`䫶Cˮ} =MpCz؅/EA9k?T`˝-OY '&iuWeN.)ijBߴO+31A&F+\Ŭ-&Q M fFsm`qLZjח6^Kq^DaLS_y?vb[mc#,2aSЊV[)gu:i5}ƃB8 >mCrݧ*QA7v8:kǴ\ Qnx0B֣^Zq8Y4O\5X`1Aq/pQYz/CnPX7.Nd힔p )0taQaO8mDOy{o*Jݢ[}T:w4mT֗^DuWᎹ!);A%'OͰƎuѐcț$4;I^%.^O:n]#%#ZRCQ)~gw[;|}C'g@ zE+q}g!DP@uj0>W !hc`f9Ѐ\vs\]FFhO{J$t4:gxpӗC(Q|%DG?|Wx 8 ftm=`eՌ*[BTcQۘnqQLa:;+S5C (*#z_8N osG7R%;8LRoNoKx6d_øisa3v3y=%@qY (ϰ(%5"P"ӏ J+"1]ڋdTi\uȨ\NW*l̲z! Z['U: G%NaNu׃6K-sf'om{lz ˤf}Y= TvnGČ"cĎۈqڱEVgWPΟjD%Ϧ}ghjk*BØ&-s]C=E9!q5,bRIᷛ;M1ɹծIoQ5 Ī~#WtmIm%LZptȶ NmDsfnԊaL;VP~ۀ)M}sR{)^GNGA tŷ }+$$NR#"F3jDd&* ᴽ e^IP\ݹ/v:)262ؙ:̈ ۅ\I<Ǡӎ$Yrs#(ػ tl.Jۧ$a,5!6$Q!JKD\)l V'o]s/&F < 5l{s0}X孽}߶|m2Z *됌j*%[-LOr>LRvjYW>=w+Nbkz1z 48`X{gj  y@RԡΘ"Y#%/f X5;Ulvfe*@-U^bz)(FDq kkғ%Z2zO*o "LOSTy8ҥ1a=ȐT;^A} q^RF'hJ'MBdxNH$NtԆne(n2{a˓ "vv(t{d0# GKCxgV@mʿ"ʚ2_덞 N(= P6(#garKgW9~!EZUjKqjLځ&*\_|p? Vmٱ0Or%clq/c;Ndj\}lY,qx]WqUWƑYt/>/V!m$0qʼV $E5<9ф.jJM}\Q;eUpXW~ho~\=D.Ya}06q1}4!5yerw3+k0=hnr37#QRLn+u|~j4{7M~:dɤөk3j)ZJ=`F fL0oNI4T4'_yao@r4K0s5SIj{s ͧf|;lODW<w?g!#AϽy?ҌFo_Kצ(<_ L"|p3r_6M M4N'ShnF?F}0w p>h&>H{Mpq@Aqoݲ=4_]P}Jr~ai6⎳iST``8_Abܺy i}#vr5a߈f贈y=~u#;AWktu0w/pEjx2:G g(|S ,!=l/:Q3EOS6NS7>TZi+B,>q3vvn~c(үgQ:|/ dJ ~dT-Qk#PN>\o'?mRy /a$GU m\H=nM^Gkpc^ {-\.]]EbA0MLʰhɲ(٦8䛺4{ąS\F  _ LHgknG[O\2*.C{s=}C1PE@+´_Oi,<*aWu+WӖ*j5STďjZh)%rtM@x:ֻK8b{*s4fh$D 6XDH5 X?= p'bo hZ[UHټz* faJLZl4-N5UZ푦S-XrYBft0e?hG:S[Ӝ /{+ XRD@1bTv=pb tpKB AϠꀤNNClzڑa"`/稶aBUǭ8=n>7)Rr"JnvYE)2̋^g ]/eB W A&fA_a-2p>pv?:<| I! 53x6Wn[s߅XxkYM<~4_MC16ճM?ƓAJ['}0 iiZ+h1>LsW5sA)>sr71c&r5t,6mRQbÈ!ǙU3. ?fFv0~xֺ)In-_@x"$kzˢ^_ض3]-UT;_p[ŋ *B9̞Wl٘LʰLotȄ񥭒D\!FJ mUˑE1-_.bMC,d0gtovDyr<:fybѲqO}Q s"dfE(@S 7\ȟ^Gx۸\n_HUAM4]Xy=  eehn܄ِn.(btyW TʥgX@auWej6RKl_l8À\ ɄPӞq%1}Ɨudx̿Kƙ!9?uOϰ- ~oF-~?nO{͆"/ ~cSI|%Y䦮%P7ӑcBvr71Q:d5Po7G(ϱ_.魏ڻFCvmoI]<:j{̭8 =IY34vLiCmtyx,]@iTe:] ط[(PM Jf/+MryET =%AKtͯ._ь|n2&ju!X)3=MSV&x8AlMZ&B%ؒ"_X8la(ZI yzffX.A(nOjluu 4FnG鯳䟗x>;OǹBLg+ p"[l,So2夢SGySi_X '3qTH ܭ8B$#Q/nl{0/T_JiDԯ""U}B|m;P'euWZ 6] V~ROF!=_>}uoXأ' 0 H|jR}(;P\ּ>.! `n mC|Y{yG<9L$Xi3^D}O$~kS)13MnL'8ɠcjhͺ7Yp۔UWq5CP޳^UtZ,w:X4غ {ԋcBG21+cgtI6p vơ#ԾUjwa)? ʌ(iϐns*3pJ8 23m^Mz+DEnʎe~.{Ӈ\M-arjnrf*lm˹y!g/ݢ}z$gZbEsxj{Px*'uH,N!Ġ}M*]u渣,W0#}aI_' '͚F,>h2PlZ&P5RahMw{9MFqdPnBa od*;=o{(7iƛvlR0kl|ƚKiXi}[aRܵ}y~4& Gn&w$ = htqrO5"0OnF.pV㻫p>X6GӑK;ܕ@E:na>gù.ZTP}/VF H,C8I#>EB9pTPxL)7=H738~dn3Ĩ4.9 l;{+HHw@s9Z5J8V&*8sj升L9eQ1. 0W欯nnױCtk6Z`6:L 6Mp]Ơ{wCrs wΝ~vY}2dFi;yg,dt /QL[j<{~7_@F`8}OߕoM<~rX^73wrbb|4^CD7鵶~7޻IWx7}>cU=(Hjp3kzOa<Б0;=w~56\c_?ov9X< jwaCt^:x\/ծھiz)"mux['=5l yqѴg-`h:IpMC!a쟿uĽES=r&"wND2N(_5pvn$&Wϭ X4*3BX,B-Q&tz3&m[&xɾdZ^pZ4_;졞7s>|ᅠzO[av^D*=J %qWj,T~p=i #$Bu/s7x־R1ZV=GTޒ)rPw8a oXV] aW;#ڞ7@J&R kɮV{Y C]IM\`t;=*DϡF793I{:V|oQ7-=-eN 9I ࢲet` e&oDJ7neK[bK8h!(q.wqjkE j੽ZմN*/%PX ۽g["Pб-|Ɨz'iU>E6LYx̉{Ӫ:Ǐ徬?^AL,m*;djm p:t !)ϔ|(j 4lsS`AbWvqaFVx+RnlV^N(-N!WO-, aeqQ:`4ka&Ji~+D#uCt p+6RD0`ܑ2mILŠ/bJ Ql5P{Բ^!ViŬB' l8AqL\fy*-ʢ%t ?0`oG̀dEĞ%/:@4\>'޸9i$aLE;WAtW gca"Fv=xo,S$`2Yժ򪛠9`1iStL%1+p\%3oaqU)ݯ!G{~?I1`ZԴah2I/3Z˗rr7oqtE'z}u @~]Nt9Y,. L&ʃYj3߁t8ng#eą(~$!I!:y,Wc Z#n,:W~+vQ,aTG;5OWM.tR7R'yq. t&Te{sSEe \F?e|ysGK#Wg211O'x t)L42rM0S042=,j$UmEy& oրA:Cb,d^i:d4W{œЭ?I<|yAg!$*#DƜIOcOe0N'(ʜ.Xx O6էOb4%nB)Q43Gg"8Hb8>a0Gƫϣ {ʚݪdG(n^I>ɢRl6ٕ×~^U 6筎%m :yX\MN6=6zLB =ѐ^4d.^/80#LtM/jOJir璛[*RDD +MhΛ* Q6'?&]2ZH`%SQ2!sF:JфNj=F#uk.QҨH B%̀zuSLFY}GG[Ċ]CDi D/Τ''-%2^0:y LXSZTg6|ݠ kKQSJu6J*s>Qй' Xu:-'t[d+:Rܷ'QzɎl~J=u.3_Denz&P1빌 ș<-`icw||=ؚp=܇HWnes6a{ y;#1I/lKC4f 0W [߭AM[& >mG${knǴ'[lJDEͧ `)# |qS A?L>lnX@X„rg{2y` nyk{6,=F =:OY%'4L'#KN=iB7zH10"{G$Bmu'ж  7= 0~zw!y<[㊝݊ 9jM :fl3cӗ!#[25j^!FejmdW>Yc+j1z)-3mR)DҶ0DoqʭA":$1p&]Og|e* $ڼzR"DJ_ jI%z0w j8'*?k[p)mY˛^|>Sܰ%v)(w&y chSǩ(F"#+woHp 6` 9[|꘺+b|?INFIlpeW䪽9q; zk4m1C+u5>K(NC8EE  %o \lmuQ4M |# :@U@Xۆ붎Bx\V'qsLy&y<`b[8]*|"m TcE.ϷII:z ˔@Rx=f#lc[c[z!{<ԉX:\BG۔ltEA='sܜLFS*F3z~K_&ϾQsFt<|Z} Ӈ{O?R0fa8Cޛ'wDtt?p7U>pd힎{oH1wK};(|@Rߍ2R%Y>OYf+7m"{Gg zH2Wlf9rÒ qfF70F-sKΧ7`g(^akX %OGW!b>wjqm\p}4'2 M: Q^C%h6m`>oC?M}mzu~Y5bZ}#76!V:kw3{ԂXk%cU[uvWy0?hN-YB̷{\id^nwՂ Yaؤkf߮0K6n'AxA}N[oׁؒ>tx+T)kqt &4im/*% {M724޿aǜI&dg`L)j0⦸k.ԛ=]}POKyy`;[c {qL۹.N|GG%1qA/M4IW].[ۂaPlAFLUu cnP DS%8_f󇻫ѵW}ċ/Y%Qf}IΓ']|~aT;fd'no! 1hLgsRdkܯ0 Ď X% ]J#n$~LGno2 N0]"O쯓&  ZFXJ ,3_ԝ hĤIB 3i/y5{5/MLZR,qU >F=)-zdֵ"pLl`:9Q>{sS?>w=tCGկf|}vMj 7IHt#I #Ma,>‘~,t|sWS#L'U<]`5vSyYc/Φ_ʱHsD Wvi=1 ɘ5GY糔m2fj$)fԥjV9;X䓊:lN|,"Qn6Vn`o-8xDbVp{ee&M=WF}+D/y 4vŋ 9 Qr| {!4 |g3SӜ2 K*B56F!T_m7y=R2L tHQl(sg rXIσ!l:;cr`g6s;2“1ٷIA2lM[SA (c4f9::hMuF_ߏ}|Hj0M[E^L ̿jtY0fKgNHSUTTr-t G@;(df"4Ӝ`TΑ_E&$c\&P4`h}mԒ`[![CoU>\$Xj 12^~5DRH#=I1R0:݇Ab@_Z4˘lߖ*D)Ѵ;;tp '\F8n d.gЃk,c)1>MDz*!z"4vAdp]hvQͻJ+xi32F\S?.*KӚО#ѓGG?.eg|=(;p+&O[svTj2}-\;OZurx/70~ZDѺ; &cndYFPNCC 6׳m[n<@$!XuP>"`O3:d/W'A-|aK>M qtKvtꇩ9zZE JNyJTb/$>5lObAwYj|Tg |,^6K0um^6G~쾓 )p]z6t{`6^(@z8nzw2k`wp"b`<,Hg~{Λj"nh)be&/ʲXP̉2=GzYs&y8G7cXWD)2aS7t[<-Sbkei^Ax%j/Vd"[yHg&@\*zfEkNj!v!bb$ kY2Y2OLSTXM%:`:=au;c{ rQ\ ˙ol3yH1SᤴۇGPa|9~0)P[;쑊,j|=ͣ/<^6Yvٞ2xHyGcACF;)HۈjYjf52AFy )e&*b}S뀗ܳg7-J| $ BFq=kYS9fẏX[J)I.# cr0ic!vpY)w h<4%IPAlZ.|*3_kP@Di:i ?=mYmdW!Aڡ~!t~b1~8w+>UA+OU-d4q5 >ȏt>ћ.coqإ\Q WȱdLGQ ]g{g6?o|h?p4:[PGA4I@hjΤ ۰m?=x́zwk5'CtMK/ UcsZ?^f]< p!eߧhEF;'\EQiOIn.&SވAe0p|S!}3>Vm]IE|Jŀ=Y6} \T[|Oa&&<>-ryp?y&nv]n;p+p:Z |>^vqS_cP~4AH}! [PYm2"BYy+Eq%Hڍimڸ6 wAy5JlsSWr%Nn'vm d~c75a0q JYOU5Ş8B+[m,9>Qx[ 18-:K"A, cjJ:86=7|ָ,xÝM;4M 6\C|*[+ job5ine 0w+bVce!>XV 7Q+ zu-4 ;#f|Gxn2 s;7XB<6O%nw3Vf[9T;\np;%FগW.~:Ͽ=я3W\O)1e8tNMEO룤_#pb&I++-N>$yLXU%rQVYEJt3fKɦH*ZQGQxaac`$(f`{#Da-N k`x{ 9]'?nң^&gVwK%,#83"`kZ(BʼW0h@C{$O7tKJtj>Qyxꀞa7ͻNDmHXQ?mGhjYHPG[ F.PxmR!h5v`Q" ^ި=:] 8T_,m4 Qa *עpwd*`I?QamV֨>_Ŏ1$4⍏D'*F̎E;K[{Pk\mA{r"h=5K=ueͪS bh꘡Qg@n uxZ/;2DM@ `Sl,EEpGeeqW2{q+(`i21s#/̀[*TWFOy8¹i_ I<,ϩ[0> t43Ku8c%W@ŵ M!*RG%kcI_njPi1EH^A-Zi; hjviXx Y $n>/lSu徨I- iYq#QU,O,->^vib5ګs0v,R38辽"8iĩfUW:GM}&ȼݸh_JYI38I@!3bL\c(y?)yI_+)Wh4Czs1@b6:0 &R$"6TtVju(nD]t`DPz 6OLyZ*I_!DU>w3--^ dV"Dmd{&Q)L̹Dq<H?eڜ[Au4is?9jz?faw-S^˲tO2XXCv]Gzћa@\"9Nx dO +~1ݏaʔ AIư!LgN g:VX8A<,saIz ]bkTYytӬv}e-̵ &뜲>[Ky"WyetW}UO͗ Բ eK,q|o:S #*&t5/ȶ3e;}`FlF|+>0&`OWW|TOU&9) 9gq]^?QejN$_^\TҤq%ę)kbS7)PyhK}M%< % 66~X\XHu^F/ NL! amBJ҂ {TKJ:lD biP oǑ0@>J|eb$0-Z?"ݽՏdV>F)q5/&]1{ꅱvydͦ .:#F~BoFDlnч;,Eagڕ+8-ܱnd5?rL }*s~ˋ34ho"a!Ms? ʧpzyݚ|,m2Aב :x1bzyR| C@fm5ƫRGzYIx$Ō)H3E#'7Or~oD!nKSKTA:~J x z;)WE:}}3 zYp˼^hqP4r:/t{+?Hh9Ϧ_.OJKgؖ:R'l=0]ue ;v~V-וϭ.6(EF)l6 $3Da lFPEelp6WG^ TI+/W5FQL(d>J<݋Qhſ?Һ|Ӈ<~AZPĠ/.m#O/;K0PC®='zNa,2 5Qr%8#|*X <~aY[,T7偳"+)PI0 Yg3\~᥍VG! \LaN%qZt=yT ^n O)G7oCz2G/tBBy۵60{n( 'P̄fS@$O-% 9_M#$c1~N9E[byx,3%\m Su3%bed ĠF|b r_+$)tRI7ܣ4y%q/|t~,#|vsrI#;L!dZ p"6F|5>As?|*8+R#NԪ8Z$6ѝ` n[КTf0˱@87ÉL6Hm{p9:]ү!)OHit?M܊:o(<c BhțfqMw^m}8U/n;F7yE"@gg)Bz`5zc_EbEܩ}ߞI܋nq d&CC-=E"rԱ:[1$H,M&{ (A_,^$k9M[dn!v#.(<_v )98DvOR\Mi*Ep{A~\()A$!. D]"Jlkҙ. '2Y</@Ĵup.ƙ<&Ů8bΡ\tu[`/c6_/ݣF0|lhŸF >F3!v!(@3q4{K>~  )͖m) (mV U*zDQ[DF 7CƞV?I2i)Qx,, jO/I]}^IyYB/b4{./[6kƢs.DUWCҥ=ХMUWO0tFyT 3S׌ẍJJ%BPx} E# J7}b]{'5U[CpKDiA*"!0>w#o Q)yWCGoғ$ ʀ{} ]AB$Ȑtd5UݯGF#mAԋ=)Ud ,؀zӾ10]5sNn4,ѳ۫t^1,!i/atӊl_Z5Ytts[[D*Otrh%Dk4O[/m2n%q/az3K_7cZ %aJY(б. ;EL~{*vz#u襰v.dww 4tt_&(Q{h+^8Y%KkӦ!uvn{yҰp\/kc;-tˌ Rrs#YRmb F[:kGWʂm8V Ypj :/!c{HaBB1! ,<)OA0qo?& ܶ/J`!A09"ˏپ\'|Ti,KdlNbMDgJ]-d|)B&E}xiT{zA_14c.F-yI9D|9N$L 2g^ rYܰZqJ=MLǂf݁ /QzGW5|;)q;(DdRd*Zؠp)9CnCT!RL jdDWnmJ75B8%>^R37_.x}-ZXwKnF(At8O{ǤՋn)_\~$)Ŀ$ߗmyqS~qÇ-Շ(˗O:ƀ{?>S&EΛ:(}YO?|I@P<DE6ɫ\$q%@$)?E/M~mn~r)zZ |J/1pT2eRˤ.o~|]㮅 zˤE/ @E/-zmn^v{ǤI)-SO[b+!|i#8{uۑmk(^}iԋ~Q0wgz`) PG?'/yѾW[CіOگ[T(r7NfKtF]YŦ9Ob#6vִKdeF4)E>$"Wh2>J>| c=اKFX:J_j$lcNXwr@ 1a48#@3lo@=ԍѠk@ɦIqShK)Z6繦l2{V_  ]ɌSQN?>l fU;ɟz=qģGF_ K&}ؕ8h~0tzHfuٔ }*E-E|6fP,Avg!o'—/2G>7T]֋ v|v =E) ;N|!(@VfEN}[H}1G8C¾"ϻb!bEw__|#}Ux7gzU[{ bϦl{6!(B r[#zcM0>Wv-E]o?^1I٢Y#4iT oڅ :j lO|4eqEj5_yʼ¿i8=⫓.Ο}Tz!ݴۋܠƿyF%]fG,rmĝ!v WP)<@ْbmʪXޤr(S.:%I:xɉ GrUUL{DVռidA0nl"l ,o85i 6bE/bAcQ.>FWZ1s٢2|t )u`}6A?v8fݷ|t{?%Fp ǦsM n"es|̖ TrdrÆ.)D0qz<lj #PfG,r@BOKմZW$[w9f4#gGS<"$oYjqqQC℥r]N8JCFĸ/JK S =\¾~LO ԓ Q#ōEE[G}sr.We rwRBCp WFܲ>ŧEa/U9b[V-N#JvQu5AraM7eTl`<va*4TMxunЭHѝmseu7:b'Si@& #Oi)NIo bGWɃClMO`'t8 mЖz=. w! fzU?9Bscɋv*c{ IHSiAIDZ.bs $ؚ`UVBU/;9f!p`Q]eć\?KJw/~Y!,xh6\Ip4|@ܴ,I qtr`f0FGv3~v<??iV(kyXYug.'v]o<'ȯpE\f8CNhcteFgb)ZHVr pe1Los1E"X2ǿf{;![q6Jx[lͨRHfv yh{)vOŧփcݟcx\4qٸ-a= g-ɍC&s{CԹ`m>Յo>ևnpBjx_!5K±uf݋Dx5_U'8wk2bN;ʣr(vm%¶Za \^V!}!2˳Kqj<_fˢ2rZ5@C\\-y_q[&~fSuݐuma~8 |gS2iV?1׬躧KE򛣻 1c"KWLfcd@$%oufC$|g2ol:-pHPV$X&y+<UeyNڶ0^VE:\2h)c*0 C$ml{ABey*8m7)My֥U/aldE{߁@!دmQGrXQ^﬜㳸9ww'۴Xu۠5r|#tް9p5.)-]T6V/!B.24 Y~5;p8ߍg="#ǎ[+Hb=I<6>d;wW&EM onwaw_C~Q×Xi yd6FٔBipM&Ӣ8y6,B^UTRwDog6|}.#nޠ%."-7"D7(bٽWl(9 6KU.F6 +bqv;}A{]q8wakj|p7e_3KHW3B=)>`[&M(aUȠ*s~pHy~ }Ö7[e:FIM/Kk6~cסڱMbL7FP.q.?a$Dmxxҗq}!kVME8Orkb/k<TG 7cʰ;h1lG1c, /{Q&k. hBbX·kZISMBy%IkJE!F՟ -(khnAE88tl`osgq3i ~{qгi~u;׎b7{ 1gLl2 :7g;bPĚA@8qÅ}mWcވE΢'oib)57ixMjhW";%*o%RNB.˚k*8ݮp-nɷǣegTQ Kΰ x@q%%"n0E뗏ŭiDCrz珖6{9KUQxS) ۢm~SmuM3 R wkKyɎ)ٛg:2&1v)#NecSܰgAV&$&Ji>!7O@aS;ܴyDtT@dRfYSpҫBj2O! '1@H>r6dY<'Kf.HEc$dBݖ9ZяP)*Zڞ#;VuNJd}hHSxGZ~ r .AVI@)8O>܆b CQ_ъ$8Jy#avI|>nц]v`ƄЬöP)^@RT8n}EWcW,х؝Q}0{0<%9w~;Luȓ<gg\C:d܎f(%JQo$T`}n냛`8K 'x{{F/utwu?_o~xBR^љǐ{FS4+Bό_²JǘkM1;BW;|\LJpREoýkǣa O[Hfe,4&Ʉ0&/׸j,?58s  o8F|ќh[rm8>\/Jϻwp-UHFe!)Ӡh^c8UHV~=S /M˭!kc%t,Xu$fltD ܷ,И(>Rr*Gn1Y]#K!gyu& G7*Z&z=53⼞/ 2N>͚i΄Gbp` DYX'x2jf"I]?Xk e3<C'a/G5),e{:)]5vX<-֪TDڿ7yKu_݈;̧>v5J?L|p3v﮼ZGZCЫ8Zaɥ08:0w8Z\󏺏'$b_lM ;r'=I{h8oX6./g{DPɺ`.g70IWCD-WaXg,oLr)E63 $s#Q0=Bα$L5r~I@$ }l\{Đ 534륜*EhKfe(OnKgsY 11 .%ɑa*^U l^pH+0k*iY[>ݼpK=}RUUvFUJރr7_݅ƯUc3^%ԑfk[׏}0q(mfEfe|!aQtOxE2J؍Ǯ,7m6Ĉ;HV$D}ZDu~5VOZO2R cW֋3WI1f[ &32Qxr'5 I#ͳ+ 4(q/D[S?ɘQ}73hBCM> sԿ!{Kk`If:jOu|EޡoϪVyd 8]! =3jY]bJr]l=.,2uߓrj2#ZzV#t@_u"܎CC1hqj̊|_8I`Pc`c5 6?Ci=B|șbٲ>.Ⱦ<9+Oh YMKZ%+Ax)5.Z2y+e>LJ مI0* yI:(Gi2aDǽ<(Vi?Bu` 1c#;I!'Cb̍xS4Y:~ {j6sDsAZ5Z')ND, E6ا$.51~F x}*!,Lf@xO~Ifbo`~hϳ;z7uĐ#<;6aF[GC3p/xR4>V&D% i| 숊 2Gwt]xynC~V8@-/]R\#HZ wa  Wu]ڮJ"BipTDV@]J O~Ƞt^qY B;AbgrJooXZ] =v1Jy_w:ݷ/ɘB5HtLWؕ{_8اnMfn\KcŸY+teî^퇁:B3M%HkU,4'Ldcy?.qzD,P *ȧPI>Ú|&/h P4M%?QV!M::-ۭ4֟I!7\?Dv9~xKy|S?clG}}?Oe)A?mh;K2W $6j7lA]>UGD#9éc^BCd`6oƳ.&x'$-Gxu TjK]IZ=B@CHFMuӾ*inbqˮK毰ɰi2EYĶ7"qx kNMń &%mK#"ImO0Ho 1?N1NEBLX ,40O8рWSoqGܑ$"^8B>2/fy0t1 *ܣj낳QZ1Xz0,i'+RM@oT}M.hS ۵!!bW#GDLrɨC&F Mǟ i!ߑzchS+:-jcS, $[!dͳ6& R8'zeB ?iG[M3ėM܈Cԩ*$hm5iI~JJT3t6/#`@! LnOŷsq+WhS$T<:5[Ϫދ"m>^>nz~]$ۍ[uh'+co @yEC@gmcL"r:a&8&}($b xGKQCM77,anF[ )X6#P[7$;$OsYBХfkesЋ[FC:?fR1:p,%II& Hy ܷJfcr'"e1V$M #:9**KrQ9n]#2жaiFۄpBєƾP۷=sFކ".\gU~CP&/1c;̂hŦX@ܥ}, w<)@_xq_t-obBb33&UV`o~IՈle^~52:&hKiii荻,m]qǦɵKq iWA#&PV#UjņI(Cќ[{h}q /DےZW@N:DR`ŕeI9kiCLYukW>7 >[3 'S//gг5I|eĀgD og}eW#I#$ma/ \bs>bplΎ$G^f@ .NXy*2Q½/yK#ȒzQC(0f譶5¤hI]&e~sM_'()"#ZnS'|E%{^ ))\b ,{ܔdџ>Rzh< N.EK6frȌboaL?l'VI>߰ Z"Tk5HOn,Goرa'w8~~\`/GM/n~ZDM=0%kJPl+9\2m[䉺Tk旉RV +O21BEhU8zŏVOe((ǘSG-4 U`E-#t2[.xBt #Zp=R4W'v$KA7e,9'qef'C]JLD>K0[$a0_BДHGD#Hh`$3M0ΥgpC`#faO3(a¶L=a-@ʟootN>?Gavӳ|>on|lB p^"}q1*ѓќ{l# oK2 jbun&01fB*, 5r&bڭC;PB'ڕ]$7`ҔQM[2 j #e07fL}BW p`%q7o+b_%q3L$Hs^?л4WSʊM̾_4UOUoU,JUCZ)F]9c*bzTY?Ϥ+J5t)[oIH1)DhC8R}b,E4[ۊ\Lzrzr-my4X@NJfCJ[ 6k(|SzQ9؋uuCr%]ƸkQغ5(2rDq$W Ɛ)u͕JDSOswTiOFJ Ao m9l$F(!= RFx=FQvW^JK"S9ǴǫiWKi(6Gː@w]0eޫV5"5"HBGKNJ#&'4G0f<4ŵnIC L1feϦI)wH/qĢJBL6E(Gyê$t3ȟKq:[MbT] zƆ#wEUzER0.l3&ɢ(б#5ZHȧ*f>5bfظ@:,b»%IG @7a! B9{ŰuZ.ŧ{$C3tI;L >i45^m-KAܭղtE>{~.&ם%=l9.bXsJ5KήyBEeH =#B{w)~; G'n>|v0 OG?dG?Β=/=6+<Ƶ99r=vc}D;&C^ߣ%wD$u zK.!I.1 0@e7F}"A po3Y8]p:X*LjCeNLU:4s,ldػ}'!¹o+Nږy|L `m!r L?)jb8 *ٖ&1M랜DõG~7w/Q4z^:%ڗ~v^Z4K!cgVAkb}CEUXS7ws΅X^B=8NNw gSwQ]O# E;+y&\wFXJWu0N!?$Q- Rvd%t)3mn0:n_0I.O-ouCÏwQj_u#J`j2?B<0dQhOƄqfOa?E'Y5Dֻ⍚21αf &ޣVYci-JK,W$׫7Cov |כKU8Gī %Bbv'u Oe1(MNŎM#ʣRʍ$^y J꫚6gP'B(yb ]iM:dҼ :}kLHɪx.9BM?5NVP"e*]n|pW;rޫCYјۯL%6H{IFy2[^lOޓ2k`~,W}|IjewȸrCy?!ϩ8zie;T?>\4)TBg~6 #|Ypra~u7WBKЛ LZQc]OY#G.)~W}.vuco(:t eYԞDvjGY5N3Ay6(Gl ,۪Xw6@w8#do& Q&^ KvHleNNN#0cK &>0,v?LB~#ɔ+Qy{b?y\t B?[i%ȿOe;nYi'*ޠYoe{q#x ߶ eg8Rn˃gNYx-KۜhWۥ<9p%<vb Orrqhag Ɉ1# R)Po׸׬LV̅챗E.; FA63IO{W*8D`O޴6q/6w jmB~&Ued3%D{%{%X՛p\z)W˪Iʜ`V*u !6s\e67 ى[a![UYJt\o0""QC ?#ѧrI|3B9Y< ByO}\( QK=MJ{!OhHn ,$pF oIt[S QiOBKd>J͛/1lOjO ׻˃~4CMY520)#%) .1!h|߷>wnnJXpE/Ò3Fmys".T'bFYN)NÝ #3M>=tvmc3nKnVʮ'a$v7{0h}ts?z`z(D77_?ٯLq דS9nP%aLAIj| t3DcI8Vp.T*9iv@gj̷pW VVbVRP =BL` F]U!:OBj \G+t醞)jKx2 2 CkRBC>i͖ n)S:{ X/uZ}́wjYA܇dvq|JeBJnE:$˒]z`kiI?PL[sU"?Q$`mNqcyB,U{LcBQu,\C,B)cJǪ)Z&#Gp]TC3gP̨9cevP,.D3#OVFn:'ϻX|5+QpƯOU HgIKt}UliǓ$|Xm81@{4H 3jOjC}[. гZﳲ⛙5>Cdtt|lM\ˮ*Ǘ7XqD KJa>(]/DJ%tT2/@҉\ӆ-\g=F' K:ic Qa2&x 'g=Kpe  ׉Ih}:2zn.'+>uyOnW\ sC 7op'n'-ɧZz8hz7Iѿ(U}Ls4O!ST>FgJ^$i^!!Yչ0E)g-S@|Gmdo#%+Cp'S~Q7sC*-eKdG/XR~fEЃYEI mVfR&i^n׮OQOZ=f3l;3 h. }^Y:iwng0E6[9W="YżD xܾ,xuf! 쑌2uSA4RGxvny\5'ؼ4MIJgb? ǹ$QTda4B֙Ѹh2>5ʖF| }G#*qR*Tok%Jv|"i?iIkN!%30<&\*.ayu{BX*0On1lm~l',ނ~)q (d|Hb\4 w[}oX+pЭo4:t)XOWD4=Y~tTu1 |:Xwuvb(v|vJ`;YR)uu_wqHhYN.gח R%r3ɯof8䓻_<'Û` Df]@p'/ąDB%.L \ȽDVyZ,0>Ex8?Ni~H P$Ж+oo;ך)M- @8S ʣBNkHa.hP{.-IoU x>Bh_!FWnGWq{ǀ.x8](g|7ٟl4ϯnn<(I2vٖ,Own{I~QDQ>y[Gl Ui< ^2s2t;g5u9_R_We:ݻG%o6}k`|s SE {r w,2 #_T(J05T0ݍFW3a8f u5rp 7jhaB gc0sew\ӯ U5[ВAk 6{3]vٔ5vVazU:&Vw=׬rS.3- y1!29M"2}1 Sw 4x482vN"-#Fև$(ǜ ݷQ@wN%k{>pp?z>})AG@a]${Mxpw8ypOs?ە Qd8|Nmu~~gܖ{Y<žmhLn7X4̉]lqZp!RdKt0^ +6$ZyCY'E|Y #%V6PU ~S1'.~*7e$\[~[ E/L4,vd/s,deT}+'[ɰ~qe~*{@͋[: ^-V=TQK99 >?gE>e_ ǩCJex;8P_݊yVzRVv$i`^t?n1IK9ꑯA=Ab-Ǔ+j@"Z0| @- bm+Ԑ ^"7٢Wwv'x;͗Ţza]t{maq_x!R+v4|la+7o=oBUbIjx3\Bl"AB:Z&"S^AF\AK!.MS m;A~9uOBy v-|k cIaJ^$ -'m!K"x47w1l~iΚ2-_FsI si[SG8J׋*enmP ⽣!rx9ڀlO dۋ][S%ݞC&] tE7 @t.F{F׫c~Y-0uXk4 R4yѰ0)ǐ$TQFvi>uM~Erso\0ߞq5>`'Oa0\-HPq`a{ Gގ>bBFӘ@,6q]љO(tKnx/#1g)7*q~UK=B5J A8kip#ik4f1M)ե'{),ЋJ=׬~:~@" JǢӴ)I;%PZ;I jms&G_{:md]?*z;Mȹ0ؙ=fŽj R}bݑ52$<(-Y8j1cJnX|[N&vXCI+s(VM슎=V'rFM#zn"W]2`FgOQm?B+zbެ˼>s=Pqك3-f4y$qLRSdK+nfߤJ+ -Ha 1յ8inx]Խ9-j1TU'$(4rKZi_QnB-] 肆htyHΓz~v7"xq@Hm?eaO'ɜ^vԸx6X-wzʼnYqW+÷ K̻%'գn |hqc^iW8GMZo9.HG{tSıi@.SŜ^1id?$SEy#H3|+ V9ަQ9Q29aq%Jls[ k켍XPc,*gM^xҳ0bRB$PdN\>o\/,k5XC =&"JSGly4fWr=h{ Lٽ^Gg+}~zއQ63W[}=n`v˛9)(:a!o0mpz}簊K=nUvG )HG3T>:M˯K /֯IېVD4"ɬ,n./Jw=r= yJ`man|=V%劜FTrr ӄ,U`Gm MpH?D uXc iLVcNUn9Ym35{Fؾb,(\ u&GqgkZ0Ai2Q{{B".ɚ2HjE!E sxU=SGu@h t;T 8VNP*,[>㺗kLJ !|3ЫKX`u Pj-$?n˳`lK;KgԆs3%7P3엯NnhiݯZ.rK󄙄CG{PF@.tL֓ ]} 5:WWNH.Yx$㾃2t‡'<K1w ݈U}4rl?fM 1SŢɒ% ,~@.T?-g~C7Qա&1ɷ\15I=[4 2:nU: owhc6ze(!g WȾ p~4+ ᲢK#]n)g׼]cU"NTŁPCK,jrizz"t!QiKYeTR}`Ns}Y>)&H!4[Na*䱨chMQ(Z|Exy-}npNV'NBr!*FyVӚF^@.w}=T-͊zSxA)xkIW$0HPCgijs/ nZm#f5j,c>x#_Fp(ξ~,7dSlA,xZt/ RVb\wg! S'#?~* 3 UEKP7;=R>>bG>FBZVG""xeazLo_k% y|үe fSLZE}¡Y u̗JRhS~ -j$2W Ѭ:6Pt*' AŦ@uI* dhl"CҪ1IdySHڪ*K/M{#VUx G8H :*7ܦjfzˎ$ ۷/,0i5j\D|v5<\d %˜8F 2X"4sjhy"Eq >N[Q9܉'=EĂ I , *5b'k9 8K`P{c=餭LS\b iGʗI D??dd``itIfUbSh X7+ߺpZc(:RsxFԘw=8[j>R{3J]mT.4[i}A{cGr[ZL$.scwe[.L2mT("PQ"µ i=||x[6`c~.^/ER@>`}uȂI.T4P;{h /vﱗ[\v`5kY׶SBHCySb_4(OzeH|7/_yy~*`t&n~kF7XVtY+lrsg%B7_,E,aq"zDp[.X˿XLJ&\6{)Wg/K :6RvuQ@Gm~a:>}1t7T߄Th>eJ3b]+.SZEa۟~/lIm8'49nN޿Wht%/XƏ nq^!*#9o.q]]LHg/8Q 7NJLj!PR~&A0J L||c5Q-'fѲE1 |i̭)-f"~Ut$7|MUÌz^@FNp z0!c+M8%ɮ#@#t+c59]4R<2`$>(v 3ؑ+7nMJ8Um)0fP.NOxZnoQIz4߀9xd׶$Lj)lT7&e9MXLZjHmoUXiX{RSkn;OP Wȍ+p>JN-{{od~ta@"Dfn+󀘻{5g3xt#Cg |e3跇]w&n_uv~WV4~2+WOTZ{FI?g;g|-~sK#Hk4DAqQbf&W@<"Q|",2s>bZRqxL/}8miW಼ G9N^Bׅ+ /&x׻^VH' =Hq,%N'bmptl:ʮ'JٔҢEO_ /t(|Κe(ҵGӾ}ѐ <]&Iz\`,qg#*4vVӽ&=FI1nzMucXFcjB#>VWQc*-q=`]{ 7~*\N8 +Dz DFLe&Xwڹi&+Xd:rW^1,WDUOM/[rRڊ@䁂 262ӺA31^fU1aôentr4ӏ^c.Y^9EQYCŀ$zHtJMya˭SEVesKeV0M/Julv9,xgmݥ!J,[ΤspBbvQ*Q>6S9H#%k@FUJ#ɊNCtnXbFCY> !d˺fVl,lT{)z MAs`ڈC<تx,;P ̈́%y7CjaP|OOZ))ʬG#-C-ҳ2G 3f za1Fa$]-)6f]?dE\)Ol.9j`>3}zCVY99^`frΔ4 8d&W\jx ? `R9RxU9?s;H3 4͏+(X\NƪPMT)J7JjևC2#_xV~)|ք,\>Jk„/UP?Xk#,e/ oGW賰dX{4~o:8JHſ (}Yr-ėz3aF(܉yH_&ds(\mpZL|Fs{9.aFwb縺|O"TĠ%.G{ZvK(CjsD Ѿ h#s`[$|M6E{۲]b FegCcl6{H_AEh:QZyQ$ז}\@|2QjKYB/13O|>Pf斳#| a>$ s~}qԓ-zΐ>,}JI=d9\'.zJ:Sz2g88 gӖ粝b3SjORjo$TOoBf#˖zG,o.D0邗?!r;ȹ8pe$Ȯr%~0d. vv"4[1\D B~û-..\ ^v )<o)I18 f&/ v @۞x-,p}sz̥WΓ\DžZm=a-1@*Bk)5׸\~||QuMJh ƨOXNcUt<魃V"KRNЊW "9$`j !=:Opa,͓9Eo/=r-?eqjpRpW~=0B7L#xMpC=21=&J-ֵ&3&)ra׮0SvM|$ x_L6j#jS{ q Fzcn/-ALeIq)?}})QMьR.S"T aIjzCxOރns|WEe;R$JǑphzA+rPF]ՂW$]ߝs`JޅGL">jv&9/ ^ Žq>E^:|blⳍ}HȵYju,!(WchonVCqJeqB;%ħ'GaG sW(AD&bm:.1yA'@{XԿ$Gj#?N s!ӆg4*K+hsP;yD喳xvuqEgkټT塋օ˳4p~4sߔTT:5m+=עCtr]υ#cgJö4ɰ@9TvvL:')V5r"rͷT,*Ng_$%KZ裝]ΕNII9~mG@M7F4X!MI2 w:gF:{%,w[Εx};$ Y|߾ÚŮot%;q3ˇsy6}v=XOgߌqZ}MM`{7!3NIϾ2uSL![Fûv;v3_qʙ|h0<ͳibٟ_] gI||n^@ַHI񡛘O!o ID,M\Ρ kVrla>" uUsRu+mƅ= G;N)GZU=a} N ~)J ,xMӉϟȲ"ĕDTor+0kyfri].\z_>c.~s7|g縭/{rަ|J`#=E_ֻg7`u؞QgOtE\rqV (VIgIN؇ḩ $%N6^NN73`Gb255N4mhc?rkGƵ'z]USe~ [ P}cn1Vh袂azUK5 `BAzvGƕp '櫪KJA׋9?tyk[icF+B+qjP=3mm!=LcP!c| Zᝒwժܼ!KA YjSoCwb, I;⨯U..Q[N“CJyQNt 'SEG2njE[,X9^ۆj/i!81n;T֯^N/x'R}Z;h8bvaPߓ+Kz:0|}MݻĖt:1߃{~?DD̴(O %qē WOZJ4k`N|h_, ycM)Px5qn`꠸kV C7qNgTP1؎t#>p<>xcZ_(kGNڒ@8dKokbM {QH|{o5|-T1蕉{>\V"u]j &o*ZEnu}>[" 6ߎV~䶉E;q K U틟-܃%xcԟ"U(ÑX8q!aIit1=3[(ȸ̢uDhz6EJS`fwFs]?GSW&w̦ͧo~ED g>n~!NFw22/'9F_KKK%M-sܝ+7ӜS7Ŵ4s{ 5 մ[,)z4|{ht?ܞɇ} |?dAL y»D*{Э=েR;7-;ઃm:yrښ)jhL5/RG0)BGIX6"ojIbl-n6bYlIN$q5rQ=O1PʞK{a4JT|6F_*{,JcyP1B<n>j61rfۗ') }_ԣ O!;wPR'g k.۴koVnDSRCo VrYۓ֍dԜbZ[y;Wz١݃wթYfuwƼCI)HdE+u Mr_4olZ +7cF<&E1D_S;m##5=))w?35Oxfv/N}Y(J\5 !Ms$!ɅMjGu \hR%G{Z,. ف%I(> I :6O(ɶ0-ZڨP-RJBXLFY2rg܄Je~v9 |ưV \>-E*W#\JЍ8]=u 4[9FRUvt!/5$z%`xsn,*R"qLEf]Ve]>*&u71@$svqrWgb$,NSB#htRQf[J& Om؁])]*{!X^>^ &F "rokzT54>u\? =:5P_&"^SnYSM4]>Vt 4QbͯbW.x;vANooofFɴ Ï]>,VN2۷-)c-]ݨ_ RlQS^L2+(ܴKd\D| ]*u*p7I4+ٷ)$1;)=pذm˾Jvʆ&A#E܍KWw)fkl69ܬKp!IS@hyGJա]gv{MԎX)/I^5 4"GP̵1lFx'`SD#ߢ&ܯ횪 TzG}W涿!z7yV27;RBz^8 o_k_lp2* ʖ{6z2:m&wY :i +L:„?VJ+Ƴou%'8yM &Ck&E\|?DMbVHA9ևȊJr=/ʛ|Kj^eT&#˲Glp$y ׈h7?-ESp:zp9ϐ|v`QPZ=(hF.&J QKO'i7ax\)Z.ebZ:7^[kaԤ)Ap²|0:+cn9Vӗ#2VAfj|"ڧqY9\4qDbiSq9YYRdnRn;יYչL;QwwH|OQcb:p^2oDL*7}b Y%%:LghJ%D]ᙤb+#EaEaxbY5oI~p:D1^.B)wIgr+TTXNb(+>}Nx "F*Yvtq*owp-mUJ{Wg+8:q3+)OtW`$ C\z[6bYːcQ>Q{cR|7 %-7p𘍧~KRk,79l} ̸i]?F %``_Vg7A-Ͽ5mXe5L502/2 t<@ƫ>FWšKd 'TMuL'N VBQsm??C`IhD ZȏlqGo]=pCuP>'d=M,16<*4 4Ǖ9$h*X*[dgHĪy3tI(UͫH-Qk:J;|CUHݿi}"oB^t)lOT.fư*F9߬aשx݄zd8 |:yٴ|0_M?FS\ L sGYa\MAaSo)v ̭;\K1.E^ `1 ps??פ숑]4B;3i/ӤZƧDO(6kW>JÎ !훐8}Edn uYpRS}˱b)YSA3xEDcޑ?ؗu\;QŔs d/?XeL, !/ْoP5B+qYWH9>F&G֏s[ӿ,6{\ܡ<.ۜĻ@DN K &r2[4Köy\88L JWcOtGR\Ow^ H&:E/˽t-ڈZ7a&#`19̐BliSqb"n7Aޞci+Gڊr Hjt=x|/.>`?{?>t<|OM+'t=U류trфsiR ی5}le8v@# t8mQfc(L@l^QXh0-&vRc[ b!g:BQ:$ݔ|Ƭ2_3M8q:ro@D72qwm7֡ KˇOn,>&x, nE.]NR*͐nP)8 J~fG2Xb<"lV]I^W~.c0Q/h^6Kq 1iswvېU- -GT®%Z_2[!:8 繍+u#7)I]ki RKFȪ p!BM@B d TWw5} S8.vťiPee3vm|cc>/1vF:Cш)/r$P4ώГnֵ[jBz#2Y@EfNM}ZxNKJv),/'$Mrc~nD-~z/Z6mrgHV+c&zy %Fpo}ph`W>w{`7}|eniz\ſC ϱq1!}m`c鞸`%TÈq;^%}b)񭓎 ji1L.Sчqzf{yBIޟG7yZH!p𾌓Ҋ-sHdxJdT ue[`STM "J\;?z cs?gPLS,%UFC`s/3M&]I]9q-U$+Ժa1#X_6Dh;M⍲m/nk͗yiTB:%A= 7͗$> m=XS M)?룒ڪL%q2z)˓^J/av·Τ?b>FJgͻ,\eX.kV1CvEiHa_,  7xCjJ^yMJ 7]wݥie9_`y@ `/.D 頲'{.TӍҵǪ3/Ujd=: ³:-[W!^E"[־,.@[ueê`]X<2OA `{쎸'n7|KKR"im& jж?IkJQ. ÞD} 1%g;ٖ]H'Y^1^A!QӀC7.<-45\]_ٹ/ W6v,1kxJ$49XrDY-VLO 6Ǧ8P$_efK%s3MN+tS]yrLL(=Y=+]?62üyKbPiΞ9ޑWruWEӪz8끂_.unuȸ>@Π}؅ZsQZQuEg&i[ Hh5\Se`krݻI·87powQM}'uJXۦ)iI۟N`%7w[O9M1hddvMX:.襐BZ' }Mâp[hnh`8GW:ʯM& 7A`hk@Jz~6܌..OM4Dg6|c?g;F_wxȃf|=:3uy?4y~s1 6Ejd٘cѐGeZGD]G N?)R0ՕُQlocʲ)*  &aG ~~ P%Rk-!h76$vW>EHXޖ'EUDq|8>P?Wjwn%wO|v(WwXA1GEtSE@yU5z& _;] +=4Y٭k fI ĥ+gkd2,Gh|DSemm{GSj"{EܜYץ;M cYIE=ǗJ0p?vEĒQ՜ͧ4~QMܕaNM(CUuB:( 5DRqI gm"_ӱ zfԦP &hqkq*+tNXwae%UrdHjAwy>gdМS3s37/^S&VM/*X8Opm`"bhjL 9E28Z⎙I8?B"J5`wqmC'Wpg{r-z٨xK-6qN?=Bnj*rd'VƝ+q1[`x p+և_g 4(mQ<Ӵħ-'#| 7c^1ӓD'> gG]0 ^A1 ̼c#hn1Fb|o[(KPBTύ-q豷5bq{<. 6=_AW\0t|a}DE+kjM)FKq_ăZXW!;WT 2Q?.1>ox(Fh4?`#qO s,ۇ$c CW W [uڭVV%&zTz #FaLDŠޝf7V evP2Uwa ߜbEP&v/x̺XmT,q0b:$䝂D1Rͽۀzdtg!Fo3ro1[` 3Z!zUJ8'خ}:Z2h[ݭZ{[eP<4FYinѸNN#bcw)&QۡCri=IQT⵿EZGezw6>>x-U8ʛg|j"Jhe:7ZqGufz@h F.{Ȁ_wbB۴3 ifrFRۡĝV0@vyI.@C3#+1l-n@O|Jq$? G<3(CjwN}#d(xU0eL |vj wFw/*E'TMXeEXP3.i^nVY)/fS_P.e Lh5CW 6VN^x^ ,2ufը/"e5ҫ-Q_lzc|<օl{ N.é tS3J%:^ 5^PFtj'peܚOóMOicpm?ln5@:h^zi_ٟK>2bwE6W1.|Liwwܟ7Emd'( xD4G\ZL\DPRL?jbbZ>.|Mp4k G[~lZq0bTςjUlWRg#ӨtDdVުWLrXƁ]Q{[A"%/h}]%FBM 3U_,4nq(36SSG3@&xqEI$g\"0A8;^\Up&ȂI rI;\N vxCcˣt.RBKhʝBL&i*,=0.f\a[dsnΛ*PKM^%:8&RԚ.FuYaU6kK!kS%q=Q]TZCI(iHCM`eN5H"5G;\.oзsLùI8 @Scs.;x2C\ `Y1 ֔ N_:ZMfo+$DWJE \TOxN,FPrpLѓMdS"`Ľb-M֬VxLŤN V)yb֤0G<(.7IlichRԦ5Ȍ^ Zhu<A5_nEjyx/4f9˰ɲb`2Yy%~5_փ!dgǭ^!$\4kxJ_7IR^앖;BTѧ"8%9뫑 65#CfAgg?RW?>"JvQ+;:De4a ;-nX!hhU舗* %Bwhw};lMOD= ~7=F2p[SZ=7i ,cuXIL ,x=ֻvs^aB{~;`EtOit`ڙHU%{Sp=Gd7lY|SO@*lw:7<+^R"F&@aL_z{ - |j_iVulc)狓:u$ù8T5_=HTvDpg ^jn"J,P|,7|^=b`fz^u= Fkl\oժ{?g{,ľҐ |¥ ޿M^b2 ZR[:[ָoH%OawaIGSpB#`?ny_ZDk{z&)4&q_U'LeixlئKeiQ['w&nq^N8nQyέ}:bE_.P,n罻z-qć11Z2:^~$}ʓF3G}^l>20'>jKsw,mhFIDsbRA1Fk#u,h2Lр7X*9DMv$pC@1Eoq^B`s\^p 4%&y 'Yߧ$/mF&9D m&0E=?Iw*pJx*8 ɴtT,kyM*KjF3A _sL(|`Sd6D69%+WӀWg%85/жK~cŮR}wKs_nQB5(%,,F[it=ېx_ؾbJ6jTb1[/PX駋N`ۺՇ!?uj&~:…QȻP=sU],B,ZOnrX6 5ƢV- X#qO50\BjP"3=JVxpi 6Tܒ rr  43;T3=ݙ±ֆ6%iޅtO&ok`y?FP\0+IObm% *ilŐMJ C|Yм-0t,pJtOWfdAqJvzK>QT^^HX:+4 ؅"ve 4q `S6*]c8TZű |'FF“0d;s]<&5@mؘsl!ᡥHj0yPc9`#nۍx,ebbeCJ[#Pͻˆ `/ ݰnu-@͛y>yح6U+*TizE ?)O/KyjSAz=ȟyCVDx J(ϔWR)ߤ/~1f. U.&JROXŒo-fi%^a%+i_h4YQ,MKE3$2J: A#PjkrԼ u\U~f{f>[BQI%#`4~~hb7Co9yN6@:y(< %LyU 䓊tPw<&\q:m ]$PQGhǵ1<ĆFԘWeMUjLZ0jDZǤ*4'P}c((^0(okG9Tf»\ #ԘUGG*0xܿj8 /!ك[\UuxBqP)B\|?H>8cY6?RH`m⻭"z^D9TePƮ*jTQ Qc%F>Dՠ)M/#mG uSra^G*cy0|W; v`#~ ŬDK\P1^+793w٢;_U:KVR*ˈ%_^ұċ)8۰3-![a+FbI<+QK}۪Xt8WXz Q[9 0t HLbn];mwM)N"=h8ޜ'wk(oAؠ:C^˹߸qhg7[}0'Äw-_PA^_c0SUb2n8AdUn.k(\mǃm=J=Ga~?&`K\.q71n{50AG:2^;.|/C&vD>+}Oƹ6?3`+Bq>*| 0 ۅ|wCIqmN0Q2SN7B&O8rt\yҢ[L)Hg^ʉ N%zY'#}Rm/.ɀwc ׁίǤ?\0Z +\9gne෍"v+- Z[I=jw^c]bny|b wuu. 9tgg ?#]̹EBX.Ur겞 |z beHkn;~k8썐An>, slVd:qq !'j0ؐq}Z+h]=<ߢ^JZ Mv/t 'A×QJ3;2g8AkRP7LݞZ7+@i!'Z3/#PvSx*i =J~W"-s= ᣥXl#>lR洜 Z=W!*j%ike?[[}=ȁXbyoϧ}םqiݨ+pFs&n;.#|y#6nm!2_9:'-߹,[=5*p<Ŵg+LCmBaB$R@l?{g^[F7NF7L@dVG??ݍ]>ˌԎ Ү=κc5Ϫ%*1ܰ!)T E"0MJPtTgVձeUa_Jrm%׺`rxFDSm1ٯG`cyۄE-?NfSL6cִ1G`6/[icT*HJ`x'e-RFb&Mq[^ܤ`"0Rc{jj 4.3I&JɭYT6pQ1)T~30bK \¢f0DEά\]9ԌY}LwW9n`m#λh{S1QɠtԤ~) t"6i'm>.U1,B' 9eF-΁+ysDvL4]"rl}oC'Dvg-M+_ sCmm.c%&"nFu*GQ.6isv/a7TAAy˱XMvd:8}1=3@#VRGfk5ܣgJ Y? %0#H4~鎷Xa?roh^0׀9@0.×IWo ?qkow.+(E~y{}BJ*4 Jդ==D֞An(a>2.w*wXYxRժ @eβq_+HظEO&jl~Ti D6Gbe:ਞd KnuǺR]cZtab\ҁCœ;c5 FLFzשhndCDcVnG@8EoUz "KW`4?J#S]#p++=zł,^Vݏ>v8xa; g N**&q-pc%#ZaJJzk<=eT+)oFL>5 7&jlK{Pڦ[pY,,<,Cknަ/N%p\; }hKzWZn'Zr+rBC֣W_QqP,N/6{z*[ u;Cpt_gX t>X@B7*ɪI#^o0Q!x݋}\+ R'ͿBV.,A$~K5[Uu[azPG^_oγA4Oµ)^ B_::=;]2zehWtP[>ͽhJ^~2L]-eP.v,{JT4Jɰ$v9WҸQۥ%*8DITLTAtA.}]FkCMZuq/r}Q2+>7eA=MvE/ِ.uOucp6^7|w(c4$~H%RJ5{,u۩0uu+%*L/7 @b%vA*B$V- *&©ൌÒZROJptX;m4('ڹk䒾H^6тp#c4|@WPgH@y A ĝtIZUt2U /J2ݾ0U߹~f啱 b˴}^ D^)3okڱA;*+fQ\l[r7QG|&KX>#썩@$J>9eɣW.1.>lËGI `%F@xr7{)~0ܵ GcEDܡ^f5ej x3%hSAb@o%f6G0,j>G?oysn?o plHyl q`Ȭ&.k6+b4 Ei `O=<(!xiUR sgj+<3<+`}ʿ(̎&0")T[gM$0;%%V sØذGޒ%;lkH 2_15#Jlnk:R853$qPØb\, ^C=ʺav N0*a1F4O»DZg;*&(Zڀ{|7SdiNC-2`JYFDޘʖ eO'NC1>ZvaLޟRsqp*F әbC֡DiC̿Q6^zCh@ R (LL(s_IpBm>ˌUGB 3eԼ\ÃC="w52UKj}P3ZVh~.bL:..5xFT0*Q Z/aװQp˕<E6>8T1E&S< {P/5:0-:= D&a.x . ^X1) “퍧)$͚-ɗެ~I@_z ޞQ$lGV 64/VMΏrd,JcGDַ2q e%"?zgexo$S+LH a$OYE Î5uN?գJ.oߋ$gƾ b0y"ۘ\ Bv{\qQ#~S.FigreO ɝIXi£Ӷe rOld0\Aj$w*-Ƀ/7VPp<>yHFvuO8{U@8CZ,]I ^L_ Rzmm(ϪIx1^[c0 H]-.G`[տ/)ҧT7ǖלS6DJzt|P';T6QѫGjC?P &oaxLTSH#ŝ>JhKx8##'wRRuC_s d0 fR|KBjTF=SY=0 Zim,Y$SXpZgsX/8%^38.,֮pof+)1rpix%3o">._JG hJK4\KQ )]DpFOz2 *ު&"_ku1ps*aKS>ɁIJCGU`i$RAŻ"Pc!B( z~~S)U?1YHmS*蒔 ]Dg1D3@D:!ۺU.V yT +Rsgq1. xI&{(_SJzp|-#νm,N/ 4PW ]FDsD`FXCX4:-Mѿ qku{n*$K>be>_b(Soxbu(}BԖѿ\8mxNp_9 pƻ>oeL$B,łe!Kuӝ?sJc+G䧣$Y!" WSIxV6hDG3!5d@S33޼į|kI(M ۭ8ty٭) .La jt0VX.}^A΁–.+5H~ zVrp"~*Z!TG؂U.[h]/:E>h u!e5,ZpBIyY{84dPKf OO{0a; ov^s #lC_JW cExJRc&S>O<:D+ݾa v,Y~ӊЭM_rQb@;8yh1E,(h$ĖWx^(Qݚ +Z` Œ{ ,[>Ƴae;1h@ZIT\'eI&ӱɯKe4?u]C, VɈJH/-^bF gN[Ê*7]{B'V|t[)'ĸ!cӄetͩZO\Pa_TI$'%$*(~sVRyi? 3Mj\]D/Kx}5IdP :KDSjw P5ԹT mKpFj2 -k-ax-*rpy1S}HiG,FN;Tu)):t< -MAנ"LGz厡E~mI]gA0;5Z׼ek8"ZAK:mrXc^y :§#q 7u0뵨*YP.-:KFפ aj%)TE|Yiad`9<,4f@|-NN]94 yh]He`6L˦a*`+EbTcy[ ;sya[5H840tx2caJ35:n C<9Xz~#\bm엕FZK3P%/ڪ&Kʰqs<.6F`CͅQ=SSNܣJ0̡*\ f&L0)Ǫg0D},;?EF8M8pq;yf,?fv<]dֽ;6niŜ3Bæ]oPM'9}mG2_ȑp*-`c WYs:5^FUQyvtyL WC~O G(iH`(NG~Sw7W5iDދ!OX7i .#*8,? x~ڠ"|5E^~<4  ~pK1n!vޥq,-le`Edj|H*=<4K!\|ؘsQ|4,^Lzƒ͏1 UmXy9ѯ/{ OY(>0_Ń.~j]V)lpZ.M E#'[fVUkN AWnos0|G#Ҏ*/=OW@O;Y7]7uw]{Pmy̫rP.!vS#w )tp[W~ላ4P[n/NQm\@QD{pq[ĉD2xڟRhD*w$0uh ǀ³!a V]q?{mEۛ~ލnTB?FoO}7\?Fp::2x<跇^1 >Mnq7H{+_En)W|RyaD~3 ǸPWmsާ|)zA#QƻnK?}ZJD N V p˿"#0qr^>oF #9_( c$ZĐˡp7=0AU ӂ/FC2Rtrfy|E;B4Y>y8X 6Z;]mK{/5IH a^f{ uY<<lŭ^nW[Fji#O">yhq%]ZMLRnzѝG Մ2՜ޓ\C+q 싘ZcOb{oB\BfC\uC\K?]uyf@ V;ϡZf)Zǥ}8'7H$FÍj亨:JR$O9p5h_A3y`!_8o`,E vv#L(S` "i/}FE-32<8?=$Gúֆ_S6JV13j}C;?<]sa@k*L$q~Z 6p.\ޫi:])=mb(*u(W$p9`DOJSRѨN̪ASZQJTh-&㽥8 HC8s+]_nM9OQd =]DXG<$Dߘf}Mf ;fO(N\p`Vs3ѧ茺p@7nIK!}|iM;ٝK69Tj*wdlǃ RPiKnYVgt7vV<•?w=k  Qvok wPǪnuO=CjXKAxxЍ$~\)0BM 3&~$ >J-%GY{zN ,"`ɦvu) ~0[(g ~(B{`BsyHBMK$Ji@ĈH qIeA,+w5|* Ocڃ}NL'8CbIф˄Dq!L.5mx5q=$.\ ݶĿa\Zʽ9Gȉm8<#q(bd(BQpL9d]!~L)wZx+޼p}j7?q&'jyw2jyJ6|q\)M/jC l_7:Õ()zv'99qPHa+l9V|Z&P5K 1Ryƛ|zLT8%~adތ%Ӻvr S`Fܧ&tŤ|"1Ϭ&,=kjݨ=qzqY0Gǐ+v*o/DF8}U7O$낟B^;Z]'w0"+~՘R4ԯU"o y#cjp1}!b#O9h\FK5`Jm2'h[r^Ǹ , |"' ÁU|RJaG݉ *ۃ1GfU8* 5Bd аTCܬMq*U"}L2P2۫c<74wlzABU8MD0QX Kwno|}cMcE\CCvW@~-972Pvwpސx`3lNU(q: YX#*vX˶-[WU ;9+AttmӛjVjV5$Rjqv^FHSR(kTs [0;^."uW?~->_ }?_R| />?>nW~CH'Y#14PB?/߆Ct4fXF&ϽiOMl:O|~wU|rz<>WZ $ѸqYݑbťMQrEZ]{?:nw>~Sj>5}jÖvu8v ˿\>A!,+}dG[[HKpEL VjU T+Nۣ)/[)?݌QQ=Ac'Mbe$za?F:vH=_<IKd]=HHɗ4Z5-4y=k%51PX4WJ:TTYVHH!7v]kE bZ9W?SYPzEЫ'Dl&,@sf+ |B[wY{'s vL` =$0 {z&M)Gї4%mپM}aFbV?=H- Bn;twI }Ox5џuF] IKZwv|ѹmub4G?|X1" 1~NFw!J7-5菦\e(^oȬw7vuOzz:}mOPx4}_>.\!}_5b;1\ܸK:;VX~F*˗(T(w lT:'x}z $s48݀o*IЇ<5k@"~BK';h/$?xW&^ !3|PC0΢]4#4$fC^j.]flEbA3M $Yio$0?u5\nBE =xrPQ+V~v{ؖUmGR{wxRh|Xr)rgù,< K1&{]|?N~Ǚ97a?Kt'zka//8Zh^wuʍByÐ 5Sc[傮]O%ߟFOZFgnYB >A/?n*ŜC*F- J*䨏'4 X.#ꄮHT>{ e|| wIY"t"o2*g!IsvW0_gXwgЋ }0gĽM VB/wwsb*$j&Dw;\B3Ks(24|LNnP]CqGXAC^V;U/17j%8y5ګMInWt ޭ|L@D~N5;ơT~ "ZR\\rw-`V[&& [-">G{s#R|그#;ydHt" pΚnD~!Ӫk-v|۴p轲]'Nkt΃Z hV懃3*ԟ}tcѤP؃v K5VZ GwϜdvV +|IC p OW'70|;TVyn'B~y̸a>:yCt7߾i>>JE1t,09e`B]pnNBL*.x;f&"F/v !myNBf9vcnUfk;պR:}vN7~-::\x=wDQ5&Bг\SOa ,YR}ah/;x0A{xK#q\CbԕKaH 1`< Uny?Q$aUTa IMMESf%PaDU 8ܩπJܹYPĽ<nb-i$ukr8SG,>GaSМ#@mh:P]% : Fhv%\ƍhp@-L"|(;`l[8Bk$~6<|9J& o`Nn~VLc.>uCܼηG[LnB&.wl1~?bŬכwK1Y|mbړ^#fɵk)`~Meq;xԈ^3֜Sn[̦W3 fیtuGyvN3hOgu]{M]U"~ wWnY^EjɖkLJ[EmIďyZjz_8~=HN⿒zt# qH=@T6>>ǜƇOT⤁%xepeimxynA]]\6W {kAтrn%7ϒEp @bb̼Ƣ\xg\ L.&g'ra]*aqvxֵ=F۟R7/ FTc:FkF|#..tIB|4r=//s5cׇ )2 ?ƎA^/!"K/ky_ĸB<zI08s}_r=d\€:˫C!+]$ɯ!5Q0ۤsgiԜ\d<'nC<^̎dQѩXCY &/;,N8(LYk#S)Q]zۆN[ech7i'X\}g!^8nw2 qoHPs}Wkakj ^kNŝt HҦB`fDy (:D.aN qYy3FzB$x{nK]iEi: <ĉAأ&ELzPtScssnP97$2B^77v:8\Q3D{.pofWE3B(%1<3*~ͻ:Zp#r1 ~* QZy.n#a-,u6<f,?Ch]gbBE`I7n:ft?u2&е`wvA qu']7|ٷd?DXsVgѯqmF|s(<~-9S78ӭ;uUΊVcr@ kŁ7+"BZi03bL4M{1Js; V48L"2GMc?(.pp 3xJg8-L]JpH}T9jYpQ+1#.::R ̋l/]1e݋+*8r xdןhzOԮf,%>W5[%GmrV8EPT8|]syo&^pJ+L*>vhbs<1OP58s%rpE~Sp[=3ogb3/980ۓ%C xs-<$E-3ZheȖj O}u 6A"d0ʜ=#t㑂>d{\sT<%dq+%m<3$B"rp< GW1xkxB5 >~݁m}5ejR}w >? ;0 JEJ, d]L)_9Wp^x加dqk|oױK_)2~!sgk3.ڭWFO]x룳 MT^&u|ЖC{!|aYRidxW o^Ya9G@hX ȹ=GcM/ĩX1?JlZS%|e\MKʡ*:ˀ~1`|-`][!91…ƜsX-=&wL $mZm@:^OjvEC"?ONIVŝ#,^LAb~>=ɥ%WsΤr zbƛ'IT"lo&{jog{M5E'l.! n3c!1G fXKdb˕,jeB]܂Yk/'DsR}̌} ~QF*BU|ӂtyrl #j1ޒ2iD~m "- A&[JM#.2V $_EIH?8W_ow5xm|[֑ɦ2.T%CwaisM6Qn(r ;?ȲvǣLu;Px+ 0a+i@%pM__t.oKX/CW:e0paNj=Xj.nBCDQ--ϔV6eI2+0 RLsv|_1 H@c*~ i 8Sp0Q=|!Iku]R.:Fޡ;JX+Ǿ[:#?EEEZr`E-o )M,k[P Q*B*t\Clz)xdhi Y4dǣ{/ ÊVwä1P1?ҟmDEcf=OldwKki2w傫q}*ɣ3;PjG '3#CA`f_1 [B!6`Sѿmwv7z) t?H 2ܐzfHJ->:8NgG9IڪTƓmgV$I&,_GjЬ ,(nLu7LL~77] t9:;mm;qpdEHr5N24}8oCh֞|q=h: `V E]Ї :U cz+vfQc^OQVη^oP1.=r'Of,lm/p2-&8LZQ@JD%^BXkOp^\7NݩQb^ zv.#5ӒP\⑷FUx>aqY'%ai(dm}r!&qVP =q;qivf52u17x2vXGWDO APsh P0OT4^2 }c BT){ D_eT5vq=uw5n@OT,h̟j'h|gcbFpSy7Jc5=\1Mݮwr}-6>f;jn sЌگ9VDhޯ@,/& dg!6P =z7p6ik٭|^TZPS7krͳǿCNC|1[C%$`X*haMo_(p.p &L&<9@zUV jzʕHLД-aUr#SI+>j{-O_q@Ւ$znƷOBF/+hR%̴W|N4Lgqў bоqGta=V@.__.(gw{q-(FioրhF ՠ07_cJĝ~M +r}!N^}ޟ/UC ]tz{ˎڷ !tB-i!}ȸ eF$FD/OY0Ȣ_4 gXx~8À(_2QclOtv>|8{ohV%  *\kn@j]+z9\UQj$y;|g:Ӓ5(^>lB3f:zOgϲ z6Q^,w?[O -Y D~w?GP\Ⱥ|[;O!gp٢+H,{vG4mp~v >؝~)/1d4i߆n7- 3Q'o#4(U:i^t+q *vGպ+"0wߏ>v .eOa^rW=-ǣXj4Pח\ۖ/߸~/ߋї;F=#Gm*˕>Q܍sw_\Dk-Ovz@?8 a@BnYWJNB/dR?l c򩵐AcjXIa ͋gß Rd)dnˉI`4bU TuTOg5 'C7L1S2Ґk=}+ǥ "?v1>z69' 2Ԟv|)Yu&ap6+wAIK3H!DYGS~+dzu!0FZv|-p-2be 1=t~|b' m. ֠ kAD 4(u 0:-J[eF#x+X, EZQoe= O-aEQze,F\LrZKS;'VwL&?5l fmI#hL,!۬,ЇaDqAܸHQ5Cz[TlwNwnT+sxtxJF{ b,VQ.HRsƋpzΣ^Ty&2(x>-haҝ^=&fEI%g<'qC!\^\j 1O1/.]l˼ˍ[rqm XPZZqX r\9N g ~K v t>D [%mxJl^N1Հ[hKr}#Ϧl-5[v?T{*(]&3 i Z3ֲĖY=OH5su>_|j <)ݻR "3.|>vYwH(p2ͺ=w~S|u\տ-F1 ||81Եx _I9oL&q\?6(ٕ%Lk[+vECc^non:l4 96OwgYᨸrG A!rmFξGނ2 Mz==U~ *%)VʙŠģ| =ǷRz.hחȣxpĹ@(EDr bwF- 95*X Ov84nFֲN`?8ҫ|vCR850Ja7jaQwD`(H_3P#6VL"vЎ3o7:4La^3=])O!SǼ~Hc#;E[X{0Y"s)Nv`D{~lM{7#Ep9bvX-SYWߛ 90}Őɐg%0Ȁ+M >NCL'V wJ뇪߈ sL;M_4q+׺X2 Utxt荆)e`bђ)Փ9bLkᆩK/Og&˃x| d]~JQbIS7cYn mHmg\jD2 Տn XMo&`CF uGЫs]S=ׯ4 xr2tʘ R)kL(W˘K7i5.,|T*ftʋX} i^x9qVݿ4 q@O$]-| wSUSMոz3ҭUv9}uU3Q&7)N\`NY˫Wvgj)Of7e aiZ-SIՋKjҦ6c˝1_{A`ڴee/|!37q%tv_}A'%{cc>g(X }9%]_/ɴ3E}t YZ ģlk6TG&lB6-.Ʊ_{#|geKO^=*žX垝嶶NoY p|a}c~}wh jMgT``CzN>.re:fRx4gj@u(i2p5aT '9z}v'?}E6]@MΠkHAv^k8/wyO/xeOxZmk]Xplg4`dP f)V_";dT1I Q}w3r3Tf;l$|7-0K5 ;#|<&/@:IoJE7iƚF4[(;b}hQ>b^CymP{ok;b;4'Յ7Ф"~}E[&߹λ~tSI 'D)|m4$:_a2J'7۪MGcx2 Ӵ5ß]wQaHKߦqo|V|Pj.8SSP."ycۃY2(KgGbkOGCdjLL7Sp#$YgWB-\&.}o{:3]_28jldBAqP@ rߞtF39[V2ZA_ɸ1G4`&Q _z9ۛ *\l$:Gї_Рk7ʇݼw܈o#G{MWhmJ6~M3pJO|{ m) {d| Yq8e|bWWA-˫9H I[pV:Mzuן]^A'ӗ۞-k|?n/~@nȤZ6d WOIld|m|=v~ fr$dJE_E/jZ2&~̡7|+?3krdƝA"Χ3w"D<6%̛z6 Lؖ&n@Nob:ǓHu׋I #졤 gCcUa¥55736c˃q/G""itEzxv_˲JHh-,o&g>5 ?tUvz}b$MznF^ n ]uUSE*[ΫgH'g ..5,*5dhiў9:Ocf U+zmMI T;uCqY"[`~p_"qP }.!kD踜4΋D;+ۦ3Q&{ ~I_qNpIڄm<4su%ZԁpQPEwtU?wax\N0Irk'~͈̜cwQa{a4 n\P[۔hzܤƽrZm" 5ʲje*@I0qף1qHyK9Nl.Ń(3Q+]fAVu\ȅgeLw=t~Ӱޗ,'6c.D>3vƕ\Eue]ꗏ qu*TK:&'jph1@ʂmT<˩IL"w98)S:s5AWs{LAW{G2 H PYy'1|5&;חOt+*$--k tGxAL`? ay\AwzhX#5F{q% fwygǭ(zCe_q5a?¦U'_T\K"sDy>XD*=5c&mg@"mS;8x%JN@wuP{Ko0 A\T&<0%-O\Wݏg vȑ\pAxb"jb*rur]#hCx;F>_龄TrV`Ѝp֛|o&f[y܏8:h $% 8B_z*o5PkCݔT;eEuG˜z@MCxts^͢uY4iBo?zIڔZ{ zREW3@懃xh`'nE9 L^F7?qeI\TɨUY"O`0Ôсc8e HO<5@&qlם)ܸFdJb(sZu\)BA@^:|llm[%+(IpUW~@7-MJN [ fSsIY@lϻGzǶVUAÔi.44+ d7%G J6j(H퍭K-ئ\e򬴰0: mLqRN޳H9>$ )gV@8Dv:~Yܶvu&X6n𣸟|%|4l+D`#%&ពmo;[ד MbAݞ.5Fj,u\/>_qrN7?R.A/pWn-"Ɇ0~Ntogn1 }GJAT?yS' CO\a94!Ǯ-gPfhm of"l:gbI.Ӫ6Mzn">^/+x.j2p62_1x7,LQpJDW` p7#O]+m]3&[9*4&'P'(:D@U.9P PPl*6Fnc jIM=C:!tltCs}xe֒ خEڨPyǢP ]'B1;=ZܭpapR f4R|wcJh6>ySXm Z-3j14([A-<2 0\SPXh%\: Ra!Ӳ-$E0!+w>S 4Q3\9VV2(LUl(-Pxb#_~Vj̳ gAi,wn]++p敏1^oޝ[G&wgrl[ M&q&U|n7&,23LzU@̺Uox:EEM NT[jJ?s4^*66Kmcl)*[;; &3tiei K;:ɫT>ʃ( #x _6p; .+|3G;)ČGX.F<ɀ ީF8|%J^iaalf B;^h &ۜ⧘Ò2^Iay4)\PfJ`C_PU)x<34q?u %n4q#ZBjmr_m"?t 2r_FwI^6Tݹ4J^Huݎa֐ǣz0yPaFg>o9= ʇCU밚? GB z[*c5d<]D%X> ܏.! Ũ#^!nFŶ2CxK}1,MDC41$( E?+t4>B A|2%Hbru )=r# ^QϜižfձo zWړ/5Sq6LlkMDOCmRCB&^P3DOtKpO;=d,H0YҦQ={@7MFqZ409"s5ʣbhCuFCؾ; Nm wp99سx`o>+ⓡJL.nOՂ>4` H6b63jTJd|R&0C14xƵN:tq\s 0dknDCTJōSamfɅC]`G}Jrm,ImlN-M==I@CS tܕcl][|YUղ2ݖۥHhđ8b 7_Cγp\LˀCp̈́~7<@K˥*o*T8dtFW'0Iyb5So]6V<:Y 5X=jDvĈnn {koΡ Ѓ >(dڋifb_bxcя ވ-̗JBQJnb߲|݇W ȟW͹j Y.':/UYWnn<uAtJC}j#n_f:=r{ 8>?o4~z>_6BIϡdWy©76,ޖ[W .,']n¦>\`s-s3-Mbub09 9,â$C\!icӝW~ˤΣ Ij].J,-Ot!׵^SVعr8ACҷ?rBГ'dI/5&(ފ4O\<*z^aѪ>Q*ײZ8-#^c!vպmylkq,`Y[\׹دډ -X :~La2q|#݃uJz&vK|R&T\@;K zwռ0yЂ}3ފHQZd*oV=eTb4+ |)5[Pg=^ ?FB0qw hJp`5ڿ,9ApC,<D|2`x?h2*:ob4-%:S%mki\l4=&rZ}sNe!#FݞB#*F@gWrDG!ݱo7턝g O>C-ay4'ys(sE%u7}J= v.7!B~~N/nF.CC "^7'/w-q->|5t??7`OxP~?@Sm?! ` 6hRCw]^Zs]!|ᅒ\Ⱒ;HGQ;B hٖ[nk-)] 8Wq_VGC%Voj^z]/< ki1ɇQ!I*tTgz'6qقX@k-]O_Wu UESgٻ?2`4Y\;/)4pZ){YRrnC `zaLpD) zEWP&9#ǐ0$ WޫֳJ 4! spx1y. f_ ' gaGQfo͞9:tPK="PY͉g\'V ?@ u7]1)J%,4&pyrE,S '\7@E^Bss'3^kB~ Y{M)5?9t$XVaJ%h&{& m wr/՜/4Uu9Uu9ՙ2LubjD7$A4y 11"4M7 q$K"&Q.e_+X'^3ۆZv;jLR2Z:\ӑ4(Ǻ Acf6ptsbC7`TMc\W^7~z^&PՐ,KĂi,3M?;3B-] ׾ŎaF|,Is%^E0X`2aǢyrͿK_Nl7)[ &<6! QLp~F"SheݿV!`yt&c/[Ps4)CZ޸T9[ZEQZ#(,m񐍆CutjYzD `ߒ7~Da ۔ R)y1[loJ6N "FA(#rVm BISUl|j!ǜޘb)5iͪEP˿^@_.W d[9*y,d8C\HTM3Z F{5&rx'Oի;ڻ(vc7sĺ\s#p%Yöd/`ΔEN^s'S]7/W5=oN]tWwy9$~zC! %F[98` >Y D!"VkM>.f/Fp%3Љa"@lvd(c5bڠEq; ܗ=AGR0xK&gFX;cS}C6xqт0CxQ|;'BW1V k1?Ftԕ ?TiZ (&OEJa3W?i"`xGL #N+U߾GX집;?2_&Ʉ66Qp ȂՉvѿd|3J#Sc,௻nT-&xwmV0ES@5@lvB5Ǵ®D L2O><?nݲ.U:leTr\PA-;E"mZrV򤴫DJG! ci2KL@n.Yn gNE-㲔f" A/_- X=y$b:_,tCJ^ӼK sAr"0rpn7! Ewr%$-r,[q^E7KH*SEK%D $%E:BN#؝([%'45as;زѿQe9b)ҳjCc_N{7-,`KHs)ɷj?}nh5m꘿(8zmK:I,(1F>A^ߠӾ do5|K{V59TiVF @]A1e|jݲhc⃓w졭/Ӹ>.^cFG+H_d5i3w'ϭ4'~z+_oJ֦MqVkԫ!k+N"I)!\P.IaqA ڜ   Vrz|ܙT*/7Q-W~7 H5c .SyD/lgNT2WߗV8|u ߱^wpmt#x߮^MPjӀ6Қ]`UЦ98|4!]ӌl$ŕ^Q}W'{yY6e ڶ%d)S[ԳgLY*3\DhVjK ksAq6RC_jօ Nutv ^65,%,9FfN:K~ j6' 4'5:tf^f#UWFMc8j"He4#p-S!'8<ޯT0M !L9R;(E(<|6S-Ui ‹Ù( j\tu9pS _ZLajBH u *CǔҔ4A4C~5">}f'İ7b;˥&Ԥ=07QiOg`27fڦkv OP4)a/Xrcקj^!-݄O'8q#Mχ N򒏚>&J>f=NV wbc&z4fj$4G\(z("4io(HxC/s*L0˒&3b֎y Wݘu Fw Cxh,5`\KxCYG?biy$rvvcnDxQ˘cLWF/IX܈.i6c@pOS,Sitڒq5Ҙ N$jtVoGE9hBX8$MY4z0ZmV0 |!QżΌfI ܋[%]{\vuV7x|1ٍ/fwxGQۃK@yGG 9ń"Ös W g9A7':"\hS@7 /`{ '/o(imë-\} FnǪR&aip$#+E)Ž9lP0~7̍,9W%Y6,|ZA!$Gj*!KA 09 \OXF8F@Km$U$Kxa`8}30V1zW: 6|pv=6AV0y g(%W#ʼN@^ۂvpf})>\cHڪ3zT-L{^sv)Ǽ6sxۙZ:>MG$F _ YTX8}zg w̿iyR aap<2SZ'EgjBb2`bKUm0 (Z+/x LT3єi^hiyEQ $UH87w˪5k~ Ƴh8eL%3'Yp> =۽9udz}:_t+.Hs_C.!lQ:cidG=l¡t9I lRϟB}EZYIGŃ59l>i\o|nM^V1$0`Z3W4OU / bG> <1#̊U;DO =s-$3sĸc- ]߫+T7Բ\ vR;b,]#CKO`\8(KQ,&L Y;pmJZd\/I٥ۏ0 ~>= d)owMLFbEv ȣV\=L-$ 0w$!*5Q鼫kRgEΎ .\Bc)OVTHI(1}PHЌ(Fd2uUgj.N~ IjmnfxjH kLֹ0̴qo!ϭ(k?2,! S\Y1z"~8% E1km^"" Az /BX %-yG7&pY1W\ёhP ͠hҨ鐏[L"DuTzMlc|gt9sI4 ?λNƻ\ |ҡŝOi;d0+ہvU$!dNjOɝj%/y""rbUm<]PϖB*˛*‚H̹k@>r=uLwnx#U XC+5AQbrgc%V8ƌ`(XR'sϜdAQ1zeQ SL$)J=zиy7K׽}<ѝw Πhf5)r,sc)L,yC x~~qGKɯv\l낎\iw/T%( ԞfeR,v\UɩBӄÜ\z?$2Zr -n9 r;?5ׄe Ih c-TsmZp"P-Sg {mЧKo23 dt{;@*K2 8T 9Hr|"5p˔^k"= *y e|48o]% ,"T ۀE җQ ?;Ln"{.ny /!L&Ggmľ m;O6Ӆ{u)y\`SFp]-6g~s]mJ8GU1 1`wlO(+y?Z@Bn )ELPNSAW:CtD3 4rc1kFZI R $DŽ;]*L^Y#"wm`⇉3v@|DF LJ-HvOa2bhA/u)8Qw'cK׊Y۩a0Q˾ű,X΀wUh||~0lƼ -YW 3vZtms 3`cn<ԈJv *9k`h!\rv.Ս%ގr߼d,*ti#K,ՉR?iɉI&(ޏ]+Aқ p4&Kl_&$UԥmN%0b #{o2폆⎹40Hhң`YJԚ6ŧ 0)k=Èʺ&"ݴZ `wixsRJ  n$dCVf}qzk`װγlUW$a>{[܈B Wu, R u <̀5"׀d)=b0p:@(&TƮLĈf0W[ߘ/ ]9^m# ~ 4HZL }@>7>GJqV VU*r@s*MM@v"f,1+  .# Q7mL"4Pt{7_.@\~ߜמO*yZӀ 4|m I< j)FAoکwX@v^Bs^N XN76yE YwRC[\"4J!vƓ]-S|@ѯ.Qzl_{nosx߈ ' P%M.q<.Lp+G'R`ZԪ0 F_n1l\u5PePI{&^ml H"bA56YP`ˇ/dڈ.j k.]sGK7wwbmKBĵzZw ޟ>^4v;TP5"__B笱{#yfXkkm[T#$Ǥe*uRvZi_)MGj6E3qRpرkL  =%c>`hrf8F%h6"ӖDF֔%+ĥšJpdO~6fpO|R~}CM%C3eH 0~M>&ֳI7gڗZ'.!;׽\>i-"N 'M4IzFޛSMqs[gzۉ.0qџ\"]O5SiMrUpzhwc4m&5h)~\xӞ D?d2;~zWL1ffaU;L8{{paM|%$K'3e3Ӄg6Gp}# |RxVds$< K5Z=ld#"czs)}ȴ^H=کZS\ލ-WĊ+ifmcq9F(2mefqf z,6OMQ Rߝ"N̴ZD\, k_SXzti?7Wi>mއ?7G:_D%j>~*FIvHY8ć_&P^M]MT+3짔̰ҸP3QBP5"eM2A_ú3fonzMIlSUh܇.m@v 8=rqGgTTzG=tܡh؟& A/w,ey0j0n!\2j$N!|R,-\9W,)]S7yz %B[ZD꫒FUvñ8)h7 ߣ媵F 56Shjfdə-OK%# {wskaj Qwb˛~} \z OEJk],Fp~1jU^/r .BO9v=|Z4hP=ECsgdOFBxKh84%bOΰmKTm2/r4 `s '6UPDS ˊ1IC)%,WG[3w4Ji IY_L2j6Hh3Ia->`HpƠ ۭ\M'`G WI XX+jTEM [%";Y]V͂ g_/#E^>\whƛyM(2\ _S XKmm6X :St ؉l>]8 ]>!vW.A=fxU_tqo(0OmmJV3HYi4j-'8PcO$F3*4-^3$ fO2?&cטЗ:iXNﴱ^#fB4|+Fu䅷$NxE,rvDw de"@vR7຾xқ.*U)i/:cmnRTmQTf E管v)ECa-}z#c &8{qY 2%7%z״b/QKjt_T~_A@s۟L/I?QLgnùJ}M_6445:}I?#hUPn]6Um|?ho2T`Tq8N$Hu'D4Xqod>e^ #BX=H+Xu87AA<Eot}ZiDa=XQb=b&WM5yZD<9pz6{ne#7N^1Ut| I#Df題TR聗|.>idoO1.ԈVXW 56˞4|_H]g &1oϳɴRDqo9 p$Ġ"Jـ[V"䊆Vg!O3^kmkjjqei$QI"T\Zu:ܺoE.s>-.OplyLPq M`3}s@7 R<ΫlZޓP:6}YzJ>)Juh;B2(/" B Tti >F a>@!_?iQ}.]D]LT7&cs``\tm€l| :cz?2b@o)e@әږfCLbѢ<9f=1 ] yO27mbwJ1d7}ԥ$y3&fiNO.0:&lߌ&Q5eDЀ %6XXuG"(&p7,^muC$NɿLGI/i2DF_@ eM,;SNAI\ d}r ba5TK Jwm '>FnXX'@k07azZsm*|[#^oȱ+#tRLq݊q۟lHVN1lbg 9ia[5>.|()-CѤwA)!$@Op<Ǫ%PBf1y`^\Ӆߧ_>@ k28ŪP5ti-^Qp+@dQ5nȮDn\.Gg+$Bor}z-˝TMtDA7P/6Aab¢ 5j 8|K ^ǝ+?޵;d#M29d^nvΧK tWm)!ؕl] u,{juVpBˡ{C:e{)9/>S%ǭ@@z;C^;zM599Nn"~#ZM1Zn<7v*Q 7{eZV>>a!`tZ|`]#Q :.OXED#CpH-py ?Gk~;OLnhTe͏jwW{ya#~@wgWlOwMAi{x\ M#7@T& l{vFm\ 졠Ai=n؊ࢻ\,$QIˬQ3Ң 9hCu(R1ypܧ,O>9*z-F .l?)9~5% 41tp6 Aoӛ/w./>p c`/fJV Ըw~4ιA\EJsf&߄ZZ 1%@ zӱi<֜H6ux:Ja9; 8zZ3QY?G .WM:sɓ'GJ|RȠ~3zy>˸1AULDP^bfein=O)@eqѲZBrYxSA6쎢ز'3[+/*sU~q \m?ip)|pӹ=_ߝ gv\$ar9(}t?'IH/tU(z v~\u{5},bLl Kʃ\Kj6r^alc>8J -bP"TČGك8JQ4>Oq0F0 lTx+&@F.SUSkY@50-0&Tg0Ӥ[1}K$FH9^ڕzVm;jIL6$[Dp#-K@' SQٳ:9 \r;Gq'|>iDqEl/?IWA#@i/x=q#f'm\5tR8$eRSw*V/""PRAR5rE4,u=qkWlbL0p B1)&}@-[D|v0L<$0cGIIj67EbGp\&V QRBPtz7JZ+VBTf3J4*Aѽany1( g MRsRKr@U_9+ ɗ %ABXw±y+a33F(#։* MPaZĵ2uLH˪M8.h ( %XŃ =xkU40t=\еy6,@dcKP#Mi6Rk ԟSp:OzAdWUI=w_m{{@۔0Y:y vnE )Lj'yʁĐtЍ:Ű 6If ۴e=ql7{ӜPp3˿X]]: q!g)?=`Yd?2ʁQ*T'XNʢu0C'Ed=׎gqhJ"Ưme\sZ2n+8S^ 4(NL邤0OAAM_#ƽ@Q"ޭW9r 꼦юE`9 +o&IbEr8cV3>ʼ*#YĞGLRl~G tN*ۑw!'M *0Ta eхᴬWՊ\ {I7s 3up4C8  9؀ʕ^m|C,}s%/Mp+HoaLj}`>DѼۛals 2ŇLGxk|W\>^Arq3ȱ‹C=ȝ·]s&ȏ @K}:ľeMId(LGȒC2㉻KY7F0Wd#4KZJdpJwҡf }и{ TxL:aXBo!*j!+io"!c$Rjɰ6B/\f:i6;x )^m?LJi`dH8rtN:CZS$@הˮ^ rtiQ_0gܗ n c@aJJSj`v@W_ ԥ|$dN^[82~ [f SN opx:,1J!rs KEKP: _-P_Bc}‘_r]_JjB6΁(ԣѲoR}H˝ }k.Oţ#mnSNw3z7|Z9enk/ l ¤:m*h[õtfiRff8 T1̭ASN$^T$е7 hht=C2P@#)ۆA{_Kا¿Է" z$]XF*]n&2yj8ȆHF!–Ds_a޺ E*o)%DRr ^;qGBNJ*/Ke-IȨҹZqgř~ ӥZTlhK|t稭k[&qӾ^m=j1dj?G`_8df&gRtL&,QD"D렚uqd}Klz[tȫ{+w1L0Y1Bep򡓖'- <cunt$OHBBJ( %AGXdtkNΗlk>pz_|#+z&Rǖ:MjFvĩ@rk77/XG!Qd.O \sjlwٞ7<"$W{Yeڄ'V|n|c?_2HݱtA:e!Xd .;N;C^ϯk`1r/Ը) ?jo(iVZ ~7{uyUgyF^ $jQq!C-?bxۚY)W1aTP -zuT&◚e2r{*UebLo߼h1ADu:s'oe{eP*"< Lv4Lә/ ̊=Z1ܶ˭-7F4ˉV]1'ԅP! !T|x3)í ``e!"/D-Tn%S/duGeqD?Nj>DA]82a#2T37b+_vVnC3=鞲>^r/VZ9,OvGKTG1/!le\@#V0O_0ʯ` yg$c9jqyc@kq Aw ZL:6 :%hHݗI#x5] x)p:U}*մ)>fcp8/`67iaf:7΍Ï7 -LSDp՚_J̜1!*#wJ+Ԙg,2r\+l o)̕aj [L;- S.A0lګ;$İWbVU`8wc!7`|pAR [悔N"PHbؾ7thT2ŧG.=Mx9=9-#}j\v 3MWQx@~Em6YQ; ^9?]Ȑ7qS~(&cnɷ~O!؋qgUyf;Lw+xq2HBVi{Ѯ#(ii=-VR@Q"b<5)ءysLy#* /xMڌ%)~p5mG[ ]TYxBDձ$ ͑ERoh&.{co(i>®hLj0!Nj3gur*Yoꪸgǃ`Jv  ş|F6pF}Bx:YXo<2Y|O5WWƾ+o0 f_'r^RAb&Nl~ܑ 3rvғ.EQIJ*T v 籺5lLU Dvΰ(  KV*w42U=A)g7thC[".*>#?j ش5+z'G8Z~zD"CbYCn: @vkv' u.i9hB!QB175qfZR[n~|+ 6Faz?8?(crRC#NJ`@6q%9DNzV=ܜ]Kq{>Ah94?,ug}`ailabz$6\PXWOZMt&6^H&|m܂ɸE†]#:> "4RGu 7BIBg)B?>$sT=],WSNL"4*M>hJg8Zd`hJYt*8Ab|!Qwn,qe|C4"\kד_fwAq4G&!nj#@|Ѱb\_mKu1y u#eTZYl+2I .J 8˜pGS0>0Нb6~dr6Z72aJ>>aC]fUϕ"i0S5Rfl,u[Jl .Pd@t1#ܐ!\m;NUaN8$Zh~l·HDAv$3B^?1ȆHtK0IAd* ?u>,;VΓ7)I10i, 5uPuhQxJ& k nXa+ c0=W9T"N[\} 9mvdšn.&w_6'^yWG^h.n8ӑp^"/[6mkr^:,ƯtWJ 51¯/Ol: `drn!Vϫ_*жB,)[3q3 IvxC?$~dIF6+F`6Ӡ:pbN'axG~բ[Pd2.[d3V,.aHVyͲ-k^"?\ x6,`z63PX66N/|3%3^jsL/#Y Tt$FlU XRĖeveSaD(B{[,u0Bth1i8n a.H >Fέ|4&$N/KrGsYGiݧIw~o0铥aڥp:X43֡e?龠×:bOga M#+a%֕:\i[c| &=ųj4)N C/t++ }qɍUu9=uПtq/iF*/:xQD =;pin_܉/ EX GW'fXNK9ɎBÁ0b[7 ]NU1Sf0#[\WՋ5\hQ,!iNT|6=ݱ6pn:ʗRNҨe+CáaƼ<=جWx:5fIS0`;bO |M;ݓ3Yn >] Ɓ&.]$mMR% P .O5_Č;'##VFϛOhvvQJ:=U!>U@Ԅ+DK_{q^ܲG ]ijQ:=sv\Jcb.kʡ1=0v*g}@VM!n~/Jw V ZmR/m%ULA~H+^<3}^*/n-DO,I-yq)- w}EGQ}:xt%L(ALoU!o%Dce);] )%\wq;ѷiQ\׍KJͧH^40bhA1I*yR1ZAD/-ݦ`U«jB"% yY:|.`IS{iN~jG]P78Vg_EkƎ"{1D&0 .LCjbm))G(d^Z1]qxpnq-'!"4ײxB 5U$!gMtWථeE1؋Lb)ƙ\UZ2lݑ5h4ijP3aQKd2c !&kK{ =w!/b3yJ6Fe88#\Mo@F,^y9֙ Hp#ĠQQ;Pc{;v,@{s nu4L4">LaUcgWFy削PUs nܺV52_7'UtC׊ap&vՑwrUT|UA4 5u24]Mjz zW3>P pnC!hL ;BaFNduI#@c5qYZҫŁ%EDʘηw.x \+.hrjAYMC*PKA=S&:\ED~*bmVU j{7ch܇vkz/BS>{ӛsOYCB4˩iZ?tn% W.zZˌdrs%VIMfl÷n4GƕP~haީ6ęBE=ĜR6y8Y]mdoP/QDw1-hZǍzy@—[+ɂ#aps?}[ Gݷ?Cٿ3nn0A0E%0EC@ڌw& xL|[ ^D.VG+pɯ%KX,t"i30 Z tˢl\29JA#&ڒ|a1"[֓eJ?@g`c|[mu}Dr'Gc$u B7xڻ^' K7Y/8qL=Fn>8)b o ϻSx ٰO \UL;N|p!h \['_tM)}An^Pˆfaxɍ/Fk7z<֝!PD{}HέGͭɃ\WS7hVw7Gh4q?GWUȘ|n60 $z2)P[nxb#CaUaaVe0`f R0&*G:N/`D`"+€K Cv J#S( 6ZይAu0qˠA,aS6o{DI|D;H4Zt^H2TiQD+@6p=s-պP jӠS6ds] $K}Ч -k_xꈾubӋ¿χEmT+6aț@XLPN/w(>QadGŅyJ6/ zzI5O"k[ȗ`Rtp-p6YC—,T/RG1`ܭZ'Qr[g Bӓq7CFǷ]Iu/f VƗt|3FANQO'&$ a p9.5&SvND.w,tۗ Xs~+_q3w' uM bB6h:ݑz eK_Z\ޱ;[.q¹}B`#?O/?7'C@ƈ\.VDK j$YEaQ1 2ddDTA "S>$D:HR"s $Cdc^R4|h/vᖹɃxu:c y|3WOsExy/l:([?9N F4tDQve;yB L {ʨ9A޶ &4_b%GS~?]?BCCώ=7W@Ad_ Ƶ[,.jVGE`HZ6YL 2+Ҙ!PA+鄅}B܄qM2>Tn^^vGr-zv/qyŏ1ЦQU]&IO %{UBOFlYˠW1ࡋOKFCcڜT-P?xaaӄ89HUَ9do)ӸݑOu(HΥd@d<&l'va75)<()R@Sr[G>MS^JKP5K3r ڞ̧7j[dW|b E`S֤sm1)?l&̌fKz1:ɗHpe,͘[\ܙGa<˞Skˢ2| ذw4@UxKbw&vq%|yڎdu3M:8Ywp}C;,-" UNJ>,{,y~~R'DK?GyM@W R]M Ŗ/ZUp(m.um#Ж-6zdmv= D<؜(-]P-*QU n"2P! ӡBكh L.pďWr-#)0o؊TF#%c럫ןy^z/!XLȸ )+r[tyJȿZD`\C7,z4GA8[axdaXYM  's[nLR_}*-UZvX@$PJ(l |PJX7Ҩc0VBzW2=B^a'ވCu\Go}GpURxY5eC( >vFsV7 -!= }MmP qnf:~/^ +m;Lԝu5(R}wF'~]M}~ ܷ]_TOf 8b2K1in -&眖SMۊV Đ7<ĭc)ܧY,|oxIƳw~X0?ENq?nK r%Z?[4Մ6ќ8tV Ӡ@ũH#ͭTL&|x=L+ib?%-_w2eliwي&\2m`f"tx@{,QQ[ʀ91,Ŏv A$=hSMּDXG&G?ݣ†pX(FǔZdxپ+z<#w"T:ؐʦ4iGe4|A 3?ꃌ﯀Z;"IKIInn YLAahWi:r&'QzҀ5·ʬZxB 1k hxSh7ԕb71i>?|½>eyz!!mE0Oˑ̑^;"DoKV-es f5;:  -:ýC4IO=d\8'4:ɩc5>ygw9࢓ #FA1!%<:dCH:A~a;Rt}Dު~%mϔB쉺2 9/ḏ# d y"5k "zT[k"*ʆDOw;me$M #cϊbAN4(`sʣdO KSwoZⰝd^&z`&Gr[o}D7s} O! 8vVCP5!.yb_vud\ o# ԚӒ!nү4F\ +FXyzb|z\P>= !lp#IY\""?,r$$f*d*R q;tp3S|[3w<ԃ8,IX_w%O⛼%ClC!t<|vC"^OYsH"Tgte#,U ;6{: IaebO>?.bH'Ӯyݔ C,:fg{Oχ0njp_B:6H7"KjBmH|UUIDAA9'2"f!oZK(N$JH9zJg~01Pt0Oqmx?!q_ ٴgU0^x/-/+'kIp?en_ȦG뻱;Na ?s\f)4kszсc[rׅb(kP>|%>2"t!څ‚tysRdwJ%,=w , @y=S!> hZNՄ8wwTDGǝh.gmDW~I?9/x@"EZ@c`bw:ǚ|BB7|@m(s> D#)j+cr酎ج'@3axkrp;k5N'{\j#njp^pouL9$.1lgQM[+3sMBq]Ȓ㍛} zbK ʫOY5.=6Mx%f?a^$̖b[XW )!v<m]2x3fk__j]-y8[Dze=cKk/J+!Wd[o_һ>/25k(`'dY(gf$9pUm<+(C P]Wc**سrY ^(అԆ+ej[@Q RI+S-b;兎CdMًY a@6XRU;W6H k{ߚ1bŇD + 6&?RہaŜ2bNܴ<ɼ!mVs| :axe:RP)XT?Ta+T݂.h ,vQ`2&_&p2'KT[:zxւ f Avk]R78ANt0YO0OgH)vAhA>|㎤:7q(Ao*9NZԤj?$@&* /`] 8#STA(Hqr &bzّ2&u/ G\7,E¯K,+=SqȜ>bo?ˡEP| v!^"N 'ӴXNFcyB`9Z:3vxqJ`pZlI7#| (cˆ!`Lx%2 W2jF},ocH7/!hHR3[ᠽ{C  AR$?$+ENY]SKע?&V8G76=q _0ENCJ09n4Lֿ@JZ͟DsN# Y>n fcƛcs.̨Um34XY)Y37&/eM217.c2F= q ܣ:l3<%(5&I=9L,!E0/ʠ1r5D}$h&SS<>9"ê(HSwóɼFs땇e"+I# E0 B(4a <ו:\T?#,P, z!.Spf[yP^uGM%߈~[}":8 /99ͮt~;  ,Y|r}`FwPl'zN-d}f]m;3t2 ;C εUXoxطaWݽt<T?;G`&N!j;lonMsoeW/`g[kL " <7p7Z+:X=cN>Z|=(kawYO|1= Qn_XG2p;dkpDW6?bW4 NC)6c~GKrL9 Œbcs`&61!(wrB֨yZZ2rƱNK}3'0YD?v faɽE 28BK A60CK`7$%) ֞K#SzOS`1nta f^#oM?_"3ADvk+]HwIb0 7~oP~U#4Fh?췿X0^ȆdF+)M$liI~a–n樖 +DF6dXfVkt86~U$~IJYw~wpER\%3=pk̀=pI;تU_KZyv6E-`8껇 - e<Ͼ~Z@,EמDBayn|UCRo WeNJŸ<^YPT?B  pȃ=Ii2{Il] ֘E3B׬,%mQ]y7Ѕ-@% j@)I1(OUBLyVuB(مF|r;)%X#(uP!Tu&R"XJc8aE8DqIFȀ*5{dE8|Ŧçc~Ҙ1o~sB1 @V穪4XDǎ6SX |HRie\y1PWܤU[o4zBIAůGQLӣF'8}K:g@e4podi(B0i!g Ix/4|luoKvZy'"c9Ci aNy= %0=:Du tku\og57)_ 58ڴFڑҁ=wog N: YY:Zv| HT*6]AL>6 .)L&it sSm*j,Ɩ\B"wF,nЛW^g~sIdl8!xUqw1Zȼi)_y B$yF"8:߀ bCؓ@:U]^\rT]B\ZU*y%6֝U[E}3\ WRg8by#t_[DF@Qb>qck߀YgBp]^ep)N 2ɗ+vBN\o ^퍈h!\{-#S6FT?ʽJT!u4rɂjJr<0d$  >n=V5iB^8I!9D{}=|~x-œ8%wxx%Y(Bl"`gv񈗤A>zAy15m\<\xCt$oCIIU; rkPpa|{ ,|v2sϤ9fnu?ėͱdSNs9x1_ ?w#ok}$[gr)c<ɘ?{В/!3?n-~3qt H0FU &ɄSד Ƥluǚ(O<<]_1hdy+ p}z^o0 pvY@<  wu̥Ȧi5c 5K<|p?!<f:aӐ: Qi>l]&kE޵N1Qy:jty7ŦYQ (xO^fQqk9j! 5 3Ӳ:UQvbH:qjݏi@Wj-[F\ϤJ4xh:9;>5ܡ\YYԬ`:x8Uc01" )it:daޗ/0L.iOƳy j05 sD=ti#쵭0E[(x2e#zqv\\ld S4*Zt#LoKh8π2UՊJbB/<:Kw šrA#ApJۨw`+)֝~Uձl5#b G=Q"Xk* tyHiԖb-&6S@֭62i| "}./xa,P8Enⲙbۛ9g_Hc/?:D2 NAr:av"-#M !>j(y-K:k#;{_[iO=xwH{3/buȲqMWU)!:5!2BEw(j/awlI^,e$q0xqDC@ m/$BiYlnP:\ ړt>*_bxIGlTha@ h"Ϣ≕ZЍl>YG tIP&&7)m ĨPjNX|nA Lvi^bwʬfwfC OE\£xa'Gn22Є.$@jb|֣5^eU2jH'ˋ*3kr1T(s\  vJyWFEt1M|  )Tɚaݣc]$5xENIjKήxĶBmOîs$ JzuYOX>:hQQR΍'KԊ~$$nC)ThJQ2-=3n+)KLjoRғ{gkOY?_A!IR:aζU ##QQjI.iU`lEsb=w: v/)l>=(r-S9x O9WAXh ThWHL ‰qҌ[ ryXHas t~UuwxWvB1?S=zA#ՎiǏ'N'rmt<#jzAp1ƊvKnF&[A*8:ZOLNts'[p\N<ʘF!%qʒTͱ^¦b0I>UMAYrE%^'PHۚ7J6=OA[hk]vS:5#-{;b1 f݀iL.6B.VI7LO3JXR<21qtq6~t*%;Rc/G˄ f!p83Lj%E[j7Rˀ̲% mQJuyd:Oz+v.$z}:)(Y1PΥ mI!hU X|6awB;3}QT\ZlgRZ?5g- #_WYMx |c4w5za@_e86C%ES5%0F̅c!xӓjH.(yJuuu,MwC7fmtP, 3>q&h*-Ey2g٘ԺֱCg UD 4yy.aN'׾rqtqI3 6kڧY'kUNɦolB- p"UYeH,(. Yika 2F 8IřەsGL`۬SۣOZ0TT%w:fF"gu JGq=qd;0gu HZ^+aO%t^6&~?(o*Fue}(`tL-eIڂXgh, [cy˂҃ Sgfwq9)XA hoJX_ᴬ}8w2l3W7G3Feʊ|' y-&Xa3JhSحmYCف V.\Y Qw܋+э?^?^"gvd ҁ X/p:BxG8]Ś"Gj>o#d@%AɁ/ereR%7=^߽n:@$b @B>=2?sТFO=q^>*s*UG1pԅ!B/*6\#vagr¨vϳ_}mY$_J'(eї}R'C~V?O/]yr}|T|ݡMIyD"uĹ CAad>h.lw1v{]BE{'K+O*O)\wdY¼o [}ދK(!z)\dzOZjmM/ǐZ5Aa<%1RLOOc5! )@mjѓ.y%DLwH 6A1 ;~0aL--v#d`<9rf-U$Ļ9){].TXkWm"]1[$َ 'W ۽`jUYaN2˘"&aZOtؿY mq|D \ 6Olnkr$K"%.LaRm 8.@^)sѐ\k$W9h`@WJ#OWǪe%h9z{кł@o <*mL '6`d~ñɚ2G%',V[HZSO9vdNʵyh@Kk~3MP؎ i? YSI WD^.N8Xdk6t?l #-_{P\ .o oBd>Tn/6.fYc)U$aKb.0mήܦxS9_ ,o^/}lrnUGX` ^qunc}-;)@a"(nMqss?ŸO'|ğ}? D?oIGȩ@Y+,BA" @ C~ɷ#5)нCwfNm޼]؝HY;~eG4pN &Ƴ>)F@|7̰)\Ih \Z"TC @t!;Ne@ց)1]C^~GP)/8Xcu~*f$LXT+G؟^H`BR_q7ٜs r&FZyvʅٟގ^9$K E5'Eb$!=Z"(XFc:`:@EWl)#ۺd)E.xR5Kp [v Mgw?)! $IF0oUg_m=)_Hnxvd-y0:#F6csMᙎn'{m\5HEmDA i2 ", &|wZ CLaY!l6'Z:$;OUs-y[/}) ؽ"!!yPP `N'=p?!\fS 0 PO5+}CTn,X]T pz dHشCf,;=+́\W2En~i@F3<[N^4hngg4>fno`]?]"fIJྛ$Cm%0Ȅ\+#-^vmt` J`߰J cY9Xv q| WJ!,PBq|@ᑏ6MgiCf#ͪкю#j[iE#WU9i? 6jp4Z%DJEybR4ίb'r-A(1fдm.䢦pFE=^=2&jJ )_ 2qrww}ȭ$ݝzwwWU*rww۝Iž Rpz̺8&B^V4\Y=|G,(G0֩_d3JyN,OL9>*#Ǜc)#8 ͠6^ലh 8{1X emE"S .=8A#1[(c2K g9݇^Ht+jVԀԫSQ >E`B>D "PEɎ1,Ţrf;FH1"\zeP,;g:pD.:qZ{PkB5OIϤLĒU׍=;l` ~ƧnъW,e0/ 7|2$dT~Irfm~%ALKP@Wk.#^ &yȎ3#y VoӴ (4@`,#4I$H6ԉ *'[BFV&; JPRTxˎ6u'E〠Ufib#E%ވ]?Ol-#2dW}Q%Cevq.L9 Ĥ$:8} 2ZNiMkв0g"ac}"\|J@ ?#@x~)<-ȊU.Yb'sRttCES gErNȠHpvDXD.G~m422-o~mNI4|v 21qʺQY CLl{ d%"GEb[|P@D􎓂h(^ma'1I3 ~!&/|)6O,= _أR LlL9^/E󒯤G}<vRU%[#Њ ̵Pa~B/CvI!\YȚVx\8qQ ;n(Ν[ɏEćLJ> DW {k6+&rxt%ң Y)K$*US0d|RzKS1`7D Qݷ֤fc2C|n4azck ǶD _fOW nBNG3¿ a.MTׅb鍆%'MQ1MVʦCqحVo\w5N-$ɮah!;a.DK>>O!:1|"%У$MOND_~_&;*&N*ŷ.T l[}Xu."PMBI8 Cݨ;$nseHc3/-CB?t?v-D,r+57Ƅh¤i6Hh)ݫdy:y‡#F͠օހj.az#@5A`Iy !Aau4|G1lx;3k*Lq*QNM;Jc3[cYW@tZWhzf܎/3`-؏+Zy/4/OmXc!ᦾ_]{@>YpeaI [ˢIl$(O4AO%1?`{=ir8Ty;zkCNUgQ+UAnk-!Hm߇jнzc#dXj(,F>CuKCKWJEz #9 ,lJ*TU"Wp6&L;0JwQ4!'BQ; ћ"Oaި.#W5x%1Q%z.lwЅ N$i5ZZʨ(M9!Zi"7RD>J~ GWԧ`_l/S(t9Wh0!J%AKí~)k}Ƭ_r@yD._~< 7w ݡ(SIb6SIHmΕQq0;GKe5=̼v"˜v4ibZ-t54Wwwh˒^<@qdE =8ǫ<Q6{Vl5mg(jb;mgJ0F\QbtS1D+pL=$<3Go8>.y{mWvҽJn[ 7kնFدA*IׄU/)z%-"ƣ˷7*ca-4&k᱇!@l/{01ahQE %E*L ܡN IY mҜ[Sc{n jYtn7FF[Zvji P_Cr@yѶd( S-Qdhv`׹aώQ'욹xflnI٠hLΊ$~8{ |p2U#20}l`Ҹ䗧p ۣ8x[ qs׮;OM)";A3o7n6oRY3u?O giഈDW#SH+[ *P#r]ǷcI1iZrxଌ,E^ƒ㲼p::s1dss^NЂ2W 'n@z ETeCs= &ztpPx<}t#G0s 8CJq;}N !miKXVc@7`W k~{@g[Q b,.*ČE%Sİ#K ww0RFcΆG8ǥ:Z Bot 67i c@<\KX]d 04%2t"/Hp# UyMU/DyRG ;@Y$jl6Q c l[t酓uJ K{mS:lfA`af ƖR`ZE`M2+q'JkD[ Ht?fPQbI"AI+QfgK\U AI JjsxR&P)vĎv} A*Ɛ~׺0lQ-rRM{CdƩYhq撠ӗ_a<}>*`.ɠyH/J*McT㡱ϛw傤{z9^c0'p<=/;:=5Q>AR-ngo~rsQ"ohb&Y:'nS<iA7pY"N˱'kҟJru Tds"/iDEdrwQrO0Q#ۥ=Ɇ~9-X\8a9eR֝JXۏ^LOC't(%K h >Fe-1ޒF>!D;لfO0 D C# k$7#AO]. xrk2Rcʫ Zz:!$K V @T` 1E@twG *]Y) deM'k%g@>A(F0-h<fX=Q i$`i:KTЙ*z،x Ѓ!Z"#vZ)l4Wx800YaT&)>K3Jԋ>!#qEzx(j؝Ti2tGCp >% Z-հ\E93gP$ A&֋MNF]wA-d$gV($蛫,Oj*!uwA}o}=]vLrEeD>1+=ůbqc\14-RAZ}Urq܍?wBx2&Qx4h"-ͭ=]Џ<@E<5vL19 BVPX~@> yuhiGkUHr]A,(eE婪,z8)]͜B\_.!+Mc6fG+bQF]%R`y~;"bXoA9{3g1H3ŦMgZMTfɱГvCҖ0( [5w,~>;V6Ukꉪ\vHYFhk~K2kw.6r$-Q:<엿ZMd*FB!WXm_ŒDYbIH{b!tg}8TktU&;Bo CK6)ypg`t@)=+--GH/ާ9UBoi=*%F(S䑹YQ=RP)-)c=nJyL(EX]KmJesY+n7do o\ER>Fmۥ]v{'֪s%=;nZ]X;Tj/əDЭ#zd̚I G/oRJ-֒V*V\H́i9ywI[2Du{[%$mD2Bo){J9 FF_8 e ǻx![}1*"Q,{{9GW5KwEnZr2þO109IɼOƔ3YؼGOhqާ*7U;'>S$uKlλD-Mw N^=q)S4_6>(#,1DyrF;,RJT-0MEߏs6HOEmU<9<<#^6%G{*(+XbXK;O  8Q,ihm1 b^b[BGm$"Ey>i+CZhUc+G mopXJqdi1Gy)+RUJ(%<£K@rc,mKݦ{l8=BgdeҸH0m95xBYbT/z#=FUΧǣb흖~010ay#E G$Ƀ͋[D {F::) '4O>ÓwMѤқ.&hJzI`脓QuμkGOZpaZKg#.ZŝňtcpMB/2z(p,ʸCMa15&%V  !r),&Ud*`.=U=F$ M\jTFz!҃DCP5TK^ʫ DO_)Ŧ:>JG*c;Baz2"V?ڐXðO X/jq][ BEDϺ2N6{ VmmxxOA)RCtPBT=hΟmH@~Ѳ![ތ 6+lhۈ^`3Y@{\Š?YREhz2żͲCZ?k~va;$cK[*Ė\]+smqA8*G/I'(gC o+!Fе|܈QƕN'R +e2\V4Ft5Ũ}-٢qEYqNZ.*E kI{ҲPfL x{O:Re"rfT]VeKV\ĕ|qՀȑt bht&s+bb=}x* Z6%C -etz+O{I؊.INidG4b΅\b%Ěk1NSx;WmxY>@0ior6d[ޏuFy1d1INvt ʖS'z$xI$`۔PŌkߎ3;M'WL U,WN-izbG_"V޲SЖbvGIfCIQ~i;{F*!I$IV6v|w~IK6=$A-$A$AQBe tH+%n- })s~_)~?+#t-9?ɸE2&zGu3o@M+5PךPPئi&d-11ZŁ=wU-%=Ԧλu774ětt IIy-}V|%!y1zO0!->tn{a~C!@G-~DH^9}ӵ>1 B1ӕuc6E昪VoJZiޛN)NC>.!l{i>Gli,2,_U >gS8Uʜd|I79EH1"Iy~$$1 sDN F1(2#!*-% IVީLLMi=~? J܃E!%hG؅4v)ا6A;a ,Q،!H ʼ@Je؀&IRC tQ8A//0v6ΡL+>C%O煼ĐQq?l$Yqd+5Ԏ#=P%W/ LYb,Ꝩn+oX|+ B7P`GZCN!@9F< e<ɳEp88f@JY?:zYY_XK og= z-cǑd{#; Vѷ?3Au[fH`Qyt9Y7L !@Z>A5]ϔ&@RJXx\=K{:Yhm%rEnciv[`/H.tߢ8!4!}kJ -iFGff#FgK9q~B_b>x&PȆ>P[!Ngq=ç \ϕ#glӵmnRtdx&g8 r &l gF/^3KŮn:^<)F*&]F@E|G{a\-kQn8֠$m/ DZr#kܼmIe/eIvAp;LgP)8H{[6PV|>BSH⫛J|H%S \Ў4l[pC,Yeȯfb,r-B~9F=@$KxI5]sw$"+T ?U6|~`x`c:89^׭~2jz~Cˋ[%!B U|ۣKqLQ< ;W ܞxY3Į-Eُ.!K\PD8]?E(iĞig@͞pqCܙ{Wba3:t7?ôzB'fWސv25@z|8zPl>3p%p3`8nt2~Ɖ5iInkj8M8|FG<#׸_D1!#F,JOm#Ꮧq " xI.zyD UX6ShT.yYm4"\}&Ec @1= R[ >R@8 l"t #ǃqbCmlXBjP~ ˇIGe3LQCÆdD8k WüxR)BET#t⛱;I67nܰ<8Z6= O>iSݍLa8.2bS_4F}ø"QrȑNO WŦ Sj!`TTZhf8  ɐ ^vHY6lF=fڰr(nmYFЄEu`cEMK^⢞&W %\)X*BU&/GVhѥ(Iwhw)φb~"WkC\#bw_,PH>iJ`v$[ 3թ{ޠe&z`qZ>?BҋC|̩Yۮԡ(0|>lOQ P]p_T絑|6J6Fلl\&L7pf љbGa𬒉蒷f Ms./v?P Rfnȁﲣ;|dzdt[t9OcآnwoT|QNgﲏ#LLcdN03\Ev)ڤ\3CUi'@;q#KP4sR4 0$KFù['ֻmh'Km iV6bgbv˱f2rL~Pmt7zdg֓Aox=ST;@<_G$T_,ݏGxj2)fqR2f\_A>p_ew{7Giu}׽ݻahހͮûfrכp80`Z}͊_i~Pv{AKsm~I>bȿ29(>d^K|Qۗ/'7w}^]z;,r:/l4Lt^w2WdhDZݩ|v]Nf0wwT]7$).7P$_[hw3ԫ87*] TңAon)o}n஋ZMga=!+2^/n-g[ovТWMf_)Shp>0DlV@O`6)Ҏ{OWu;嶝7~ݻj0,fvizR'XQx7V&n9 PLbJBwQNֿ{BV1rR͆7#i)fu=i**a:q+ݔreHl]c}1[?]b'rt?bM{-k{ˉfo8ұ®Zhэ6B;h՛m섞݀Bq͘4ݙõݸ<7ߓ)0tt0G,!ujs82QeS5zlxNitMs|:;^ܽt/qj(1.-{\ t9n?wV> X^1߯ǜMߧf; xOf 8UwQM:+BGo(8trz5BMAe q3E q,_C C) 0{sd Ls0hVE$%E (D*&RȱG%8Os(ΡH(q a N5&/{8ܼva>DJ9ECZvs.}ai椂@t?;^H̨Sxgz#fqoB-ǹ|Cd€Z(p;v'aڋwAE~܏SYL{D,ﳇfp{\,:qf7^=@bk{C\rn<5ws$eT䜺p -Hj;:X30ַ !T[w8f.#:"3ma䡄(JмuE$H> gɣ$Yx$=R}w$đvs>KUfu;xizt#ўk~ܹcL!dҬnyw#Sڃ1i+F{qϾiz ݬxn/wGq4-v G_n gd;-8ƘTp UuGA2"%>"}v=x D@LMϬ]׉`CLQa:p;p0+!&w9ܛ!~*A@Ht58C゗?XusN|0J] ;j8<#)Fܝ Z^ FW1HDŽhmTBNs7u`0).Xc.]mKOxگ(4σ(˕zw^]1vLa^W8M'=<V9۰?>RH S褌pӪWBKV.U vP2kO `>b50 Yp2pݑ>GnSwtf`mDP+V"+*q=> YoLg՚2'LY) EK }yA=r^nC+e-I\r!>*R,:;Ŷnq![O~Iڣ?$er 4ݬV(c j~t6#ۂ'zO؎ݪW[2ֆ^u?FMaowz.VtF&l[,gaoU|v%Uʟ.v&6gҬtՋaG]/  -qZ'yoN A`f1춈JOx'm&;׃%h I9Pgg;gω7CeweӿMJppNn@XܭcCsytv^oU)^~'5?|Hu7E w>1^@LIܻeo}ٓWØ=PZ5ym=|4͢5[7.,A5V~[w'8G%v⎤jx~k r{+~:B?v(9>ȫ,#yh ~.'wB>uh>ou[7kOsr[rK%ÆiuK>NZSvtH,[a/,Œb R\1a:;/v/2[ xXzb$B ^kLZ..Q-]6v9g}>oQb.v sC0+5$R>_^ Y"8d"GE[O-h{km5jBBegXZ7HC èɝAK`8çv瀞@dt؛Pe,J˥gL2A4nr"'6XOh {_<_[:.BaZDhx'5 @$$tىL'ضޜu,rZ#',\kn !uLM>6.G)ʼnOWT&*J'~ I|SzYgG99\d _D>!Oc[F-H   Oؿ Wl+vtgYTWϯ:qN#0h8$|ٌݻœ|_Op_m: ŹxAce~laos_<Ŝ "WֻO5BszX%|$[wiQb\?} -=i_gޒy;99r}9J"[+D.ͨ2\w,XcMNa+bHdw8$Zuߝr7)a_ 3Z1k"-@n;=9Ja YMQjG]_;iG}܇wp&湮;Q$A'UG2՗ًT5:4M[|c_6k4g.P=cc!v{xed%.9gft/v`t +2OVVTǻM'5pԧRl K5䕏֔,ύ 4. hw!7̽t[oMpg&jrGt\N//9Hxi~iuÍ'a}~ɭ ]`4BS"DA/6MSw2Zع83s:lmgKAe?D eHwx^k}d >}wgX,_ngU$bN[d$F!|޸OǚaM!±re$U5 M(@t(6H"|}qA5{Oʮњd u6^.rѐ]'z cn8X<\;pr'U\>'d Wp.P6{Hc_#lǏG" xwte`#Xpݜr)/ z6o [,a7~RXヲZjK[4)}B#8dױ|M"֫PFy5/A&+:.{PxspTCj {)'S  Yb + >yvBYn֋eqlV쪺 ;H21|@J(IGL1G~k[a9ˋD[kyz]Rk'rgrYnohP#8v`!! %q꧋Cr,ЁĽgJܱ xt&ͱ~~N83 CCG{^zGng_zwpvQs F~+!XN5V}qr;IB|YjP-Ν-fDN/OXJ7*G"b.Ǖ~J&ru~疨o0qرNB2Uڅ.0Q%7WgO4cjŎ2L\j8g8mr9hXJ Y[,fপ/ዉi!Y~/ 5HzߓV :RGivˋ:6K?K,C ÿFVw !J < %Wܰ_ ZNR! 9բА\Y/HoVs4(6MZ$E Kq0nKy0k7cб Omc1K4!ޗZ{'͝!;;$$##:S}H&xhɥk864D~h 4#kw:Y _FSRX a5̲.#3^\glւ}0r]N[6R-k#`(1 o0oDPx4(c?ߗ^5 ^>v^Jb:ĚdLEl|߸D㭁|O " ) hcKB<(:n+thL$bi ň)B$ ]?(i H$:*+WV%Nte?4`#ѮNҐݐy9\NV~IPO4㓌Oy jX@og ڪf;])ȖseO?yuPaV,:/(XYZ1`|Z?6gvk?ӆgL[¾D+h4}܂z@0?I8XO y:Oq ]>Ο/⑛gY!_0Z*(|Z28_bL(U]A={XInӯ-V \w>qCuqLOlHNZ<n6v")?r ,h[ܽ?)|4+UkYgXzyt,V$Alfy8#`o7lpE'v|wZr{{_uXu}Ch# @|Gev3 Bpwឫmg37bsqvzIpw|3ZjTm9碁 I+8΋B'?5yz%\~`.ZWs_QLL/zF+2Q:\鷿<*rMu!۝ˬW4u\Pn};JVowej*Gp/GTaZ\SpN+ߞ蕋yF^E0OΒCǃ7vX>r(,džcqڭ^6ť㢮;@"`KcN/"!w# SR0S&t8CaPd)r|NL('>$%q9@n`,#LTzЂ{p 2)0lC`gVyAWh(ߜ0xE:{i.L " /)gZd>5" $6tKE_;;d;-ݑS+!ٰta)tސ.`'G[vq47sDS=5~[ v 3l zIE1EBnfC;8Ân1|},eZ*12곰ָpe'b[:!n^7H(Ar2 S۰EUMah>q\C| Uv a .7[u64] .{o y D3xHYq{2wdgsgYPK`;`R4B?ioiYprję%n`^,y"sJQ0AN tPcn !r %LնJapƠc: -%$FCXރ`jeE#?^hB _z#1gtnX G#-;ᶐn J@~H~ɋE1pUdK<5B" s{J$z$C2[rŭn^.LrxvFaxMIO( ?:mS*v5ge ۨw GŊsu)PĨ5G"'+<Օ(#=`Ga f2=?s-b.`T!ݻ'kD*POG??O_F<`ܾvI[R/֮?+7}4C]~YD2X5=F9;z6 !?!4PhsXm6w~тBw4;\*NluI0Apmd701\˪]:.,\ e}}&Eqfɵ<wx_^C\oBva~Ty͗bbc(gZWUn| A]#> ^+ƫkg:rNǻ G^yElxBU1<ّH~%DiwF1y&wUx!80; W.2}N|?aZ}Z9i=2az<Hrs&C- ~p(ƆZc}aӵBye6+SQhMj /`(%M4 齆W!B߫g<:yP_OKwIUpDɻ4##<` }o:/SS!2+rW ՝|^' EdŨ{Ϳ [d7Cu0glmTA+oX$=>ψlfMxb905h`cy]w>_Cc4xt 3Λ737EEFJ 2R[ΪooP#-{fK.1ɖ~Q?# xp1EcCF#zJuiә^|۝L|XhOH^2 3>'$Ɔ,g|P8? `I)xe27{>?bd>xr?SJJ8%9mwJ=(f/1ݭbB}1ъ|3ؓ`2d"8^Iz'ӰP*'Nnj);~EEńqF[iI(~i$)4p~6@p =c_6h)"ӊ(zVޣ*O ߹*DmܘѾPŽ I οv Wf#aeKb2CF!.:4dZ<86&i9B6Hf9J< qR:",5/փh)!wF/G⚢=ǚm]\ 7_xk:Z+ } d ),.[!ٽ Fh7Es/H CGdZ{ZԽAD>&s% n0j^ 5 MƇd/c6ު6e--Oژ4:x~ QϿǹ NJW>2N+x  ,vEa"}%12[\%vXxE\K5g6kcZSs[=7IA]Di8fLAӦ¯G^$_.e'a]aQףR3#Q98a &[9:ȃh͒1zH vK/0Ugl+3``t6VevoC @b: & rpDy& ۝#NU%Tʼnq9WT! 9 y~ud.5(1!$9?lx?*6ˉp~V xR@g7|2pKZӤͥbE*=||9%ɨQ>=HR\H{(P뾉;TǺtfSdw|A]U tn"^} W>ZS#H&sYcT5p!۰_BތN.cgbIyVr):}$I@D~YQj)p|- @~%|l0Dr<Fքֺ[l$q89=^<{iWn]ȿ޲ScYy=ѩ]L#O8~d|igczfO4Bn@-3 VweE\h0BXIɊ񻛤p iw4׷Q owwxiC"P<{,TVa u>&nvhkWJG&=LC +A\Sl@I%a>>app "^LꕛUm;Iε;mۜ.$]LK\q: Zd$h$YThZPOx>`;Fx`يlTowk! "9w`rΕQ0;VO+%t"x-0p,ԽNUaFu80AeMkkj ޷a@ ZF^Gt0:siKodb|V{,:[8!XC?E?LĮOXy$B[n]Ӽk0 ԃ !͞H֫l1zڽIx{2 H !_/a[w{p\cW!|wI|4>o(~hS6ŝG“xqT!?TY.x#bS''S$Tq)Td2)BO2?M@B"1`OX0xh&#m~<*alF={e#v]Q0d21[C\RWU{r._Qkq^d:]Jy1Q坦;_7t3P^ELR8@ܬDHzrx[w4tj׃⧆q&6Tj5(nԪPRhe - ޘ.$@O(HjNcc;Y?kW"1`PXo#}=:v'8=m\¨),"͚0:<;!³L %\V2WEzǟg2 'K@,lz}k.-ΰԨ[oϿbs$iYY^T`Ba/;Ϊf OotCc}j7:ƻ-Ov\v?, W@Bw`o:<4DlWsELsZ%f?b4Z9‰xg8 (0 ;p+jI0‡O'ঢ়H0h6x"rGs5ǥ`x.B.iTIhFR{ɂ 7>a,uG<+`#s{7%;YyY o+,H'=y|7U'Cƣ^rqpt0w3AQ* ɗJV -9{c-K~":@S2z3`}&Q"Tޯ6Q˞X(%1x }+t YMэc切&T.&r֨F/住>v f6,`![?}«C  ucޗػU"9=V݂͞;atb;&P|!ފmƳFcPvܜcH2mYy6M+oh6D{L벋i/L8Aͯ;!1hGom{x8{٠ "IJ&dq {g$msO$i*3AܥfMi[iC=9PΠ G SpC*ͬAfpj>P{eҿK!AƒCKn8j F?j62:!xΚ.t7+Ǔ} vםՖJp ͆v6?)}ZY\By}h ֢ݱ詵,-[ev^tC?,Y}m-k"yq@& 6TˊdW Fyesaz4Z$vl80o1S%s R_ =1Fpy} io\_;ī$SlmwG%bR 0ɞR詘%14ziYH^ч@ ^f@zsc`rٰ^8+MF, Tao_ٓCKZ)ƄwlYeK8Dc/Йϵԧ?Z$J&Y"rǓ_S.0Vˀ6R 9 57cR)tkkdžpHbs 3UT]OB/EbeyCm+{7~Cߣ܎ʯ<+SA$M\œakţ0/m*oEObw6'yx+z(r FV j57lz8!1쾛dL3|ɠxB *+"Jd\v-#0!A˰٦K9r@ǵ;MtQWXpI"#P<}=Z3GactY2pyHt>7ifi ~F[P/Pzfλw>}Kk3Eu2_4J?zoowRKte{1j@~h$!l&T0& yMۻ'4dٺԣ(w_ߝAN\ZV)7>yQ \8L8^>LFy$}CoًX 8v_T-^gywuni$;0h?LfKq ICfSv֯tJjlЇg' HCe {CBUZvᄏwrkq=ߒѻ@FސR S]]Ԍ}B͛Q"^/74,`~=#q2D$'w]|Z}_c#VI xp$~δۢBtsf}HLO`H-+N {$ϭ=yfKg3 C<30Kl)Ԏ lɂRDp).xw1Aoӓ m7R\,8.R >k;*'ű@s-".A[:::9"*IFdGv8٘T-MvnBJU( JN!:Le M<ݩ~9S7@Yt7 3Goye)Tʜ(#`b\Pn|Dż%EcEgȨZ!KM ΘM6/ Q jKZzEwgM|T<'=yjP\aP?¿Kw\E V 4Z0vUs"o77p #K0aCz ]FR=Ț=3/=?TRěBᜢvŬ\2ŭB~3.\ջAr@ hrW'we{|M_M'$t 1ͤU^*ۅ(}U]G;p:p ~G.N}c'x< \,d}_8$2Q$x\ DF$l ҮjkLNbj(rGp43rnQjM _ Ftjl_m.&I2 $G9;maS~w_@^K-`&=n1RFo.l*&v2ZihbRBTvYЩRqvMT\Ym35n\;SN}Z!8矑ek`Lh+L"]7ҫ >=rbV M;ͳ5{ڒ,fk>_*?QЂ={)#|n Ժ]m캊y91Ey#D[|父?Z0LG@=qY=MK5}mzgBߛߓhayޞH3_;Ug #peaٙV۲zlϧnfuӱB:L]%RvCGLr(KRPΘIVA3һ/O2p~^h$ |a1ɪ:/ecO' z>v+y,djSH wn̘?ڄY$s7T_dR/ goځa/%cN#trմ:=5kا*uuʩI i7 lk:&rpMl$6M4ӋX+hMD$|[6X\s4&G' n!6E"q1lߩnz "&X;]54Q&Lƕ} kѧ@qxbfEAçb,xdo0>[+B6Z݉[j~p/O[T(A6If_F1uM8S@B( R&-4ż[/Kc5#+L Dv f F$.J)ԌH& JX]t|E r @gjt٧ 'M@TJV1P$:J%4f[A@jwLc8nW5ߜ-$$D LH  &Ny uHrnv;(Od%,$QIMHc׭a)bʖ~CZo#g lPf|#vvҀ\& C&Ve4> D ' ~z H=@ 8TsGlJ1p*?*QP 9^rTVW'ұeUmA84WSǷ]8_w?%F_ۚN u!56U jl`3AZK&_[u['aƣDn8_.xP/OgcsTȧ<ȓapƾw1N&v @)mI@$3;X߹z=6|ݴOc=b!rĒlUN RACn|`-LbᣙULBr9ɫdBzcR`P5RN)؏7f|q>>ݥuNՙQxRN+ q ^ qYw 8aGc9t`4ӓftHv@З 4YE8yޚ)6KQu-=ѼZJAŊ$T¡YI etC~^JT~n~Xr<>gw3aP.Ѵt8WdwNZn;nmFj>.'Zw.ߍhpfݼ@:o ?"B_0n[Ӊ,dŘmbʰ$*vcӱkJ(,ZFN͋AHD:jF,? / ^RC2לΨ<^7)Ҏo0꣼,5:2FUfAοEgYLbg;GMrCH`y^ >>6Lzy] rG$Du:l.\+vH$'qYDNU~cgR3ۘƻkd"Yl-?#\7[4^A^2ijFoX4ꤜA |޷muOo}Mv;ݴ7ѩv&m"L9S D| P[zž`^P*1:uUHKӊ"'.ʊb8b|vk)mIF{L <&£1KÛ>X$Cdzdi[ݬ~;>il]FPWPx2?`+(gC v+{YmWVVC^F}X?&: Ǹ0n;FX[h$s_^}bW?R7ͱ ew KIO  $Q/Nɞ`c/0.z!Pi1Z@BϿͥL'Ŕ#uAiR`Jx;P8N4zoRNpp# Ayh #$PgğH?Y!3O!O|ܽ75?~|(~M  FHޢ ?unn91v0toӞˋפ${|Kmk8.V6xYǞ3%TM8?c='v>~L7W]Գ*>J=q̣ ^ص ~ȡWx< v;Q$gsᐌزQᢌM&h úp LxBv.{Nl^]$7uK}Z6c٬*M|=-uf#piCbcs8#Kt34ѽ3h6t7N_` @O,K|>oОֽu{ܝ}Y@6_jJh;V&:mPjjV'ƒJ7{z^nAvӪj7ؘ584nlj8h>t[zx *7RT,j1=YƧ}MKp8qDEÙBfy -;L$z۔'S/~ߊR4ЙOQ ~Mޯ_ǓplBNa[x@~n|%w>ƮրSؑEC^}X 'OHέCUUT&*2e"1&d.}`^Iz-7 '3H_Bx0;^I3+~2|.`IYSʥ \A"KqnD'ܡKAvԟp\R+ M \ff_l0R P'igM8&n2nex JfTZȅøa3;Gɣ#>۞fk#13a' d.pJxw 2O>#v8a̝nBK3s$gU!DQȅZF]³<# 3$eX0<+ȯq>Ƣ)Vɱ%A#8M$!plGW*nj-RP%(?3O5pv/2KhR%~6>F̞=GA LL2U184Y! 9`,Ճ,ۚKgHtb8.`sM!@XLi `ٴ(Ѫn&VN/-"LP o[G 0*}AIv[/ysw60auH5FUyN0s`WLm!{Víq@sx0e\3EnL#RHR {.1в:nG@"ZMD>t70nFi3K,71ںinlA(.AJ\5r&u#B#v뭠Ka*g?ʔ:|`(:O`5ޤdk(N4 @^T|Kϴ6VҪG/I&:=TaI& 9 ~"'Ɲ#+ֺ$HdʺY[fc +JEAh$ "pWՓ8ڙuNY~KU2B ^>̫Ġs>#>l33X^ M.w8Gf&*5mÙR>C0yӵ4+I2}']K/Wru_) nYt~=1lJX瀚2CNE(,b9-.h>J9=9=p뇔D,bbTtHtZgc߯ڀMAȮˢPJX4{/U"kFaH~A%z[ @wm9'A3t>D[~g/ȷSqz<˵8| xPBq%E9%k@9,"+"07,L%$:ˎo'*߄;όۧhb:W+7^e6#JS*r瞒B[="U*\',Ȑ Ir".VoA:b5h&:E],uO4/' )ͦLL&St)ݐ IITc*üpiS-_Cg rs0%2bmߊX?"Jfu~/)bfd㣻ub}k6BKt}5_,hR$^.s9 wИ{aBҕDq6~EG!`J9Zm |A^w Bx}j~e_.Ggtbl6$G޴L@?}9|GlT!|' >ç_\a+%@In Sqt=-XG'Y*#f/{= PCA;5Y9rAYCQqwj/gUX|an=Gջ|Brg`~yd, Ms:/I{Ίx`rfr?&D Ud{k"`$@S&"R)zi߈qwv"qRP>']]6t'A>B AJQ ݊Jmb1=*-$b1(Ż{ iyJk:.mz' 6lzV%uGLW q=.,i41[Pxp]A܋BjjZ. ͇1OTOfEqZ(]tն!k\ᔂSŎ'?{\ N|2# ?]ga-Mu0/{DT׬QZŲ½b Kybpfӏcw|hrU+Mq.-Q9k= vF,ddI`&RHN;W>N6o5/羽#w̩ 7+\gGf9yCx4/ݕ˦ e6JZ%ꡯ/UY۫tk޵LSP\j_œ-\ Zzȹz^ltVخ|PKzYQMij,2..\+( avz"Xf]<׹QxznsB8pRU 4Mg?7*6(ݶ>=e)D{KA[*f4(ބeX\ycx:`N$'6B(5h+?(;e(N_'-dg|bc͏Ğ;r.?=L,;[#;Fe]Rr( <n:PmrvCXjYZ̓ +\{~xZ].À'K~dɻtrh)ht DX Sy B&0f tݤ\ ?.\nOl5=ň=otRJqJH.uINU7) oR uR݊cX, 3Pҋ)6[Ŷ;=pwb|R;%1XM/ʔ8)T˸)TNbv~.4ɥtSJqs)IOU˞BAOqL޶J@6uB<7C2._H]Qav^p{tA$dHo/}}t9uoy0eH&sp+Fž#*M2ڼ5*{lh_(% +xd)ĥ&nɮI筓]~?=T(oR`qRFݞayD(&T,ÏO6+T&p7FbBWRh ^9`ed4NuåPT!Pwdb(Q2E*Z˛oh* ФPb]Q,F;R5FK2LoyƪPGl;r%RQyD" u>GOUt;Yb+`ʉK7mubQH)ɻPxb!"0fy0SwFr0K:|mq|![ncVtUs!*ʵu z]Si+cvW_|i]hiH\#W {=dZ߅'R`Z|kJ7:DXxT۫v2V+Ѧ3Dϭr rQ1@+77~,@Zs{al~&QGO7q 6Y't=/%A *KnR& -jrk^@9!Ե^SJq)BE=cbdtJuItV̈́r*hQ/5VA,:QLFBi $[=mmAr8Ȅ<=$8O^m*bsdNJ`u Z_#5׵īn|D]W?SgW#n㿡)e:+iYR A^ {dyIhN2[&XG&^Xs%(ÎЉߔW_&2WD&{v D~WO0`d'n,wݤݕujl[nKc0~XHfb֭xFTc/LN4q50,oƁm"IEέLZ1g~"MY̞]Lo=>Q\*`I:Lo%"@]*γ2@avFZ'rlƪMj穜!Ùcɔc:D7fUciƬhotNZ+f*ŊN'fJB4b6aЇu7r6e0.Q4a6Wutԃcd,RC f(`f YbD P(%W7q'G#Q~W ]Sf>"UB.mj/s*)[*/AR+O*]M]yb:K#Kj-+SeKiehSRQb٤4Q1QeU-`Y^N)Noe芠z:+{*g5ۍ}Wec_amUֿ%eUBWS\#Ti$9M IU6B5efwLK1%Q/UPKuT&D(qcy2J](Qk̍9UDHr=T'e@rQBTXo켕ʧlO2MRKi '{,vQT73M.HdQdP\ .LL]2S2 1#24LV~4ߟjb(Ȃn{͒ RA'(V ~̔JYRSʐAuªE;3#Zɦ.k.~1rȿ'lԣV^ 2}奨/X)ʿ;E\H;/+J[ߠ LS[]` :.V8HX\j ٔySv u9]Qt #C:< @1[A6 u C坏;vJueJf}EVz0C pd>.%ox]xhn$R0k|%ɵtL6zU]o(P{K9#7=y*ozH$~SI^fRռ@/R cC-|KіdooƥGJ֫h9Gjf]6WT+9ۭхԪޝ m״}љqmx"1!9w(̇ 62];·ɿy(D.7"}TM'S*%\}XA[|}VӘQ~Wo]&rx[+;D7NbBԸť ,y_&{ou ד|qXϗ+Gz˵qWGW1‰}vd/Dҳ:[xO! ^'*.mAzpa_x#SR륪HRxZzX^2i/ `vЕa2Mg>Y?V\Q$['RknͼaR"Wx3Gψ:RkTnUqR5^حu,Ug|[jk;XTS%џ[TV^ 5+yEx6.1J<nmxu"<{[~;#os wGus}w62UY}f܂H "{2ۚy$P^[$Z)ޘ26,KI]",;;)ҹ~1./jh M|ݎfMyIlOɟW&4˥;ѿ#֪ds7*b*c3ݱ j=7ۛn,$vSeN4g 2 fQ E]eT+·tCݚu#QPfz݌Dt[]:N?í}s€)kn͜*/(ITh#A>iS4q=K$!A4"-4)E\\T3lОU iE/\A iDPs AnQM*3nUr7$nb` 6n7@DrI"ZG;̈!J#ڽbY9"YQP@P&2#Mb87Pl#V}C|Ρ`´)*uNH8ę5~7:7vg1Sn)c+R&j)3~:(pƢbCaϴ.QYj? ך\%>VZs*Q$q-W:U+ j埝NL&0 -e^Ir UR^˅j%W Η(DX ѢVg[vJ!`3龣IX{ɫyL滯Mnۡ_QWWsZ,w jM @f:p8«8LDLdô*578;jݙT2*"3B>Oteo;mk{Ժ& 6[Rbd|\[vЉT&AkObvSv'+WrA{]oa,d{Y,)v v©7u];f'NN`{9~SYEjhAtT/T_0'c駗vYNF]$f>'X^|իj P=ш njp8dl̇Q氯cؿn߆Dq.{ 8vgx^H؄vU'@`= ǡIr˦mAP_0 tf؃nRaNG76 HlX>U{{[dKTd[lt̫N4uo?_|XM{~Mlx[~^T_&oNI>I0&n:Oj2禺)Wj}1W뫛 u'8\Og7ë tշ̦wBfbW} yu+R ~ݦk2 gcF/_pscևӻwkVx!p|4&Wmpzmt@-3G+X/wZu)w {5v3ԟG&ѵͿ@ ̡yN=T߄Z^܏\S|l{AhꟸcY$!@hrgS|q>mW4w:H r1ngE8fXQN??L>CV"Q2IjRIG|=d+֬^xͦqom;y)[M:z:o2:|LoxI21=}Mr3$_/n1ɕa5+ x>=~IPLG8cݛ TïC37㛡?fGu=n1N(CFI̭ {vh! x CvT;-/6+4=9rGxzI:ƛ)R=ͯ޾%Ս%qk~:@4Б@}Tj=OڜZ`9{#@Z]}>Dɛwjjs =ѭp9dg<.7W%>} B-+zC3=Awb>څ/`jC[6NJMkHrNY =@\YCCú Drn͆QۄmN'vqN _mVԯnIQ_fx޷muO?#Juv>II2ڟw=3_O0GVNuhn;TgCo% >RƏjrH7jpJФ:4?6ꄓ 9TX{Ir5/YE/ 1g50mЍorۣKlF^0. %z)ɜKS˭(w_\^ARCʑnIo쎮cn^_9挜2!_7E>`f`}IaAɣ|uu3pN+\CR5Xœqq_oj<\ sFH4ܱ@E_%>>$ ?S,s4`T׉ NA,8X@WﮯSJDQWoRd ]OglZ`Zm1)s{uW?} ?^"rv:Cq^5n2q|)s{Moqo}LjSr[5s (sx\}k rmAжy* N4*:q8Ei gKKX ^P7`!٠Z佉תu5`zzC8NJ9Ikmy~0m8S{ W/@6>X: z-%r jfËh o;)X^S95s])^mx*//.*bR*C*P=w9G|²R4X@uCtBUPC=&y>|݅ahxXڣnƋ8QT"HCS{8] Y1:2-^b8X8skdq HkCl,xlNO^ .OrEfKPTVT%!hCGaq~yN Bf>O%C̜+}^KܶklaK2`|(d2&Z5ܧ|&<'Y= eq̌Ky|r&4}kQ)%w:6\N'8J<щ oZ^u4߷m8v(.>f;)%œN7}Og&9|!/m.m_,8վ?wcLu ?Κ)^51Kkφa>ZBoRh!o6!lгD:2 ̆(>q:Sۉ͉ñ m;];z=*^6utq`pؽ؋pŞ3Cs' 8lTdg"mjWp tFB$/l̪H ;C_!NO6=D),J jVgY9žVk2lbQ~:͖thjN'H˚؂]+kh5ZM/[Vd 4XG|~@Ua{~h8p֨ d چ~lSA&/zޏ{\^1>Qp`kc8 %(,0_6?~/wSLx=IEUL x ~$v湁f(%`Ks/=wG&8y uӐ7J0_z٬/hݳ^W٧8p0u9cl'VܩۃV ZgLůU+ W:ab1YruGl8I~;]LJԊ2\<[M]s<6Ʀ˰^ 9?;m=A&xه,EZI % p?[+eC84C@>ãC^"8~y'Ɗ ͜wŗ-,x .@-$3@ 0 (! HIw?_EO8?zAE-qf, дF41Y"[744)_ms+ xwʧm`CE7SST6ǃ&&XH1|E'cM7%*;Z0[V4xZ~t zat:j7\usq~x?ϰАmIo= ~94?REŬ€[\UaFS{!.FĂ`ޱӲ}+8z.qIpCzN l6*co'mPS1JğgoPՑuu{hqnd1xB/bEW {I8&\mp$M\ h+r(@\'5< ǫg?@yșP0zXpg -+Ya 7RNFF yA\ `M8cPtB nXsł&+"Ƿ=\{/`HZ,.aK0p3_5>dž} e'rA-̻yk3iqa+!X2åA(;m-SɆaR'A>p  bO/` }AkG, 9&>W'W<>δc$UdGM_dT06nYc;8l 5LoTf uQqpn##ͼ(I}b) .CqK⊧B& $3: 8hX%d{p?h\Bdf:tܣr;bc|>Þ_ JHJs"m=[? *6`CmGoB)( <}C$@^2q(- v7`kXhVRЦ>H!F%$)IWc#Ru~x3vKa0y4d^- ?4>XENڈFT%22EZlIҿpR0l Zq\ϱE$sC7 ь#؞ܶͼ,o~{nbZ禡rLjY _w"^ +Q ;W91"ZT'G -/2 B7NBp6?6nf[sUrVW 7y``As;|QZ !lb#Abm_$/f8 :Vf71n~5fIr6[-08a˓7'kGqD#y ?Wtn '1{?Xc81{nY'.CW3}bF~{ȊΒժ>Җy`i?Frfp"wy,.s$$[FtضN> ų7C_({2~)3z_shI|>xNutabhЛ +&e,l51Ғ ʈ>+~k#9z;y .'+SXg [mCOfPs~ɫZ QWdr8 [8W)/@s@W$o&*V/%-cQ&H(7W7si] J6%ƬIsx*:v(4_>Ii'p"vD9}.dGnVp@6&vU颜km:@oh0\|xجzd;Fv4'g N\(`zs;.`gt>&p<gMlX Ⲇ6&WlDm!_$93Z˵W&NDȗ2e@?Ս?1BH(|e_7O>.EC@=%Fۃm'I 9'uxcj\Bw>lq$.^=Ф L9j2e-/wkؽK*%C}yf&AEO>F}*XDI_pr':`LVA#Q^(,9Βt!q5^]W]YҖ^(3`1(@MFAZף0%W "lSFgAV*:H,Pv2_7b&|<׌ emwK CYn2M4v>{2-HBR[uӟ%;t3)`Jy9*͢t]Ă/>:K:;wb矁(Ń*3Cg8(" )GxuN E0(O;%eV('C8nnOMoyZv҅0骛zU_WW7БԉsW7W(C_&)&|rZxo ]X"Euӷ4jz jhaLY5n'O?^%!]|]8y3Lv3X^o Qaۑ"T'U0:he":xc$[B'au`+_q v#%XjQg6hؤ&a~ D)X|Iߧyg25_&xۿ-[Ƈ|)_b?HU^"f @Z֑\ځo{Ft<$'i'ӳOl978.[tyvi $[S%ė_ܗ$. W \9c)שKT ;LV%0%q р8`<‡Œl7É'40FT Mg8X$7Sa&6kCR"l|EczB!L@FqF7h2\#F%$Ā2戅>9cTj2R Di?3(WAiHY@㞴"5 ߃ &y5oT(mތ&y*Db﫠&~ڐr=i*W74)% agbjQ̣4Ҭb`utByܜ\/wD9l1\SUEŬI˛Ub!qܩHvbzjd0SX Yo:3WIRaf6*}#ҁyuwKJJ$\#PgU?]'8^Hq/ >9_JBv5I7׷#_`Nw7D$w0@mG Q#kpDa` 7)& `eFP8Mn$K7*l6nGIQؗqڙEʝA i(!d#*3TV*ќMn(g@ZH=Pp!Zi$&,"h&UfvVSvs0ܺj6ӈ_5Yu1I XVP@>s&Il@bxnExuzE+Mn]&Z%ᨁߍ`J t1<%s.y5w)v?8-e M]>ɅE7>SlUsw{l$'s$ { F*%bPQY&!1( sJp+ C;Դ$:t8,aϾ -M@v.c *)I?Yր0d l2Q ,Up6r]3Y}HZ hc}V@W̜]!} D7|=P/9['Oe+Fcýb,̿o0ꨄgf>E~ŭQ*WX?}rj|\Ĝ8XI0I, fH _nsnd<;mO?& 8D M}"oAoê #4 ahW+\GH/K*tF p.ý$45[FL s5e>Oi\t1I9Y{BX,2=VUPCq,%Qtu"#7sR^+] VKȄ]z;Hw2j+&)f!JĢ(ؕrfH6 *ЫB('1ዶ%c,`]:{KgbdinxvH0 %K "јnN>6͘0C/ȘIB^2~ El2$[mS@W ~n\pKx!HV\c,>0K [AV6|Q`p4W6䏶Y1?#?w$.~V_q0Go}x 0bhs2^$߰ᣩ f|8ra'g|^BX"(yK!?guҊNIӮ:Ӯ:RbZi|`?-Iͪ5:;4ƔM}8xg<hvmFvEDYf&IKίR0u.MOmZrt 3|,; &5C@\Yt82M_Q^>l:}KQhЋ6%q&1k\^b g_l? PzイZgDs6{ݷ_ydžkg\i]TUu_? /=|+ =uX4Ï[:.ݣާ#<|xw~MHk^D\e;SY~383z ~U >\D=Fj@^o LT&աi^ #U&ǰlv{FOb\3s> /Dp pfmk t+n}\^pB#i]޳ĤKN?piK_E$<?Rܦawa\*^4B4_\m|Ɯ^a̓j}:[^WN. $iC)/&%P\S6^kP,֚}eJ2>Js{"4jB}i4CA]I uEA` 0{g7YkUӣ6؛ф߿N?TsC;<6~r n/QZ7j` oNS(쿕iee ٕH)5_:_)Madagz>k@;&طc}*2LR%.q7}򆬐Ý9f. .ʂEPk~<{'ILRIwt]*ǚπ_XK9 5j$U2tS*4#i(86[7Y)yv\Zb6F`e76TRY\ڶzUc /50ƑJ:v <>cA6Q[1 q\U K$M@NŀY7 ކ؜jKzjt* ɭcTPNM&qL\vt'>_I\PF枡z)z/HTb8* FC U5C5.+QL6ڇTdcWIz׿KG:(V %}rnu*hX1QqNkYpoi0KF$P*2zi(/yCUSOc :<|nwơrϛ){pIƳ kg1S5RrALd7Gý@qeZ"sLQWBC4U-Mg_X0OOAk: isuo+g1y@oA2uo d"+j'҂3:u+M̀.#Dǥ 2? (yӜ˭+{FdWյ?t ̀zO@=w,ҵ(Sͩ /E{7I$6bj~۴?'k_OI@6+f(ifxNYQո*@fro+Ŋ= WY4WVe( umTs8;mK%zP3xtH^-RHlG I!8)?+#݋nѣ_èa#yej~[(/l`n~].Aiy ϫFq_A0dGݐQY:< xl0{G/N%,R abO]) l,uY ?+VГN@%J'-dOF}"%ꆎ_Eۇ~i~sݣBS =NlLvAOý>}q' (}\xϪw{k@ؔz=C_͇NSyjx`6nx  zDc"^sHo%vn+0Gף#xJH0 I 0H^@{z1 Pp@W%k34Ӂї3 R'biѬW.TOe(E?KH2 z]ݍ^ARM' Sj6rAK˞ĘUb5yr k.Mq`F-=.Q!ײ=d6-)G!DBǫ&k35NroB2Pbؿ„FGILM#SrvD>]ci:ߖ}džb+-G=*&H.T;.͠!ED+S$HB뼅Bé8ƱF{'( 6 殉̀ԚwxcbZϞ#~82Cmtn>qFDܣ?{;Jy͎C'b J Lo%d Q^_?kţzga@0 Mx@q50#{4lӯ2#WQ俞g4䷆.Pp-W2mj#b癟>U%Qlen0*! x;< N- Pݮ.TyOr1GRLp=pcjdNͣ$0ԎcN pB~oV¾fr** C^{1*㔚=.1S_kvop =_ɼ՟kuXi«KbFHB\MP/}S=RaKe'NQVW6ꋅ,#wQ hЛ=OJ{׬épD^$8mֱ6Uf˱DF͒mb-טR%Dm|\T5pߴxߨ\eĥv5?~4qN &"hO}Px<čX2fom':Β5u*rܖ; l 05I|@ %g~_cr mfZ ] d)Jߋ^"ra%(щ,ugP:p>Et=>ʒ)޺^ P^z<5bGӆt8@c*P."}57=9zd{&hEaK>@&kЂ>Puuuڦte|vbs4Yfa!A6`TW@yB/ܯx8E^ꗦpiS#sͮ!/6ci MQk9[: ]_]FY,ª:Y*%G+ Q&z9W͵ru2)i Ny>L@7dt2ӟUTDP@6OB!YV@yt BVOD%Jc̔GW<>~p@2-BC \Ê+}aɨnZJie`PlnC:I ΆYˢ{}5Ojj)u$6ɚv C ہ )H>p̠?áfI!pGQ54pܦcnDgbK٤*)9NT&:*,.,GAR+"x$!L(t&NI[e;m(d*XqڵwkM3ri 2M^qaI$ԡ&0f[@$*w~..L_ .mà ]MsƁpw31lO4^G&Sto{Cӵ} -wX޷16, uP(>C~S[#0aHF@ sq42KQ( L5&*քݣwp|4a"v} DݰnqjB 5+} ArPAy8ƓD|$7tlM-U{ zzS*b畋Gd9/84sBتIG! 9c>!?jki5v]VR4OudȢ#Yqw'Hocł:@ LJ՞ܱ,xlGea0]jsPĦryW!C +3t|-vw{v޽o9p>i,ІVXNcEPs7Qp(:NSO7#3Ñn)sAz*,j FY X骃R HTmT@D%GUg eVIS("WT_+&BTP!a9`)&UF߰p36ukۑ%AAE@@QPN5T9v7}=(Ϋ Yb=[9@Mv}I',YpO/˒|!B$(O_.©BV{fF2930|3ͤlM,_ϧ:6HT/Tt/fkOHC;ö F'Lp[K|Zؿ;>uN=$_/Q ީO[6GQJB6& >hV}08!w4+ rqSǾ%]YDӾHJ":f-W?E߇UKqOm{^gtaSW[g ϱ-%{Ь (⣰¯␢8pmk_5(wr#Į*h+5 DoR9/~\~U?H$QxQU5?H-^I"^ƽ7^r"}ƷgwIHa(?wӓ2yvf흙ye3)UtJ/)be%:^ ]5.-X,3%}$}@X¼P8i,F${?i }|Ѓc#Pe <9H-Zμ+Þtѻ06}(+)![of%Ĥ5b>u r.P[b\1NDIx%B-̗.s10NniPXVN峋E3HohQX2(]$51[Non&].d0gR]Bjwr=Iɯ(c ]. ˻p֘d?~o" BY7f:cF!F&R,U4,K*mo57oܒ%fdhNK"bZ4 1-$FfEy2|`ٙ@ mE)vIqA3KVTSRDVkkʢvJ%6Oۼa݂E7.0e`8$ gU퐊/(k/xln^ .v:KLU&_3^͓fZ"TB|>l6ZAAF"XIat2@6  UY. EPQd=hW 2sDj]vRN"v d VBjЩw 97%ߩU5l#ȰlR3U֔u!X˺}BҜ8Jϖ?ps<9.Jv~fU adOL(39 >(L8NB9HIߘS,ڧDSyCacry\R9=/c_]l|cyDoT/)RXP^W /@#lf"RfRrQ\GqyNR8%y6i6n]mfTZV,9z୪3[)`HJVIb%NVPMJVȚ\Ii 3Єƒ9LJ;L:ja{`B 0#ȷPMBo4kMm =v#+g^?7sͪNv,AT/풭zZHzfss9³֪XdMyoəze:٭,eBK0]׳N:;]AG:~{KV^`]]طYa%٤:la9QfbOtVP ZՒfGCX˵U pZ`RP\,|kLunQ%j.ȗQ(~_BKQsQ,8oXJ%4Ao ̅FEpA^TEQ;REQe-&B$BU0Jr"?EX%-=MTt򝮻))Lә"er1LZyw[M1fl}X*eT<3dP%y̬Xde)LgQ, ΅-US2L2h X-0Yu)TF2$x$E'ԤZ.0)LV2ZPDb_p/L.2HHd:s(mӝ2ǩc BEb;F};Q1pLLy*SQd,srDֲ^ x!kےJF )J>ɨhR=Ci+!2eMRaQb 5\<4Iٵ!Ӯ e`vH G:8XB3h,ψJ52=#Ԡ=#"]gDZ,ψyF(K^u9r"B-'"It$BUvR_b/FO}1t7ҼR7"6XdݟiVq=뭱]Pʹ#ߪܹC;XsGJ'5J}Y[X!FFId9g|t,ɷıD7.q˷F+(el;  o'A):MO<]RrD"OX^5-ba3p"u+Q]8{M^XpQ uzS?[l+JVdQGF$cLDVń[T]Ry/r-4T&d>R)#eQ:Ѥy" 8jaoUv 떥p1L^4sH* 0Ea&ʹQCﰺGѭn>q\շKyz7~oGO!8LG̞!@N`5<-wjE{DlbaDԏx0 wpĚ_) n!;|vI wj6Au5_Ga2b8`z3|@Ӥ#7IIFWa5]yu;.c|[ ބM$Y2u ҥRئ"xbZDOF w)!]W{Yͫ1n`wthZl *T^̤C^#>(('J ?y_D‘Jgˊ<=.y{M2Xه_<mzW ĻBYAU2gea\@6nyg5r'ݣ;櫋ez79f?SxnKhNpwV/zRt"Yr9Y_nl i>(@>߫eNMw\$xT[*l ~lcGQG6{V( yĺ[I֊'ijRj&[]q<hyVEW rlC['xC;r<+1+ثۍLA+QӯwVD7wsR |I2'p(H5SF-2FkGTTbC&EobHZvujp mQZ-7 N MR4O-t(a=bYNF;l]qGʛ='azֵZz~b&9smoy:ջ# N¾4>GT|~ ?wx#M6 V X~=d ռs ,a5 lژx Af^c|u#uPsÎx@C` r$߄ ?Pϧ Ľu?N^Q,?WQF}(iݺS.!Q gB&фĩN}s/ϧ 0)60^/[|7|Ċ%7jNu MS|H^gZg !3S|0R1T:1` mS(JzA$kJB0TZ2C7{ 4_x"x/lXIXP0iYF: ͱ=~@*̌JgNtF| l{qufQ v8H@{6~&h:HXP;Lu8ع$YϛЯ7$/@_?\MG htOh,_4E!Dp<,)b7%hOZ-UO'?eKd:Iznnvu$ )öMU ΂`k(LF Z0]f)Sy:n2' aAX-!ufֆcו ͶÜ.ǟ48)<@81속)[-qYab<+(Ì&;2VuI67wrdYlZh V U}̡ti׺~vF!*A#f͵v>AV|ɵq0<9ݼH=ogHnP'4UxoQfimNugt zQ}q22w2sv 9P޺A]dF\=7r[XRyp6i+XS^ͩNikMO, oGUYdxs8Ve>ɏO r#40[2˘@UtxQmӡO1:"I<׬ut&Gu+ ȆO'su(Զ=ngRmڪ>*,K:+)VtRB=7a2dNOBB Ϙ]N$1oD/pG+WFۻl`f9AB2:H,pHƋxOvUVW©>8Ǩ$GLN}h6)<AvۜjM|dxhj&@#s@ sI` 8P,^27̍2=~F9q}8BSm܄.gB1 T\Ha,MshP-?`{?k8 _=2_mk,ᠧGᤦYr7w%, T'!iw!os M>Wd~ }: r?{@WnP]Zt?gy9AO`o&c/bhs3MpdzE I?J_<~,> N ǜU8MB%o&_ J{(~/BDR// NĞa+!~/w?{ ?q:p=3az2L07D yF,Z w¯9Is5T/LxlXQN`wNgWz<<[ĤX^yX6u Jj}u _.Hh}Vn|'7"웇%zl3VNow~e!o]twDw^ż\޼gp8H~s$jkXaC[VhP*_0 +09!n}{DԇZ7OظG*V hqCZ>/7@^h"1O h[nvX)B%$2=}4n i)WOL9bdDž5$#zRJ4 Gw nHHz+3ȃAqKJ?Hf!Ah[@p@8yVN%Xln[G&6@G}vu€o랁 "v\: ;5|-ƽ=js~Ձd}}k{MU/y])Bx=Ư޹pMsZLmo,ׯ>ڧ%zyR yO&#JҋZ\e޼ViOp[3~_9HAd:oyd*[=K0& if0mGƟ4#B ,ʞӒ Ԕ>./Z7 \=Gp_w*|#tԱCvU=`%6u12Џ2 T4Vvճ#I~mby~n7l{h=u Cԙs SD49c":0d pIѝH6 4ܒwWr@h%k |<'0O 8Eot!{ ar 86[ۡ vacs;'IȎǥ)ɽ"kT)X˜Ym7)_pgnj^s¨`54duLA<5|`EFv] v2 A;w)Y%ō+^'w .c6k{dvKح#)# ؛yMҳ5Jk<_1S(f0f>踫`Fb'ank //2V=aSsԻz߲Ws>'[C(\V?7BQ AƜ(/}`<)k7Տ?7kެ~,:z_|(ni-0D^O,T]7oܼ%GG?a VpNCs+6{=<즆PVU,+!Vi lPABHdIJ+NCea'[C.w~{VIX?WVJxCC>iX_MQk=RyodO=4 C"Ǐ:+VpN!9n<}e+I"xx(E:7`:oXBd8 %<0\ &R-:ݟ9Lx *MAPyd( p;=JBmx@ħX6pZX?@ "ŀ 8,+jaQgTݿ#rWM4MWH[;32KlG/Rh0^ÜutyFGͻk8nw $t;OM+A m!%{ϟ2Ùp>Q)V7ZN}~]4fOFAԧ_ GЎj8'AO@~~~gq@6d Wݯ_?66,̌X4|SӍ up?sdɬdrGgolang-github-cilium-ebpf-0.11.0/btf/traversal.go000066400000000000000000000064711456504511400216340ustar00rootroot00000000000000package btf import ( "fmt" "github.com/cilium/ebpf/internal" ) // Functions to traverse a cyclic graph of types. The below was very useful: // https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order type postorderIterator struct { // Iteration skips types for which this function returns true. skip func(Type) bool // The root type. May be nil if skip(root) is true. root Type // Contains types which need to be either walked or yielded. types typeDeque // Contains a boolean whether the type has been walked or not. walked internal.Deque[bool] // The set of types which has been pushed onto types. pushed map[Type]struct{} // The current type. Only valid after a call to Next(). Type Type } // postorderTraversal iterates all types reachable from root by visiting the // leaves of the graph first. // // Types for which skip returns true are ignored. skip may be nil. func postorderTraversal(root Type, skip func(Type) (skip bool)) postorderIterator { // Avoid allocations for the common case of a skipped root. if skip != nil && skip(root) { return postorderIterator{} } po := postorderIterator{root: root, skip: skip} walkType(root, po.push) return po } func (po *postorderIterator) push(t *Type) { if _, ok := po.pushed[*t]; ok || *t == po.root { return } if po.skip != nil && po.skip(*t) { return } if po.pushed == nil { // Lazily allocate pushed to avoid an allocation for Types without children. po.pushed = make(map[Type]struct{}) } po.pushed[*t] = struct{}{} po.types.Push(t) po.walked.Push(false) } // Next returns true if there is another Type to traverse. func (po *postorderIterator) Next() bool { for !po.types.Empty() { t := po.types.Pop() if !po.walked.Pop() { // Push the type again, so that we re-evaluate it in done state // after all children have been handled. po.types.Push(t) po.walked.Push(true) // Add all direct children to todo. walkType(*t, po.push) } else { // We've walked this type previously, so we now know that all // children have been handled. po.Type = *t return true } } // Only return root once. po.Type, po.root = po.root, nil return po.Type != nil } // walkType calls fn on each child of typ. func walkType(typ Type, fn func(*Type)) { // Explicitly type switch on the most common types to allow the inliner to // do its work. This avoids allocating intermediate slices from walk() on // the heap. switch v := typ.(type) { case *Void, *Int, *Enum, *Fwd, *Float: // No children to traverse. case *Pointer: fn(&v.Target) case *Array: fn(&v.Index) fn(&v.Type) case *Struct: for i := range v.Members { fn(&v.Members[i].Type) } case *Union: for i := range v.Members { fn(&v.Members[i].Type) } case *Typedef: fn(&v.Type) case *Volatile: fn(&v.Type) case *Const: fn(&v.Type) case *Restrict: fn(&v.Type) case *Func: fn(&v.Type) case *FuncProto: fn(&v.Return) for i := range v.Params { fn(&v.Params[i].Type) } case *Var: fn(&v.Type) case *Datasec: for i := range v.Vars { fn(&v.Vars[i].Type) } case *declTag: fn(&v.Type) case *typeTag: fn(&v.Type) case *cycle: // cycle has children, but we ignore them deliberately. default: panic(fmt.Sprintf("don't know how to walk Type %T", v)) } } golang-github-cilium-ebpf-0.11.0/btf/traversal_test.go000066400000000000000000000040531456504511400226650ustar00rootroot00000000000000package btf import ( "fmt" "testing" qt "github.com/frankban/quicktest" ) func TestPostorderTraversal(t *testing.T) { ptr := newCyclicalType(2).(*Pointer) cst := ptr.Target.(*Const) str := cst.Type.(*Struct) t.Logf("%3v", ptr) pending := []Type{str, cst, ptr} iter := postorderTraversal(ptr, nil) for iter.Next() { qt.Assert(t, iter.Type, qt.Equals, pending[0]) pending = pending[1:] } qt.Assert(t, pending, qt.HasLen, 0) i := &Int{Name: "foo"} // i appears twice at the same nesting depth. arr := &Array{Index: i, Type: i} seen := make(map[Type]bool) iter = postorderTraversal(arr, nil) for iter.Next() { qt.Assert(t, seen[iter.Type], qt.IsFalse) seen[iter.Type] = true } qt.Assert(t, seen[arr], qt.IsTrue) qt.Assert(t, seen[i], qt.IsTrue) } func TestPostorderTraversalVmlinux(t *testing.T) { spec := vmlinuxTestdataSpec(t) typ, err := spec.AnyTypeByName("gov_update_cpu_data") if err != nil { t.Fatal(err) } for _, typ := range []Type{typ} { t.Run(fmt.Sprintf("%s", typ), func(t *testing.T) { seen := make(map[Type]bool) var last Type iter := postorderTraversal(typ, nil) for iter.Next() { if seen[iter.Type] { t.Fatalf("%s visited twice", iter.Type) } seen[iter.Type] = true last = iter.Type } if last != typ { t.Fatalf("Expected %s got %s as last type", typ, last) } walkType(typ, func(child *Type) { qt.Check(t, seen[*child], qt.IsTrue, qt.Commentf("missing child %s", *child)) }) }) } } func BenchmarkPostorderTraversal(b *testing.B) { spec := vmlinuxTestdataSpec(b) var fn *Func err := spec.TypeByName("gov_update_cpu_data", &fn) if err != nil { b.Fatal(err) } for _, test := range []struct { name string typ Type }{ {"single type", &Int{}}, {"cycle(1)", newCyclicalType(1)}, {"cycle(10)", newCyclicalType(10)}, {"gov_update_cpu_data", fn}, } { b.Logf("%10v", test.typ) b.Run(test.name, func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { iter := postorderTraversal(test.typ, nil) for iter.Next() { } } }) } } golang-github-cilium-ebpf-0.11.0/btf/types.go000066400000000000000000000700671456504511400207770ustar00rootroot00000000000000package btf import ( "errors" "fmt" "io" "math" "reflect" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) const maxTypeDepth = 32 // TypeID identifies a type in a BTF section. type TypeID = sys.TypeID // Type represents a type described by BTF. // // Identity of Type follows the [Go specification]: two Types are considered // equal if they have the same concrete type and the same dynamic value, aka // they point at the same location in memory. This means that the following // Types are considered distinct even though they have the same "shape". // // a := &Int{Size: 1} // b := &Int{Size: 1} // a != b // // [Go specification]: https://go.dev/ref/spec#Comparison_operators type Type interface { // Type can be formatted using the %s and %v verbs. %s outputs only the // identity of the type, without any detail. %v outputs additional detail. // // Use the '+' flag to include the address of the type. // // Use the width to specify how many levels of detail to output, for example // %1v will output detail for the root type and a short description of its // children. %2v would output details of the root type and its children // as well as a short description of the grandchildren. fmt.Formatter // Name of the type, empty for anonymous types and types that cannot // carry a name, like Void and Pointer. TypeName() string // Make a copy of the type, without copying Type members. copy() Type // New implementations must update walkType. } var ( _ Type = (*Int)(nil) _ Type = (*Struct)(nil) _ Type = (*Union)(nil) _ Type = (*Enum)(nil) _ Type = (*Fwd)(nil) _ Type = (*Func)(nil) _ Type = (*Typedef)(nil) _ Type = (*Var)(nil) _ Type = (*Datasec)(nil) _ Type = (*Float)(nil) _ Type = (*declTag)(nil) _ Type = (*typeTag)(nil) _ Type = (*cycle)(nil) ) // Void is the unit type of BTF. type Void struct{} func (v *Void) Format(fs fmt.State, verb rune) { formatType(fs, verb, v) } func (v *Void) TypeName() string { return "" } func (v *Void) size() uint32 { return 0 } func (v *Void) copy() Type { return (*Void)(nil) } type IntEncoding byte // Valid IntEncodings. // // These may look like they are flags, but they aren't. const ( Unsigned IntEncoding = 0 Signed IntEncoding = 1 Char IntEncoding = 2 Bool IntEncoding = 4 ) func (ie IntEncoding) String() string { switch ie { case Char: // NB: There is no way to determine signedness for char. return "char" case Bool: return "bool" case Signed: return "signed" case Unsigned: return "unsigned" default: return fmt.Sprintf("IntEncoding(%d)", byte(ie)) } } // Int is an integer of a given length. // // See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int type Int struct { Name string // The size of the integer in bytes. Size uint32 Encoding IntEncoding } func (i *Int) Format(fs fmt.State, verb rune) { formatType(fs, verb, i, i.Encoding, "size=", i.Size*8) } func (i *Int) TypeName() string { return i.Name } func (i *Int) size() uint32 { return i.Size } func (i *Int) copy() Type { cpy := *i return &cpy } // Pointer is a pointer to another type. type Pointer struct { Target Type } func (p *Pointer) Format(fs fmt.State, verb rune) { formatType(fs, verb, p, "target=", p.Target) } func (p *Pointer) TypeName() string { return "" } func (p *Pointer) size() uint32 { return 8 } func (p *Pointer) copy() Type { cpy := *p return &cpy } // Array is an array with a fixed number of elements. type Array struct { Index Type Type Type Nelems uint32 } func (arr *Array) Format(fs fmt.State, verb rune) { formatType(fs, verb, arr, "index=", arr.Index, "type=", arr.Type, "n=", arr.Nelems) } func (arr *Array) TypeName() string { return "" } func (arr *Array) copy() Type { cpy := *arr return &cpy } // Struct is a compound type of consecutive members. type Struct struct { Name string // The size of the struct including padding, in bytes Size uint32 Members []Member } func (s *Struct) Format(fs fmt.State, verb rune) { formatType(fs, verb, s, "fields=", len(s.Members)) } func (s *Struct) TypeName() string { return s.Name } func (s *Struct) size() uint32 { return s.Size } func (s *Struct) copy() Type { cpy := *s cpy.Members = copyMembers(s.Members) return &cpy } func (s *Struct) members() []Member { return s.Members } // Union is a compound type where members occupy the same memory. type Union struct { Name string // The size of the union including padding, in bytes. Size uint32 Members []Member } func (u *Union) Format(fs fmt.State, verb rune) { formatType(fs, verb, u, "fields=", len(u.Members)) } func (u *Union) TypeName() string { return u.Name } func (u *Union) size() uint32 { return u.Size } func (u *Union) copy() Type { cpy := *u cpy.Members = copyMembers(u.Members) return &cpy } func (u *Union) members() []Member { return u.Members } func copyMembers(orig []Member) []Member { cpy := make([]Member, len(orig)) copy(cpy, orig) return cpy } type composite interface { Type members() []Member } var ( _ composite = (*Struct)(nil) _ composite = (*Union)(nil) ) // A value in bits. type Bits uint32 // Bytes converts a bit value into bytes. func (b Bits) Bytes() uint32 { return uint32(b / 8) } // Member is part of a Struct or Union. // // It is not a valid Type. type Member struct { Name string Type Type Offset Bits BitfieldSize Bits } // Enum lists possible values. type Enum struct { Name string // Size of the enum value in bytes. Size uint32 // True if the values should be interpreted as signed integers. Signed bool Values []EnumValue } func (e *Enum) Format(fs fmt.State, verb rune) { formatType(fs, verb, e, "size=", e.Size, "values=", len(e.Values)) } func (e *Enum) TypeName() string { return e.Name } // EnumValue is part of an Enum // // Is is not a valid Type type EnumValue struct { Name string Value uint64 } func (e *Enum) size() uint32 { return e.Size } func (e *Enum) copy() Type { cpy := *e cpy.Values = make([]EnumValue, len(e.Values)) copy(cpy.Values, e.Values) return &cpy } // has64BitValues returns true if the Enum contains a value larger than 32 bits. // Kernels before 6.0 have enum values that overrun u32 replaced with zeroes. // // 64-bit enums have their Enum.Size attributes correctly set to 8, but if we // use the size attribute as a heuristic during BTF marshaling, we'll emit // ENUM64s to kernels that don't support them. func (e *Enum) has64BitValues() bool { for _, v := range e.Values { if v.Value > math.MaxUint32 { return true } } return false } // FwdKind is the type of forward declaration. type FwdKind int // Valid types of forward declaration. const ( FwdStruct FwdKind = iota FwdUnion ) func (fk FwdKind) String() string { switch fk { case FwdStruct: return "struct" case FwdUnion: return "union" default: return fmt.Sprintf("%T(%d)", fk, int(fk)) } } // Fwd is a forward declaration of a Type. type Fwd struct { Name string Kind FwdKind } func (f *Fwd) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, f.Kind) } func (f *Fwd) TypeName() string { return f.Name } func (f *Fwd) copy() Type { cpy := *f return &cpy } // Typedef is an alias of a Type. type Typedef struct { Name string Type Type } func (td *Typedef) Format(fs fmt.State, verb rune) { formatType(fs, verb, td, td.Type) } func (td *Typedef) TypeName() string { return td.Name } func (td *Typedef) copy() Type { cpy := *td return &cpy } // Volatile is a qualifier. type Volatile struct { Type Type } func (v *Volatile) Format(fs fmt.State, verb rune) { formatType(fs, verb, v, v.Type) } func (v *Volatile) TypeName() string { return "" } func (v *Volatile) qualify() Type { return v.Type } func (v *Volatile) copy() Type { cpy := *v return &cpy } // Const is a qualifier. type Const struct { Type Type } func (c *Const) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, c.Type) } func (c *Const) TypeName() string { return "" } func (c *Const) qualify() Type { return c.Type } func (c *Const) copy() Type { cpy := *c return &cpy } // Restrict is a qualifier. type Restrict struct { Type Type } func (r *Restrict) Format(fs fmt.State, verb rune) { formatType(fs, verb, r, r.Type) } func (r *Restrict) TypeName() string { return "" } func (r *Restrict) qualify() Type { return r.Type } func (r *Restrict) copy() Type { cpy := *r return &cpy } // Func is a function definition. type Func struct { Name string Type Type Linkage FuncLinkage } func FuncMetadata(ins *asm.Instruction) *Func { fn, _ := ins.Metadata.Get(funcInfoMeta{}).(*Func) return fn } // WithFuncMetadata adds a btf.Func to the Metadata of asm.Instruction. func WithFuncMetadata(ins asm.Instruction, fn *Func) asm.Instruction { ins.Metadata.Set(funcInfoMeta{}, fn) return ins } func (f *Func) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, f.Linkage, "proto=", f.Type) } func (f *Func) TypeName() string { return f.Name } func (f *Func) copy() Type { cpy := *f return &cpy } // FuncProto is a function declaration. type FuncProto struct { Return Type Params []FuncParam } func (fp *FuncProto) Format(fs fmt.State, verb rune) { formatType(fs, verb, fp, "args=", len(fp.Params), "return=", fp.Return) } func (fp *FuncProto) TypeName() string { return "" } func (fp *FuncProto) copy() Type { cpy := *fp cpy.Params = make([]FuncParam, len(fp.Params)) copy(cpy.Params, fp.Params) return &cpy } type FuncParam struct { Name string Type Type } // Var is a global variable. type Var struct { Name string Type Type Linkage VarLinkage } func (v *Var) Format(fs fmt.State, verb rune) { formatType(fs, verb, v, v.Linkage) } func (v *Var) TypeName() string { return v.Name } func (v *Var) copy() Type { cpy := *v return &cpy } // Datasec is a global program section containing data. type Datasec struct { Name string Size uint32 Vars []VarSecinfo } func (ds *Datasec) Format(fs fmt.State, verb rune) { formatType(fs, verb, ds) } func (ds *Datasec) TypeName() string { return ds.Name } func (ds *Datasec) size() uint32 { return ds.Size } func (ds *Datasec) copy() Type { cpy := *ds cpy.Vars = make([]VarSecinfo, len(ds.Vars)) copy(cpy.Vars, ds.Vars) return &cpy } // VarSecinfo describes variable in a Datasec. // // It is not a valid Type. type VarSecinfo struct { // Var or Func. Type Type Offset uint32 Size uint32 } // Float is a float of a given length. type Float struct { Name string // The size of the float in bytes. Size uint32 } func (f *Float) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, "size=", f.Size*8) } func (f *Float) TypeName() string { return f.Name } func (f *Float) size() uint32 { return f.Size } func (f *Float) copy() Type { cpy := *f return &cpy } // declTag associates metadata with a declaration. type declTag struct { Type Type Value string // The index this tag refers to in the target type. For composite types, // a value of -1 indicates that the tag refers to the whole type. Otherwise // it indicates which member or argument the tag applies to. Index int } func (dt *declTag) Format(fs fmt.State, verb rune) { formatType(fs, verb, dt, "type=", dt.Type, "value=", dt.Value, "index=", dt.Index) } func (dt *declTag) TypeName() string { return "" } func (dt *declTag) copy() Type { cpy := *dt return &cpy } // typeTag associates metadata with a type. type typeTag struct { Type Type Value string } func (tt *typeTag) Format(fs fmt.State, verb rune) { formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value) } func (tt *typeTag) TypeName() string { return "" } func (tt *typeTag) qualify() Type { return tt.Type } func (tt *typeTag) copy() Type { cpy := *tt return &cpy } // cycle is a type which had to be elided since it exceeded maxTypeDepth. type cycle struct { root Type } func (c *cycle) ID() TypeID { return math.MaxUint32 } func (c *cycle) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, "root=", c.root) } func (c *cycle) TypeName() string { return "" } func (c *cycle) copy() Type { cpy := *c return &cpy } type sizer interface { size() uint32 } var ( _ sizer = (*Int)(nil) _ sizer = (*Pointer)(nil) _ sizer = (*Struct)(nil) _ sizer = (*Union)(nil) _ sizer = (*Enum)(nil) _ sizer = (*Datasec)(nil) ) type qualifier interface { qualify() Type } var ( _ qualifier = (*Const)(nil) _ qualifier = (*Restrict)(nil) _ qualifier = (*Volatile)(nil) _ qualifier = (*typeTag)(nil) ) var errUnsizedType = errors.New("type is unsized") // Sizeof returns the size of a type in bytes. // // Returns an error if the size can't be computed. func Sizeof(typ Type) (int, error) { var ( n = int64(1) elem int64 ) for i := 0; i < maxTypeDepth; i++ { switch v := typ.(type) { case *Array: if n > 0 && int64(v.Nelems) > math.MaxInt64/n { return 0, fmt.Errorf("type %s: overflow", typ) } // Arrays may be of zero length, which allows // n to be zero as well. n *= int64(v.Nelems) typ = v.Type continue case sizer: elem = int64(v.size()) case *Typedef: typ = v.Type continue case qualifier: typ = v.qualify() continue default: return 0, fmt.Errorf("type %T: %w", typ, errUnsizedType) } if n > 0 && elem > math.MaxInt64/n { return 0, fmt.Errorf("type %s: overflow", typ) } size := n * elem if int64(int(size)) != size { return 0, fmt.Errorf("type %s: overflow", typ) } return int(size), nil } return 0, fmt.Errorf("type %s: exceeded type depth", typ) } // alignof returns the alignment of a type. // // Returns an error if the Type can't be aligned, like an integer with an uneven // size. Currently only supports the subset of types necessary for bitfield // relocations. func alignof(typ Type) (int, error) { var n int switch t := UnderlyingType(typ).(type) { case *Enum: n = int(t.size()) case *Int: n = int(t.Size) case *Array: return alignof(t.Type) default: return 0, fmt.Errorf("can't calculate alignment of %T", t) } if !pow(n) { return 0, fmt.Errorf("alignment value %d is not a power of two", n) } return n, nil } // pow returns true if n is a power of two. func pow(n int) bool { return n != 0 && (n&(n-1)) == 0 } // Transformer modifies a given Type and returns the result. // // For example, UnderlyingType removes any qualifiers or typedefs from a type. // See the example on Copy for how to use a transform. type Transformer func(Type) Type // Copy a Type recursively. // // typ may form a cycle. If transform is not nil, it is called with the // to be copied type, and the returned value is copied instead. func Copy(typ Type, transform Transformer) Type { copies := copier{copies: make(map[Type]Type)} copies.copy(&typ, transform) return typ } // copy a slice of Types recursively. // // See Copy for the semantics. func copyTypes(types []Type, transform Transformer) []Type { result := make([]Type, len(types)) copy(result, types) copies := copier{copies: make(map[Type]Type, len(types))} for i := range result { copies.copy(&result[i], transform) } return result } type copier struct { copies map[Type]Type work typeDeque } func (c *copier) copy(typ *Type, transform Transformer) { for t := typ; t != nil; t = c.work.Pop() { // *t is the identity of the type. if cpy := c.copies[*t]; cpy != nil { *t = cpy continue } var cpy Type if transform != nil { cpy = transform(*t).copy() } else { cpy = (*t).copy() } c.copies[*t] = cpy *t = cpy // Mark any nested types for copying. walkType(cpy, c.work.Push) } } type typeDeque = internal.Deque[*Type] // inflateRawTypes takes a list of raw btf types linked via type IDs, and turns // it into a graph of Types connected via pointers. // // If base is provided, then the raw types are considered to be of a split BTF // (e.g., a kernel module). // // Returns a slice of types indexed by TypeID. Since BTF ignores compilation // units, multiple types may share the same name. A Type may form a cyclic graph // by pointing at itself. func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable, base *Spec) ([]Type, error) { types := make([]Type, 0, len(rawTypes)+1) // +1 for Void added to base types // Void is defined to always be type ID 0, and is thus omitted from BTF. types = append(types, (*Void)(nil)) firstTypeID := TypeID(0) if base != nil { var err error firstTypeID, err = base.nextTypeID() if err != nil { return nil, err } // Split BTF doesn't contain Void. types = types[:0] } type fixupDef struct { id TypeID typ *Type } var fixups []fixupDef fixup := func(id TypeID, typ *Type) bool { if id < firstTypeID { if baseType, err := base.TypeByID(id); err == nil { *typ = baseType return true } } idx := int(id - firstTypeID) if idx < len(types) { // We've already inflated this type, fix it up immediately. *typ = types[idx] return true } fixups = append(fixups, fixupDef{id, typ}) return false } type assertion struct { id TypeID typ *Type want reflect.Type } var assertions []assertion fixupAndAssert := func(id TypeID, typ *Type, want reflect.Type) error { if !fixup(id, typ) { assertions = append(assertions, assertion{id, typ, want}) return nil } // The type has already been fixed up, check the type immediately. if reflect.TypeOf(*typ) != want { return fmt.Errorf("type ID %d: expected %s, got %T", id, want, *typ) } return nil } type bitfieldFixupDef struct { id TypeID m *Member } var ( legacyBitfields = make(map[TypeID][2]Bits) // offset, size bitfieldFixups []bitfieldFixupDef ) convertMembers := func(raw []btfMember, kindFlag bool) ([]Member, error) { // NB: The fixup below relies on pre-allocating this array to // work, since otherwise append might re-allocate members. members := make([]Member, 0, len(raw)) for i, btfMember := range raw { name, err := rawStrings.Lookup(btfMember.NameOff) if err != nil { return nil, fmt.Errorf("can't get name for member %d: %w", i, err) } members = append(members, Member{ Name: name, Offset: Bits(btfMember.Offset), }) m := &members[i] fixup(raw[i].Type, &m.Type) if kindFlag { m.BitfieldSize = Bits(btfMember.Offset >> 24) m.Offset &= 0xffffff // We ignore legacy bitfield definitions if the current composite // is a new-style bitfield. This is kind of safe since offset and // size on the type of the member must be zero if kindFlat is set // according to spec. continue } // This may be a legacy bitfield, try to fix it up. data, ok := legacyBitfields[raw[i].Type] if ok { // Bingo! m.Offset += data[0] m.BitfieldSize = data[1] continue } if m.Type != nil { // We couldn't find a legacy bitfield, but we know that the member's // type has already been inflated. Hence we know that it can't be // a legacy bitfield and there is nothing left to do. continue } // We don't have fixup data, and the type we're pointing // at hasn't been inflated yet. No choice but to defer // the fixup. bitfieldFixups = append(bitfieldFixups, bitfieldFixupDef{ raw[i].Type, m, }) } return members, nil } var declTags []*declTag for _, raw := range rawTypes { var ( id = firstTypeID + TypeID(len(types)) typ Type ) if id < firstTypeID { return nil, fmt.Errorf("no more type IDs") } name, err := rawStrings.Lookup(raw.NameOff) if err != nil { return nil, fmt.Errorf("get name for type id %d: %w", id, err) } switch raw.Kind() { case kindInt: size := raw.Size() bi := raw.data.(*btfInt) if bi.Offset() > 0 || bi.Bits().Bytes() != size { legacyBitfields[id] = [2]Bits{bi.Offset(), bi.Bits()} } typ = &Int{name, raw.Size(), bi.Encoding()} case kindPointer: ptr := &Pointer{nil} fixup(raw.Type(), &ptr.Target) typ = ptr case kindArray: btfArr := raw.data.(*btfArray) arr := &Array{nil, nil, btfArr.Nelems} fixup(btfArr.IndexType, &arr.Index) fixup(btfArr.Type, &arr.Type) typ = arr case kindStruct: members, err := convertMembers(raw.data.([]btfMember), raw.Bitfield()) if err != nil { return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } typ = &Struct{name, raw.Size(), members} case kindUnion: members, err := convertMembers(raw.data.([]btfMember), raw.Bitfield()) if err != nil { return nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } typ = &Union{name, raw.Size(), members} case kindEnum: rawvals := raw.data.([]btfEnum) vals := make([]EnumValue, 0, len(rawvals)) signed := raw.Signed() for i, btfVal := range rawvals { name, err := rawStrings.Lookup(btfVal.NameOff) if err != nil { return nil, fmt.Errorf("get name for enum value %d: %s", i, err) } value := uint64(btfVal.Val) if signed { // Sign extend values to 64 bit. value = uint64(int32(btfVal.Val)) } vals = append(vals, EnumValue{name, value}) } typ = &Enum{name, raw.Size(), signed, vals} case kindForward: typ = &Fwd{name, raw.FwdKind()} case kindTypedef: typedef := &Typedef{name, nil} fixup(raw.Type(), &typedef.Type) typ = typedef case kindVolatile: volatile := &Volatile{nil} fixup(raw.Type(), &volatile.Type) typ = volatile case kindConst: cnst := &Const{nil} fixup(raw.Type(), &cnst.Type) typ = cnst case kindRestrict: restrict := &Restrict{nil} fixup(raw.Type(), &restrict.Type) typ = restrict case kindFunc: fn := &Func{name, nil, raw.Linkage()} if err := fixupAndAssert(raw.Type(), &fn.Type, reflect.TypeOf((*FuncProto)(nil))); err != nil { return nil, err } typ = fn case kindFuncProto: rawparams := raw.data.([]btfParam) params := make([]FuncParam, 0, len(rawparams)) for i, param := range rawparams { name, err := rawStrings.Lookup(param.NameOff) if err != nil { return nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err) } params = append(params, FuncParam{ Name: name, }) } for i := range params { fixup(rawparams[i].Type, ¶ms[i].Type) } fp := &FuncProto{nil, params} fixup(raw.Type(), &fp.Return) typ = fp case kindVar: variable := raw.data.(*btfVariable) v := &Var{name, nil, VarLinkage(variable.Linkage)} fixup(raw.Type(), &v.Type) typ = v case kindDatasec: btfVars := raw.data.([]btfVarSecinfo) vars := make([]VarSecinfo, 0, len(btfVars)) for _, btfVar := range btfVars { vars = append(vars, VarSecinfo{ Offset: btfVar.Offset, Size: btfVar.Size, }) } for i := range vars { fixup(btfVars[i].Type, &vars[i].Type) } typ = &Datasec{name, raw.Size(), vars} case kindFloat: typ = &Float{name, raw.Size()} case kindDeclTag: btfIndex := raw.data.(*btfDeclTag).ComponentIdx if uint64(btfIndex) > math.MaxInt { return nil, fmt.Errorf("type id %d: index exceeds int", id) } dt := &declTag{nil, name, int(int32(btfIndex))} fixup(raw.Type(), &dt.Type) typ = dt declTags = append(declTags, dt) case kindTypeTag: tt := &typeTag{nil, name} fixup(raw.Type(), &tt.Type) typ = tt case kindEnum64: rawvals := raw.data.([]btfEnum64) vals := make([]EnumValue, 0, len(rawvals)) for i, btfVal := range rawvals { name, err := rawStrings.Lookup(btfVal.NameOff) if err != nil { return nil, fmt.Errorf("get name for enum64 value %d: %s", i, err) } value := (uint64(btfVal.ValHi32) << 32) | uint64(btfVal.ValLo32) vals = append(vals, EnumValue{name, value}) } typ = &Enum{name, raw.Size(), raw.Signed(), vals} default: return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) } types = append(types, typ) } for _, fixup := range fixups { if fixup.id < firstTypeID { return nil, fmt.Errorf("fixup for base type id %d is not expected", fixup.id) } idx := int(fixup.id - firstTypeID) if idx >= len(types) { return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id) } *fixup.typ = types[idx] } for _, bitfieldFixup := range bitfieldFixups { if bitfieldFixup.id < firstTypeID { return nil, fmt.Errorf("bitfield fixup from split to base types is not expected") } data, ok := legacyBitfields[bitfieldFixup.id] if ok { // This is indeed a legacy bitfield, fix it up. bitfieldFixup.m.Offset += data[0] bitfieldFixup.m.BitfieldSize = data[1] } } for _, assertion := range assertions { if reflect.TypeOf(*assertion.typ) != assertion.want { return nil, fmt.Errorf("type ID %d: expected %s, got %T", assertion.id, assertion.want, *assertion.typ) } } for _, dt := range declTags { switch t := dt.Type.(type) { case *Var, *Typedef: if dt.Index != -1 { return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index) } case composite: if dt.Index >= len(t.members()) { return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t) } case *Func: if dt.Index >= len(t.Type.(*FuncProto).Params) { return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t) } default: return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t) } } return types, nil } // essentialName represents the name of a BTF type stripped of any flavor // suffixes after a ___ delimiter. type essentialName string // newEssentialName returns name without a ___ suffix. // // CO-RE has the concept of 'struct flavors', which are used to deal with // changes in kernel data structures. Anything after three underscores // in a type name is ignored for the purpose of finding a candidate type // in the kernel's BTF. func newEssentialName(name string) essentialName { if name == "" { return "" } lastIdx := strings.LastIndex(name, "___") if lastIdx > 0 { return essentialName(name[:lastIdx]) } return essentialName(name) } // UnderlyingType skips qualifiers and Typedefs. func UnderlyingType(typ Type) Type { result := typ for depth := 0; depth <= maxTypeDepth; depth++ { switch v := (result).(type) { case qualifier: result = v.qualify() case *Typedef: result = v.Type default: return result } } return &cycle{typ} } // as returns typ if is of type T. Otherwise it peels qualifiers and Typedefs // until it finds a T. // // Returns the zero value and false if there is no T or if the type is nested // too deeply. func as[T Type](typ Type) (T, bool) { for depth := 0; depth <= maxTypeDepth; depth++ { switch v := (typ).(type) { case T: return v, true case qualifier: typ = v.qualify() case *Typedef: typ = v.Type default: goto notFound } } notFound: var zero T return zero, false } type formatState struct { fmt.State depth int } // formattableType is a subset of Type, to ease unit testing of formatType. type formattableType interface { fmt.Formatter TypeName() string } // formatType formats a type in a canonical form. // // Handles cyclical types by only printing cycles up to a certain depth. Elements // in extra are separated by spaces unless the preceding element is a string // ending in '='. func formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) { if verb != 'v' && verb != 's' { fmt.Fprintf(f, "{UNRECOGNIZED: %c}", verb) return } _, _ = io.WriteString(f, internal.GoTypeName(t)) if name := t.TypeName(); name != "" { // Output BTF type name if present. fmt.Fprintf(f, ":%q", name) } if f.Flag('+') { // Output address if requested. fmt.Fprintf(f, ":%#p", t) } if verb == 's' { // %s omits details. return } var depth int if ps, ok := f.(*formatState); ok { depth = ps.depth f = ps.State } maxDepth, ok := f.Width() if !ok { maxDepth = 0 } if depth > maxDepth { // We've reached the maximum depth. This avoids infinite recursion even // for cyclical types. return } if len(extra) == 0 { return } wantSpace := false _, _ = io.WriteString(f, "[") for _, arg := range extra { if wantSpace { _, _ = io.WriteString(f, " ") } switch v := arg.(type) { case string: _, _ = io.WriteString(f, v) wantSpace = len(v) > 0 && v[len(v)-1] != '=' continue case formattableType: v.Format(&formatState{f, depth + 1}, verb) default: fmt.Fprint(f, arg) } wantSpace = true } _, _ = io.WriteString(f, "]") } golang-github-cilium-ebpf-0.11.0/btf/types_test.go000066400000000000000000000262361456504511400220350ustar00rootroot00000000000000package btf import ( "bytes" "fmt" "reflect" "testing" "github.com/cilium/ebpf/internal" qt "github.com/frankban/quicktest" "github.com/google/go-cmp/cmp" ) func TestSizeof(t *testing.T) { testcases := []struct { size int typ Type }{ {0, (*Void)(nil)}, {1, &Int{Size: 1}}, {8, &Enum{Size: 8}}, {0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}}, {12, &Array{Type: &Enum{Size: 4}, Nelems: 3}}, } for _, tc := range testcases { name := fmt.Sprint(tc.typ) t.Run(name, func(t *testing.T) { have, err := Sizeof(tc.typ) if err != nil { t.Fatal("Can't calculate size:", err) } if have != tc.size { t.Errorf("Expected size %d, got %d", tc.size, have) } }) } } func TestPow(t *testing.T) { tests := []struct { n int r bool }{ {0, false}, {1, true}, {2, true}, {3, false}, {4, true}, {5, false}, {8, true}, } for _, tt := range tests { t.Run(fmt.Sprintf("%d", tt.n), func(t *testing.T) { if want, got := tt.r, pow(tt.n); want != got { t.Errorf("unexpected result for n %d; want: %v, got: %v", tt.n, want, got) } }) } } func TestCopy(t *testing.T) { _ = Copy((*Void)(nil), nil) in := &Int{Size: 4} out := Copy(in, nil) in.Size = 8 if size := out.(*Int).Size; size != 4 { t.Error("Copy doesn't make a copy, expected size 4, got", size) } t.Run("cyclical", func(t *testing.T) { _ = Copy(newCyclicalType(2), nil) }) t.Run("identity", func(t *testing.T) { u16 := &Int{Size: 2} out := Copy(&Struct{ Members: []Member{ {Name: "a", Type: u16}, {Name: "b", Type: u16}, }, }, nil) outStruct := out.(*Struct) qt.Assert(t, outStruct.Members[0].Type, qt.Equals, outStruct.Members[1].Type) }) } func TestAs(t *testing.T) { i := &Int{} ptr := &Pointer{i} td := &Typedef{Type: ptr} cst := &Const{td} vol := &Volatile{cst} // It's possible to retrieve qualifiers and Typedefs. haveVol, ok := as[*Volatile](vol) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, haveVol, qt.Equals, vol) haveTd, ok := as[*Typedef](vol) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, haveTd, qt.Equals, td) haveCst, ok := as[*Const](vol) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, haveCst, qt.Equals, cst) // Make sure we don't skip Pointer. haveI, ok := as[*Int](vol) qt.Assert(t, ok, qt.IsFalse) qt.Assert(t, haveI, qt.IsNil) // Make sure we can always retrieve Pointer. for _, typ := range []Type{ td, cst, vol, ptr, } { have, ok := as[*Pointer](typ) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, have, qt.Equals, ptr) } } func BenchmarkCopy(b *testing.B) { typ := newCyclicalType(10) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { Copy(typ, nil) } } // The following are valid Types. // // There currently is no better way to document which // types implement an interface. func ExampleType_validTypes() { var _ Type = &Void{} var _ Type = &Int{} var _ Type = &Pointer{} var _ Type = &Array{} var _ Type = &Struct{} var _ Type = &Union{} var _ Type = &Enum{} var _ Type = &Fwd{} var _ Type = &Typedef{} var _ Type = &Volatile{} var _ Type = &Const{} var _ Type = &Restrict{} var _ Type = &Func{} var _ Type = &FuncProto{} var _ Type = &Var{} var _ Type = &Datasec{} var _ Type = &Float{} } func TestType(t *testing.T) { types := []func() Type{ func() Type { return &Void{} }, func() Type { return &Int{Size: 2} }, func() Type { return &Pointer{Target: &Void{}} }, func() Type { return &Array{Type: &Int{}} }, func() Type { return &Struct{ Members: []Member{{Type: &Void{}}}, } }, func() Type { return &Union{ Members: []Member{{Type: &Void{}}}, } }, func() Type { return &Enum{} }, func() Type { return &Fwd{Name: "thunk"} }, func() Type { return &Typedef{Type: &Void{}} }, func() Type { return &Volatile{Type: &Void{}} }, func() Type { return &Const{Type: &Void{}} }, func() Type { return &Restrict{Type: &Void{}} }, func() Type { return &Func{Name: "foo", Type: &Void{}} }, func() Type { return &FuncProto{ Params: []FuncParam{{Name: "bar", Type: &Void{}}}, Return: &Void{}, } }, func() Type { return &Var{Type: &Void{}} }, func() Type { return &Datasec{ Vars: []VarSecinfo{{Type: &Void{}}}, } }, func() Type { return &Float{} }, func() Type { return &declTag{Type: &Void{}} }, func() Type { return &typeTag{Type: &Void{}} }, func() Type { return &cycle{&Void{}} }, } compareTypes := cmp.Comparer(func(a, b *Type) bool { return a == b }) for _, fn := range types { typ := fn() t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) { t.Logf("%v", typ) if typ == typ.copy() { t.Error("Copy doesn't copy") } var a []*Type walkType(typ, func(t *Type) { a = append(a, t) }) if _, ok := typ.(*cycle); !ok { if n := countChildren(t, reflect.TypeOf(typ)); len(a) < n { t.Errorf("walkType visited %d children, expected at least %d", len(a), n) } } var b []*Type walkType(typ, func(t *Type) { b = append(b, t) }) if diff := cmp.Diff(a, b, compareTypes); diff != "" { t.Errorf("Walk mismatch (-want +got):\n%s", diff) } }) } } func TestTagMarshaling(t *testing.T) { for _, typ := range []Type{ &declTag{&Struct{Members: []Member{}}, "foo", -1}, &typeTag{&Int{}, "foo"}, } { t.Run(fmt.Sprint(typ), func(t *testing.T) { buf := marshalNativeEndian(t, []Type{typ}) s, err := loadRawSpec(bytes.NewReader(buf), internal.NativeEndian, nil) qt.Assert(t, err, qt.IsNil) have, err := s.TypeByID(1) qt.Assert(t, err, qt.IsNil) qt.Assert(t, have, qt.DeepEquals, typ) }) } } func countChildren(t *testing.T, typ reflect.Type) int { if typ.Kind() != reflect.Pointer { t.Fatal("Expected pointer, got", typ.Kind()) } typ = typ.Elem() if typ.Kind() != reflect.Struct { t.Fatal("Expected struct, got", typ.Kind()) } var n int for i := 0; i < typ.NumField(); i++ { if typ.Field(i).Type == reflect.TypeOf((*Type)(nil)).Elem() { n++ } } return n } type testFormattableType struct { name string extra []interface{} } var _ formattableType = (*testFormattableType)(nil) func (tft *testFormattableType) TypeName() string { return tft.name } func (tft *testFormattableType) Format(fs fmt.State, verb rune) { formatType(fs, verb, tft, tft.extra...) } func TestFormatType(t *testing.T) { t1 := &testFormattableType{"", []interface{}{"extra"}} t1Addr := fmt.Sprintf("%#p", t1) goType := reflect.TypeOf(t1).Elem().Name() t2 := &testFormattableType{"foo", []interface{}{t1}} t3 := &testFormattableType{extra: []interface{}{""}} tests := []struct { t formattableType fmt string contains []string omits []string }{ // %s doesn't contain address or extra. {t1, "%s", []string{goType}, []string{t1Addr, "extra"}}, // %+s doesn't contain extra. {t1, "%+s", []string{goType, t1Addr}, []string{"extra"}}, // %v does contain extra. {t1, "%v", []string{goType, "extra"}, []string{t1Addr}}, // %+v does contain address. {t1, "%+v", []string{goType, "extra", t1Addr}, nil}, // %v doesn't print nested types' extra. {t2, "%v", []string{goType, t2.name}, []string{"extra"}}, // %1v does print nested types' extra. {t2, "%1v", []string{goType, t2.name, "extra"}, nil}, // empty strings in extra don't emit anything. {t3, "%v", []string{"[]"}, nil}, } for _, test := range tests { t.Run(test.fmt, func(t *testing.T) { str := fmt.Sprintf(test.fmt, test.t) t.Log(str) for _, want := range test.contains { qt.Assert(t, str, qt.Contains, want) } for _, notWant := range test.omits { qt.Assert(t, str, qt.Not(qt.Contains), notWant) } }) } } func newCyclicalType(n int) Type { ptr := &Pointer{} prev := Type(ptr) for i := 0; i < n; i++ { switch i % 5 { case 0: prev = &Struct{ Members: []Member{ {Type: prev}, }, } case 1: prev = &Const{Type: prev} case 2: prev = &Volatile{Type: prev} case 3: prev = &Typedef{Type: prev} case 4: prev = &Array{Type: prev, Index: &Int{Size: 1}} } } ptr.Target = prev return ptr } func TestUnderlyingType(t *testing.T) { wrappers := []struct { name string fn func(Type) Type }{ {"const", func(t Type) Type { return &Const{Type: t} }}, {"volatile", func(t Type) Type { return &Volatile{Type: t} }}, {"restrict", func(t Type) Type { return &Restrict{Type: t} }}, {"typedef", func(t Type) Type { return &Typedef{Type: t} }}, {"type tag", func(t Type) Type { return &typeTag{Type: t} }}, } for _, test := range wrappers { t.Run(test.name+" cycle", func(t *testing.T) { root := &Volatile{} root.Type = test.fn(root) got, ok := UnderlyingType(root).(*cycle) qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, got.root, qt.Equals, root) }) } for _, test := range wrappers { t.Run(test.name, func(t *testing.T) { want := &Int{} got := UnderlyingType(test.fn(want)) qt.Assert(t, got, qt.Equals, want) }) } } func TestInflateLegacyBitfield(t *testing.T) { const offset = 3 const size = 5 var rawInt rawType rawInt.SetKind(kindInt) rawInt.SetSize(4) var data btfInt data.SetOffset(offset) data.SetBits(size) rawInt.data = &data var beforeInt rawType beforeInt.SetKind(kindStruct) beforeInt.SetVlen(1) beforeInt.data = []btfMember{{Type: 2}} afterInt := beforeInt afterInt.data = []btfMember{{Type: 1}} emptyStrings := newStringTable("") for _, test := range []struct { name string raw []rawType }{ {"struct before int", []rawType{beforeInt, rawInt}}, {"struct after int", []rawType{rawInt, afterInt}}, } { t.Run(test.name, func(t *testing.T) { types, err := inflateRawTypes(test.raw, emptyStrings, nil) if err != nil { t.Fatal(err) } for _, typ := range types { s, ok := typ.(*Struct) if !ok { continue } i := s.Members[0] if i.BitfieldSize != size { t.Errorf("Expected bitfield size %d, got %d", size, i.BitfieldSize) } if i.Offset != offset { t.Errorf("Expected offset %d, got %d", offset, i.Offset) } return } t.Fatal("No Struct returned from inflateRawTypes") }) } } func BenchmarkWalk(b *testing.B) { types := []Type{ &Void{}, &Int{}, &Pointer{}, &Array{}, &Struct{Members: make([]Member, 2)}, &Union{Members: make([]Member, 2)}, &Enum{}, &Fwd{}, &Typedef{}, &Volatile{}, &Const{}, &Restrict{}, &Func{}, &FuncProto{Params: make([]FuncParam, 2)}, &Var{}, &Datasec{Vars: make([]VarSecinfo, 2)}, } for _, typ := range types { b.Run(fmt.Sprint(typ), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { var dq typeDeque walkType(typ, dq.Push) } }) } } func BenchmarkUnderlyingType(b *testing.B) { b.Run("no unwrapping", func(b *testing.B) { v := &Int{} b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { UnderlyingType(v) } }) b.Run("single unwrapping", func(b *testing.B) { v := &Typedef{Type: &Int{}} b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { UnderlyingType(v) } }) } // Copy can be used with UnderlyingType to strip qualifiers from a type graph. func ExampleCopy_stripQualifiers() { a := &Volatile{Type: &Pointer{Target: &Typedef{Name: "foo", Type: &Int{Size: 2}}}} b := Copy(a, UnderlyingType) // b has Volatile and Typedef removed. fmt.Printf("%3v\n", b) // Output: Pointer[target=Int[unsigned size=16]] } golang-github-cilium-ebpf-0.11.0/btf/workarounds.go000066400000000000000000000012231456504511400221750ustar00rootroot00000000000000package btf // datasecResolveWorkaround ensures that certain vars in a Datasec are added // to a Spec before the Datasec. This avoids a bug in kernel BTF validation. // // See https://lore.kernel.org/bpf/20230302123440.1193507-1-lmb@isovalent.com/ func datasecResolveWorkaround(b *Builder, ds *Datasec) error { for _, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { continue } switch v.Type.(type) { case *Typedef, *Volatile, *Const, *Restrict, *typeTag: // NB: We must never call Add on a Datasec, otherwise we risk // infinite recursion. _, err := b.Add(v.Type) if err != nil { return err } } } return nil } golang-github-cilium-ebpf-0.11.0/btf/workarounds_test.go000066400000000000000000000021771456504511400232450ustar00rootroot00000000000000package btf import ( "errors" "fmt" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestDatasecResolveWorkaround(t *testing.T) { testutils.SkipOnOldKernel(t, "5.2", "BTF_KIND_DATASEC") i := &Int{Size: 1} for _, typ := range []Type{ &Typedef{"foo", i}, &Volatile{i}, &Const{i}, &Restrict{i}, &typeTag{i, "foo"}, } { t.Run(fmt.Sprint(typ), func(t *testing.T) { if _, ok := typ.(*typeTag); ok { testutils.SkipOnOldKernel(t, "5.17", "BTF_KIND_TYPE_TAG") } ds := &Datasec{ Name: "a", Size: 2, Vars: []VarSecinfo{ { Size: 1, Offset: 0, // struct, union, pointer, array will trigger the bug. Type: &Var{Name: "a", Type: &Pointer{i}}, }, { Size: 1, Offset: 1, Type: &Var{ Name: "b", Type: typ, }, }, }, } b, err := NewBuilder([]Type{ds}) if err != nil { t.Fatal(err) } h, err := NewHandle(b) var ve *internal.VerifierError if errors.As(err, &ve) { t.Fatalf("%+v\n", ve) } if err != nil { t.Fatal(err) } h.Close() }) } } golang-github-cilium-ebpf-0.11.0/cmd/000077500000000000000000000000001456504511400172625ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/000077500000000000000000000000001456504511400204415ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/README.md000066400000000000000000000023731456504511400217250ustar00rootroot00000000000000bpf2go === `bpf2go` compiles a C source file into eBPF bytecode and then emits a Go file containing the eBPF. The goal is to avoid loading the eBPF from disk at runtime and to minimise the amount of manual work required to interact with eBPF programs. It takes inspiration from `bpftool gen skeleton`. Invoke the program using go generate: //go:generate go run github.com/cilium/ebpf/cmd/bpf2go foo path/to/src.c -- -I/path/to/include This will emit `foo_bpfel.go` and `foo_bpfeb.go`, with types using `foo` as a stem. The two files contain compiled BPF for little and big endian systems, respectively. You can use environment variables to affect all bpf2go invocations across a project, e.g. to set specific C flags: //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cflags "$BPF_CFLAGS" foo path/to/src.c By exporting `$BPF_CFLAGS` from your build system you can then control all builds from a single location. ## Generated types `bpf2go` generates Go types for all map keys and values by default. You can disable this behaviour using `-no-global-types`. You can add to the set of types by specifying `-type foo` for each type you'd like to generate. ## Examples See [examples/kprobe](../../examples/kprobe/main.go) for a fully worked out example. golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/compile.go000066400000000000000000000112451456504511400224230ustar00rootroot00000000000000package main import ( "bufio" "bytes" "errors" "fmt" "io" "os" "os/exec" "path/filepath" "strings" ) type compileArgs struct { // Which compiler to use cc string cFlags []string // Absolute working directory dir string // Absolute input file name source string // Absolute output file name dest string // Target to compile for, defaults to "bpf". target string // Depfile will be written here if depName is not empty dep io.Writer } func compile(args compileArgs) error { // Default cflags that can be overridden by args.cFlags overrideFlags := []string{ // Code needs to be optimized, otherwise the verifier will often fail // to understand it. "-O2", // Clang defaults to mcpu=probe which checks the kernel that we are // compiling on. This isn't appropriate for ahead of time // compiled code so force the most compatible version. "-mcpu=v1", } cmd := exec.Command(args.cc, append(overrideFlags, args.cFlags...)...) cmd.Stderr = os.Stderr inputDir := filepath.Dir(args.source) relInputDir, err := filepath.Rel(args.dir, inputDir) if err != nil { return err } target := args.target if target == "" { target = "bpf" } // C flags that can't be overridden. cmd.Args = append(cmd.Args, "-target", target, "-c", args.source, "-o", args.dest, // Don't include clang version "-fno-ident", // Don't output inputDir into debug info "-fdebug-prefix-map="+inputDir+"="+relInputDir, "-fdebug-compilation-dir", ".", // We always want BTF to be generated, so enforce debug symbols "-g", fmt.Sprintf("-D__BPF_TARGET_MISSING=%q", "GCC error \"The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb\""), ) cmd.Dir = args.dir var depFile *os.File if args.dep != nil { depFile, err = os.CreateTemp("", "bpf2go") if err != nil { return err } defer depFile.Close() defer os.Remove(depFile.Name()) cmd.Args = append(cmd.Args, // Output dependency information. "-MD", // Create phony targets so that deleting a dependency doesn't // break the build. "-MP", // Write it to temporary file "-MF"+depFile.Name(), ) } if err := cmd.Run(); err != nil { return fmt.Errorf("can't execute %s: %s", args.cc, err) } if depFile != nil { if _, err := io.Copy(args.dep, depFile); err != nil { return fmt.Errorf("error writing depfile: %w", err) } } return nil } func adjustDependencies(baseDir string, deps []dependency) ([]byte, error) { var buf bytes.Buffer for _, dep := range deps { relativeFile, err := filepath.Rel(baseDir, dep.file) if err != nil { return nil, err } if len(dep.prerequisites) == 0 { _, err := fmt.Fprintf(&buf, "%s:\n\n", relativeFile) if err != nil { return nil, err } continue } var prereqs []string for _, prereq := range dep.prerequisites { relativePrereq, err := filepath.Rel(baseDir, prereq) if err != nil { return nil, err } prereqs = append(prereqs, relativePrereq) } _, err = fmt.Fprintf(&buf, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n ")) if err != nil { return nil, err } } return buf.Bytes(), nil } type dependency struct { file string prerequisites []string } func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) { abs := func(path string) string { if filepath.IsAbs(path) { return path } return filepath.Join(baseDir, path) } scanner := bufio.NewScanner(in) var line strings.Builder var deps []dependency for scanner.Scan() { buf := scanner.Bytes() if line.Len()+len(buf) > 1024*1024 { return nil, errors.New("line too long") } if bytes.HasSuffix(buf, []byte{'\\'}) { line.Write(buf[:len(buf)-1]) continue } line.Write(buf) if line.Len() == 0 { // Skip empty lines continue } parts := strings.SplitN(line.String(), ":", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid line without ':'") } // NB: This doesn't handle filenames with spaces in them. // It seems like make doesn't do that either, so oh well. var prereqs []string for _, prereq := range strings.Fields(parts[1]) { prereqs = append(prereqs, abs(prereq)) } deps = append(deps, dependency{ abs(string(parts[0])), prereqs, }) line.Reset() } if err := scanner.Err(); err != nil { return nil, err } // There is always at least a dependency for the main file. if len(deps) == 0 { return nil, fmt.Errorf("empty dependency file") } return deps, nil } // strip DWARF debug info from file by executing exe. func strip(exe, file string) error { cmd := exec.Command(exe, "-g", file) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("%s: %s", exe, err) } return nil } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/compile_test.go000066400000000000000000000063341456504511400234650ustar00rootroot00000000000000package main import ( "bytes" "os" "path/filepath" "reflect" "strings" "testing" ) const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }` func TestCompile(t *testing.T) { dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) var dep bytes.Buffer err := compile(compileArgs{ cc: clangBin(t), dir: dir, source: filepath.Join(dir, "test.c"), dest: filepath.Join(dir, "test.o"), dep: &dep, }) if err != nil { t.Fatal("Can't compile:", err) } stat, err := os.Stat(filepath.Join(dir, "test.o")) if err != nil { t.Fatal("Can't stat output:", err) } if stat.Size() == 0 { t.Error("Compilation creates an empty file") } if dep.Len() == 0 { t.Error("Compilation doesn't generate depinfo") } if _, err := parseDependencies(dir, &dep); err != nil { t.Error("Can't parse dependencies:", err) } } func TestReproducibleCompile(t *testing.T) { clangBin := clangBin(t) dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) err := compile(compileArgs{ cc: clangBin, dir: dir, source: filepath.Join(dir, "test.c"), dest: filepath.Join(dir, "a.o"), }) if err != nil { t.Fatal("Can't compile:", err) } err = compile(compileArgs{ cc: clangBin, dir: dir, source: filepath.Join(dir, "test.c"), dest: filepath.Join(dir, "b.o"), }) if err != nil { t.Fatal("Can't compile:", err) } aBytes, err := os.ReadFile(filepath.Join(dir, "a.o")) if err != nil { t.Fatal(err) } bBytes, err := os.ReadFile(filepath.Join(dir, "b.o")) if err != nil { t.Fatal(err) } if !bytes.Equal(aBytes, bBytes) { t.Error("Compiling the same file twice doesn't give the same result") } } func TestTriggerMissingTarget(t *testing.T) { dir := mustWriteTempFile(t, "test.c", `_Pragma(__BPF_TARGET_MISSING);`) err := compile(compileArgs{ cc: clangBin(t), dir: dir, source: filepath.Join(dir, "test.c"), dest: filepath.Join(dir, "a.o"), }) if err == nil { t.Fatal("No error when compiling __BPF_TARGET_MISSING") } } func TestParseDependencies(t *testing.T) { const input = `main.go: /foo/bar baz frob: /gobble \ gubble nothing: ` have, err := parseDependencies("/foo", strings.NewReader(input)) if err != nil { t.Fatal("Can't parse dependencies:", err) } want := []dependency{ {"/foo/main.go", []string{"/foo/bar", "/foo/baz"}}, {"/foo/frob", []string{"/gobble", "/foo/gubble"}}, {"/foo/nothing", nil}, } if !reflect.DeepEqual(have, want) { t.Logf("Have: %#v", have) t.Logf("Want: %#v", want) t.Error("Result doesn't match") } output, err := adjustDependencies("/foo", want) if err != nil { t.Error("Can't adjust dependencies") } const wantOutput = `main.go: \ bar \ baz frob: \ ../gobble \ gubble nothing: ` if have := string(output); have != wantOutput { t.Logf("Have:\n%s", have) t.Logf("Want:\n%s", wantOutput) t.Error("Output doesn't match") } } func mustWriteTempFile(t *testing.T, name, contents string) string { t.Helper() tmp, err := os.MkdirTemp("", "bpf2go") if err != nil { t.Fatal(err) } t.Cleanup(func() { os.RemoveAll(tmp) }) tmpFile := filepath.Join(tmp, name) if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil { t.Fatal(err) } return tmp } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/doc.go000066400000000000000000000001511456504511400215320ustar00rootroot00000000000000// Program bpf2go embeds eBPF in Go. // // Please see the README for details how to use it. package main golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/flags.go000066400000000000000000000015451456504511400220710ustar00rootroot00000000000000package main import ( "flag" "go/build/constraint" ) // buildTags is a comma-separated list of build tags. // // This follows the pre-Go 1.17 syntax and is kept for compatibility reasons. type buildTags struct { Expr constraint.Expr } var _ flag.Value = (*buildTags)(nil) func (bt *buildTags) String() string { if bt.Expr == nil { return "" } return (bt.Expr).String() } func (bt *buildTags) Set(value string) error { ct, err := constraint.Parse("// +build " + value) if err != nil { return err } bt.Expr = ct return nil } func andConstraints(x, y constraint.Expr) constraint.Expr { if x == nil { return y } if y == nil { return x } return &constraint.AndExpr{X: x, Y: y} } func orConstraints(x, y constraint.Expr) constraint.Expr { if x == nil { return y } if y == nil { return x } return &constraint.OrExpr{X: x, Y: y} } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/main.go000066400000000000000000000305311456504511400217160ustar00rootroot00000000000000package main import ( "bytes" "errors" "flag" "fmt" "go/build/constraint" "go/token" "io" "os" "os/exec" "path/filepath" "regexp" "runtime" "sort" "strings" "github.com/cilium/ebpf" ) const helpText = `Usage: %[1]s [options] [-- ] ident is used as the stem of all generated Go types and functions, and must be a valid Go identifier. source is a single C file that is compiled using the specified compiler (usually some version of clang). You can pass options to the compiler by appending them after a '--' argument or by supplying -cflags. Flags passed as arguments take precedence over flags passed via -cflags. Additionally, the program expands quotation marks in -cflags. This means that -cflags 'foo "bar baz"' is passed to the compiler as two arguments "foo" and "bar baz". The program expects GOPACKAGE to be set in the environment, and should be invoked via go generate. The generated files are written to the current directory. Some options take defaults from the environment. Variable name is mentioned next to the respective option. Options: ` // Targets understood by bpf2go. // // Targets without a Linux string can't be used directly and are only included // for the generic bpf, bpfel, bpfeb targets. var targetByGoArch = map[string]target{ "386": {"bpfel", "x86"}, "amd64": {"bpfel", "x86"}, "amd64p32": {"bpfel", ""}, "arm": {"bpfel", "arm"}, "arm64": {"bpfel", "arm64"}, "loong64": {"bpfel", ""}, "mipsle": {"bpfel", ""}, "mips64le": {"bpfel", ""}, "mips64p32le": {"bpfel", ""}, "ppc64le": {"bpfel", "powerpc"}, "riscv64": {"bpfel", ""}, "armbe": {"bpfeb", "arm"}, "arm64be": {"bpfeb", "arm64"}, "mips": {"bpfeb", ""}, "mips64": {"bpfeb", ""}, "mips64p32": {"bpfeb", ""}, "ppc64": {"bpfeb", "powerpc"}, "s390": {"bpfeb", "s390"}, "s390x": {"bpfeb", "s390"}, "sparc": {"bpfeb", "sparc"}, "sparc64": {"bpfeb", "sparc"}, } func run(stdout io.Writer, pkg, outputDir string, args []string) (err error) { b2g, err := newB2G(stdout, pkg, outputDir, args) switch { case err == nil: return b2g.convertAll() case errors.Is(err, flag.ErrHelp): return nil default: return err } } type bpf2go struct { stdout io.Writer // Absolute path to a .c file. sourceFile string // Absolute path to a directory where .go are written outputDir string // Alternative output stem. If empty, identStem is used. outputStem string // Valid go package name. pkg string // Valid go identifier. identStem string // Targets to build for. targetArches map[target][]string // C compiler. cc string // Command used to strip DWARF. strip string disableStripping bool // C flags passed to the compiler. cFlags []string skipGlobalTypes bool // C types to include in the generatd output. cTypes cTypes // Build tags to be included in the output. tags buildTags // Base directory of the Makefile. Enables outputting make-style dependencies // in .d files. makeBase string } func newB2G(stdout io.Writer, pkg, outputDir string, args []string) (*bpf2go, error) { b2g := &bpf2go{ stdout: stdout, pkg: pkg, outputDir: outputDir, } fs := flag.NewFlagSet("bpf2go", flag.ContinueOnError) fs.StringVar(&b2g.cc, "cc", getEnv("BPF2GO_CC", "clang"), "`binary` used to compile C to BPF ($BPF2GO_CC)") fs.StringVar(&b2g.strip, "strip", getEnv("BPF2GO_STRIP", ""), "`binary` used to strip DWARF from compiled BPF ($BPF2GO_STRIP)") fs.BoolVar(&b2g.disableStripping, "no-strip", false, "disable stripping of DWARF") flagCFlags := fs.String("cflags", getEnv("BPF2GO_CFLAGS", ""), "flags passed to the compiler, may contain quoted arguments ($BPF2GO_CFLAGS)") fs.Var(&b2g.tags, "tags", "Comma-separated list of Go build tags to include in generated files") flagTarget := fs.String("target", "bpfel,bpfeb", "clang target(s) to compile for (comma separated)") fs.StringVar(&b2g.makeBase, "makebase", getEnv("BPF2GO_MAKEBASE", ""), "write make compatible depinfo files relative to `directory` ($BPF2GO_MAKEBASE)") fs.Var(&b2g.cTypes, "type", "`Name` of a type to generate a Go declaration for, may be repeated") fs.BoolVar(&b2g.skipGlobalTypes, "no-global-types", false, "Skip generating types for map keys and values, etc.") fs.StringVar(&b2g.outputStem, "output-stem", "", "alternative stem for names of generated files (defaults to ident)") fs.SetOutput(b2g.stdout) fs.Usage = func() { fmt.Fprintf(fs.Output(), helpText, fs.Name()) fs.PrintDefaults() fmt.Fprintln(fs.Output()) printTargets(fs.Output()) } if err := fs.Parse(args); err != nil { return nil, err } if b2g.pkg == "" { return nil, errors.New("missing package, are you running via go generate?") } if b2g.cc == "" { return nil, errors.New("no compiler specified") } args, cFlags := splitCFlagsFromArgs(fs.Args()) if *flagCFlags != "" { splitCFlags, err := splitArguments(*flagCFlags) if err != nil { return nil, err } // Command line arguments take precedence over C flags // from the flag. cFlags = append(splitCFlags, cFlags...) } for _, cFlag := range cFlags { if strings.HasPrefix(cFlag, "-M") { return nil, fmt.Errorf("use -makebase instead of %q", cFlag) } } b2g.cFlags = cFlags[:len(cFlags):len(cFlags)] if len(args) < 2 { return nil, errors.New("expected at least two arguments") } b2g.identStem = args[0] if !token.IsIdentifier(b2g.identStem) { return nil, fmt.Errorf("%q is not a valid identifier", b2g.identStem) } sourceFile, err := filepath.Abs(args[1]) if err != nil { return nil, err } b2g.sourceFile = sourceFile if b2g.makeBase != "" { b2g.makeBase, err = filepath.Abs(b2g.makeBase) if err != nil { return nil, err } } if b2g.outputStem != "" && strings.ContainsRune(b2g.outputStem, filepath.Separator) { return nil, fmt.Errorf("-output-stem %q must not contain path separation characters", b2g.outputStem) } targetArches, err := collectTargets(strings.Split(*flagTarget, ",")) if errors.Is(err, errInvalidTarget) { printTargets(b2g.stdout) fmt.Fprintln(b2g.stdout) return nil, err } if err != nil { return nil, err } if len(targetArches) == 0 { return nil, fmt.Errorf("no targets specified") } b2g.targetArches = targetArches // Try to find a suitable llvm-strip, possibly with a version suffix derived // from the clang binary. if b2g.strip == "" { b2g.strip = "llvm-strip" if strings.HasPrefix(b2g.cc, "clang") { b2g.strip += strings.TrimPrefix(b2g.cc, "clang") } } return b2g, nil } // cTypes collects the C type names a user wants to generate Go types for. // // Names are guaranteed to be unique, and only a subset of names is accepted so // that we may extend the flag syntax in the future. type cTypes []string var _ flag.Value = (*cTypes)(nil) func (ct *cTypes) String() string { if ct == nil { return "[]" } return fmt.Sprint(*ct) } const validCTypeChars = `[a-z0-9_]` var reValidCType = regexp.MustCompile(`(?i)^` + validCTypeChars + `+$`) func (ct *cTypes) Set(value string) error { if !reValidCType.MatchString(value) { return fmt.Errorf("%q contains characters outside of %s", value, validCTypeChars) } i := sort.SearchStrings(*ct, value) if i >= len(*ct) { *ct = append(*ct, value) return nil } if (*ct)[i] == value { return fmt.Errorf("duplicate type %q", value) } *ct = append((*ct)[:i], append([]string{value}, (*ct)[i:]...)...) return nil } func getEnv(key, defaultVal string) string { if val, ok := os.LookupEnv(key); ok { return val } return defaultVal } func (b2g *bpf2go) convertAll() (err error) { if _, err := os.Stat(b2g.sourceFile); os.IsNotExist(err) { return fmt.Errorf("file %s doesn't exist", b2g.sourceFile) } else if err != nil { return err } if !b2g.disableStripping { b2g.strip, err = exec.LookPath(b2g.strip) if err != nil { return err } } for target, arches := range b2g.targetArches { if err := b2g.convert(target, arches); err != nil { return err } } return nil } func (b2g *bpf2go) convert(tgt target, arches []string) (err error) { removeOnError := func(f *os.File) { if err != nil { os.Remove(f.Name()) } f.Close() } outputStem := b2g.outputStem if outputStem == "" { outputStem = strings.ToLower(b2g.identStem) } stem := fmt.Sprintf("%s_%s", outputStem, tgt.clang) if tgt.linux != "" { stem = fmt.Sprintf("%s_%s_%s", outputStem, tgt.clang, tgt.linux) } objFileName := filepath.Join(b2g.outputDir, stem+".o") cwd, err := os.Getwd() if err != nil { return err } var archConstraint constraint.Expr for _, arch := range arches { tag := &constraint.TagExpr{Tag: arch} archConstraint = orConstraints(archConstraint, tag) } constraints := andConstraints(archConstraint, b2g.tags.Expr) cFlags := make([]string, len(b2g.cFlags)) copy(cFlags, b2g.cFlags) if tgt.linux != "" { cFlags = append(cFlags, "-D__TARGET_ARCH_"+tgt.linux) } var dep bytes.Buffer err = compile(compileArgs{ cc: b2g.cc, cFlags: cFlags, target: tgt.clang, dir: cwd, source: b2g.sourceFile, dest: objFileName, dep: &dep, }) if err != nil { return err } fmt.Fprintln(b2g.stdout, "Compiled", objFileName) if !b2g.disableStripping { if err := strip(b2g.strip, objFileName); err != nil { return err } fmt.Fprintln(b2g.stdout, "Stripped", objFileName) } spec, err := ebpf.LoadCollectionSpec(objFileName) if err != nil { return fmt.Errorf("can't load BPF from ELF: %s", err) } maps, programs, types, err := collectFromSpec(spec, b2g.cTypes, b2g.skipGlobalTypes) if err != nil { return err } // Write out generated go goFileName := filepath.Join(b2g.outputDir, stem+".go") goFile, err := os.Create(goFileName) if err != nil { return err } defer removeOnError(goFile) err = output(outputArgs{ pkg: b2g.pkg, stem: b2g.identStem, constraints: constraints, maps: maps, programs: programs, types: types, obj: filepath.Base(objFileName), out: goFile, }) if err != nil { return fmt.Errorf("can't write %s: %s", goFileName, err) } fmt.Fprintln(b2g.stdout, "Wrote", goFileName) if b2g.makeBase == "" { return } deps, err := parseDependencies(cwd, &dep) if err != nil { return fmt.Errorf("can't read dependency information: %s", err) } // There is always at least a dependency for the main file. deps[0].file = goFileName depFile, err := adjustDependencies(b2g.makeBase, deps) if err != nil { return fmt.Errorf("can't adjust dependency information: %s", err) } depFileName := goFileName + ".d" if err := os.WriteFile(depFileName, depFile, 0666); err != nil { return fmt.Errorf("can't write dependency file: %s", err) } fmt.Fprintln(b2g.stdout, "Wrote", depFileName) return nil } type target struct { clang string linux string } func printTargets(w io.Writer) { var arches []string for arch, archTarget := range targetByGoArch { if archTarget.linux == "" { continue } arches = append(arches, arch) } sort.Strings(arches) fmt.Fprint(w, "Supported targets:\n") fmt.Fprint(w, "\tbpf\n\tbpfel\n\tbpfeb\n") for _, arch := range arches { fmt.Fprintf(w, "\t%s\n", arch) } } var errInvalidTarget = errors.New("unsupported target") func collectTargets(targets []string) (map[target][]string, error) { result := make(map[target][]string) for _, tgt := range targets { switch tgt { case "bpf", "bpfel", "bpfeb": var goarches []string for arch, archTarget := range targetByGoArch { if archTarget.clang == tgt { // Include tags for all goarches that have the same endianness. goarches = append(goarches, arch) } } sort.Strings(goarches) result[target{tgt, ""}] = goarches case "native": tgt = runtime.GOARCH fallthrough default: archTarget, ok := targetByGoArch[tgt] if !ok || archTarget.linux == "" { return nil, fmt.Errorf("%q: %w", tgt, errInvalidTarget) } var goarches []string for goarch, lt := range targetByGoArch { if lt == archTarget { // Include tags for all goarches that have the same // target. goarches = append(goarches, goarch) } } sort.Strings(goarches) result[archTarget] = goarches } } return result, nil } func main() { outputDir, err := os.Getwd() if err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } if err := run(os.Stdout, os.Getenv("GOPACKAGE"), outputDir, os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/main_test.go000066400000000000000000000262401456504511400227570ustar00rootroot00000000000000package main import ( "bytes" "fmt" "io" "os" "os/exec" "path/filepath" "runtime" "sort" "strings" "testing" qt "github.com/frankban/quicktest" "github.com/google/go-cmp/cmp" ) func TestRun(t *testing.T) { clangBin := clangBin(t) dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) cwd, err := os.Getwd() if err != nil { t.Fatal(err) } modRoot := filepath.Clean(filepath.Join(cwd, "../..")) if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) { t.Fatal("No go.mod file in", modRoot) } tmpDir, err := os.MkdirTemp("", "bpf2go-module-*") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) execInModule := func(name string, args ...string) { t.Helper() cmd := exec.Command(name, args...) cmd.Dir = tmpDir if out, err := cmd.CombinedOutput(); err != nil { if out := string(out); out != "" { t.Log(out) } t.Fatalf("Can't execute %s: %v", name, args) } } module := currentModule() execInModule("go", "mod", "init", "bpf2go-test") execInModule("go", "mod", "edit", // Require the module. The version doesn't matter due to the replace // below. fmt.Sprintf("-require=%s@v0.0.0", module), // Replace the module with the current version. fmt.Sprintf("-replace=%s=%s", module, modRoot), ) err = run(io.Discard, "foo", tmpDir, []string{ "-cc", clangBin, "bar", filepath.Join(dir, "test.c"), }) if err != nil { t.Fatal("Can't run:", err) } for _, arch := range []string{ "amd64", // little-endian "s390x", // big-endian } { t.Run(arch, func(t *testing.T) { goBin := exec.Command("go", "build", "-mod=mod") goBin.Dir = tmpDir goBin.Env = append(os.Environ(), "GOOS=linux", "GOARCH="+arch, ) out, err := goBin.CombinedOutput() if err != nil { if out := string(out); out != "" { t.Log(out) } t.Error("Can't compile package:", err) } }) } } func TestHelp(t *testing.T) { var stdout bytes.Buffer err := run(&stdout, "", "", []string{"-help"}) if err != nil { t.Fatal("Can't execute -help") } if stdout.Len() == 0 { t.Error("-help doesn't write to stdout") } } func TestDisableStripping(t *testing.T) { dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) err := run(io.Discard, "foo", dir, []string{ "-cc", clangBin(t), "-strip", "binary-that-certainly-doesnt-exist", "-no-strip", "bar", filepath.Join(dir, "test.c"), }) if err != nil { t.Fatal("Can't run with stripping disabled:", err) } } func TestCollectTargets(t *testing.T) { clangArches := make(map[string][]string) linuxArchesLE := make(map[string][]string) linuxArchesBE := make(map[string][]string) for arch, archTarget := range targetByGoArch { clangArches[archTarget.clang] = append(clangArches[archTarget.clang], arch) if archTarget.clang == "bpfel" { linuxArchesLE[archTarget.linux] = append(linuxArchesLE[archTarget.linux], arch) continue } linuxArchesBE[archTarget.linux] = append(linuxArchesBE[archTarget.linux], arch) } for i := range clangArches { sort.Strings(clangArches[i]) } for i := range linuxArchesLE { sort.Strings(linuxArchesLE[i]) } for i := range linuxArchesBE { sort.Strings(linuxArchesBE[i]) } nativeTarget := make(map[target][]string) for arch, archTarget := range targetByGoArch { if arch == runtime.GOARCH { if archTarget.clang == "bpfel" { nativeTarget[archTarget] = linuxArchesLE[archTarget.linux] } else { nativeTarget[archTarget] = linuxArchesBE[archTarget.linux] } break } } tests := []struct { targets []string want map[target][]string }{ { []string{"bpf", "bpfel", "bpfeb"}, map[target][]string{ {"bpf", ""}: nil, {"bpfel", ""}: clangArches["bpfel"], {"bpfeb", ""}: clangArches["bpfeb"], }, }, { []string{"amd64", "386"}, map[target][]string{ {"bpfel", "x86"}: linuxArchesLE["x86"], }, }, { []string{"amd64", "arm64be"}, map[target][]string{ {"bpfeb", "arm64"}: linuxArchesBE["arm64"], {"bpfel", "x86"}: linuxArchesLE["x86"], }, }, { []string{"native"}, nativeTarget, }, } for _, test := range tests { name := strings.Join(test.targets, ",") t.Run(name, func(t *testing.T) { have, err := collectTargets(test.targets) if err != nil { t.Fatal(err) } if diff := cmp.Diff(test.want, have); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } }) } } func TestCollectTargetsErrors(t *testing.T) { tests := []struct { name string target string }{ {"unknown", "frood"}, {"no linux target", "mips64p32le"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := collectTargets([]string{test.target}) if err == nil { t.Fatal("Function did not return an error") } t.Log("Error message:", err) }) } } func TestConvertGOARCH(t *testing.T) { tmp := mustWriteTempFile(t, "test.c", ` #ifndef __TARGET_ARCH_x86 #error __TARGET_ARCH_x86 is not defined #endif`, ) b2g := bpf2go{ pkg: "test", stdout: io.Discard, identStem: "test", cc: clangBin(t), disableStripping: true, sourceFile: tmp + "/test.c", outputDir: tmp, } if err := b2g.convert(targetByGoArch["amd64"], nil); err != nil { t.Fatal("Can't target GOARCH:", err) } } func TestCTypes(t *testing.T) { var ct cTypes valid := []string{ "abcdefghijklmnopqrstuvqxyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_", "y", } for _, value := range valid { if err := ct.Set(value); err != nil { t.Fatalf("Set returned an error for %q: %s", value, err) } } qt.Assert(t, ct, qt.ContentEquals, cTypes(valid)) for _, value := range []string{ "", " ", " frood", "foo\nbar", ".", ",", "+", "-", } { ct = nil if err := ct.Set(value); err == nil { t.Fatalf("Set did not return an error for %q", value) } } ct = nil qt.Assert(t, ct.Set("foo"), qt.IsNil) qt.Assert(t, ct.Set("foo"), qt.IsNotNil) } func TestParseArgs(t *testing.T) { const ( pkg = "eee" outputDir = "." csource = "testdata/minimal.c" stem = "a" ) t.Run("makebase", func(t *testing.T) { basePath, _ := filepath.Abs("barfoo") args := []string{"-makebase", basePath, stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.makeBase, qt.Equals, basePath) }) t.Run("makebase from env", func(t *testing.T) { basePath, _ := filepath.Abs("barfoo") args := []string{stem, csource} t.Setenv("BPF2GO_MAKEBASE", basePath) b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.makeBase, qt.Equals, basePath) }) t.Run("makebase flag overrides env", func(t *testing.T) { basePathFlag, _ := filepath.Abs("barfoo") basePathEnv, _ := filepath.Abs("foobar") args := []string{"-makebase", basePathFlag, stem, csource} t.Setenv("BPF2GO_MAKEBASE", basePathEnv) b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.makeBase, qt.Equals, basePathFlag) }) t.Run("cc defaults to clang", func(t *testing.T) { args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cc, qt.Equals, "clang") }) t.Run("cc", func(t *testing.T) { args := []string{"-cc", "barfoo", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cc, qt.Equals, "barfoo") }) t.Run("cc from env", func(t *testing.T) { args := []string{stem, csource} t.Setenv("BPF2GO_CC", "barfoo") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cc, qt.Equals, "barfoo") }) t.Run("cc flag overrides env", func(t *testing.T) { args := []string{"-cc", "barfoo", stem, csource} t.Setenv("BPF2GO_CC", "foobar") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cc, qt.Equals, "barfoo") }) t.Run("strip defaults to llvm-strip", func(t *testing.T) { args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.strip, qt.Equals, "llvm-strip") }) t.Run("strip", func(t *testing.T) { args := []string{"-strip", "barfoo", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.strip, qt.Equals, "barfoo") }) t.Run("strip from env", func(t *testing.T) { args := []string{stem, csource} t.Setenv("BPF2GO_STRIP", "barfoo") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.strip, qt.Equals, "barfoo") }) t.Run("strip flag overrides env", func(t *testing.T) { args := []string{"-strip", "barfoo", stem, csource} t.Setenv("BPF2GO_STRIP", "foobar") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.strip, qt.Equals, "barfoo") }) t.Run("no strip defaults to false", func(t *testing.T) { args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.disableStripping, qt.IsFalse) }) t.Run("no strip", func(t *testing.T) { args := []string{"-no-strip", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.disableStripping, qt.IsTrue) }) t.Run("cflags flag", func(t *testing.T) { args := []string{"-cflags", "x y z", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cFlags, qt.DeepEquals, []string{"x", "y", "z"}) }) t.Run("cflags multi flag", func(t *testing.T) { args := []string{"-cflags", "x y z", "-cflags", "u v", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cFlags, qt.DeepEquals, []string{"u", "v"}) }) t.Run("cflags flag and args", func(t *testing.T) { args := []string{"-cflags", "x y z", "stem", csource, "--", "u", "v"} b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cFlags, qt.DeepEquals, []string{"x", "y", "z", "u", "v"}) }) t.Run("cflags from env", func(t *testing.T) { args := []string{stem, csource} t.Setenv("BPF2GO_CFLAGS", "x y z") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cFlags, qt.DeepEquals, []string{"x", "y", "z"}) }) t.Run("cflags flag overrides env", func(t *testing.T) { args := []string{"-cflags", "u v", stem, csource} t.Setenv("BPF2GO_CFLAGS", "x y z") b2g, err := newB2G(&bytes.Buffer{}, pkg, outputDir, args) qt.Assert(t, err, qt.IsNil) qt.Assert(t, b2g.cFlags, qt.DeepEquals, []string{"u", "v"}) }) } func clangBin(t *testing.T) string { t.Helper() if testing.Short() { t.Skip("Not compiling with -short") } // Use a recent clang version for local development, but allow CI to run // against oldest supported clang. clang := "clang-14" if minVersion := os.Getenv("CI_MIN_CLANG_VERSION"); minVersion != "" { clang = fmt.Sprintf("clang-%s", minVersion) } t.Log("Testing against", clang) return clang } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/output.go000066400000000000000000000125141456504511400223330ustar00rootroot00000000000000package main import ( "bytes" _ "embed" "fmt" "go/build/constraint" "go/token" "io" "sort" "strings" "text/template" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" ) //go:embed output.tpl var commonRaw string var commonTemplate = template.Must(template.New("common").Parse(commonRaw)) type templateName string func (n templateName) maybeExport(str string) string { if token.IsExported(string(n)) { return toUpperFirst(str) } return str } func (n templateName) Bytes() string { return "_" + toUpperFirst(string(n)) + "Bytes" } func (n templateName) Specs() string { return string(n) + "Specs" } func (n templateName) ProgramSpecs() string { return string(n) + "ProgramSpecs" } func (n templateName) MapSpecs() string { return string(n) + "MapSpecs" } func (n templateName) Load() string { return n.maybeExport("load" + toUpperFirst(string(n))) } func (n templateName) LoadObjects() string { return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects") } func (n templateName) Objects() string { return string(n) + "Objects" } func (n templateName) Maps() string { return string(n) + "Maps" } func (n templateName) Programs() string { return string(n) + "Programs" } func (n templateName) CloseHelper() string { return "_" + toUpperFirst(string(n)) + "Close" } type outputArgs struct { // Package of the resulting file. pkg string // The prefix of all names declared at the top-level. stem string // Build constraints included in the resulting file. constraints constraint.Expr // Maps to be emitted. maps []string // Programs to be emitted. programs []string // Types to be emitted. types []btf.Type // Filename of the ELF object to embed. obj string out io.Writer } func output(args outputArgs) error { maps := make(map[string]string) for _, name := range args.maps { maps[name] = internal.Identifier(name) } programs := make(map[string]string) for _, name := range args.programs { programs[name] = internal.Identifier(name) } typeNames := make(map[btf.Type]string) for _, typ := range args.types { typeNames[typ] = args.stem + internal.Identifier(typ.TypeName()) } // Ensure we don't have conflicting names and generate a sorted list of // named types so that the output is stable. types, err := sortTypes(typeNames) if err != nil { return err } module := currentModule() gf := &btf.GoFormatter{ Names: typeNames, Identifier: internal.Identifier, } ctx := struct { *btf.GoFormatter Module string Package string Constraints constraint.Expr Name templateName Maps map[string]string Programs map[string]string Types []btf.Type TypeNames map[btf.Type]string File string }{ gf, module, args.pkg, args.constraints, templateName(args.stem), maps, programs, types, typeNames, args.obj, } var buf bytes.Buffer if err := commonTemplate.Execute(&buf, &ctx); err != nil { return fmt.Errorf("can't generate types: %s", err) } return internal.WriteFormatted(buf.Bytes(), args.out) } func collectFromSpec(spec *ebpf.CollectionSpec, cTypes []string, skipGlobalTypes bool) (maps, programs []string, types []btf.Type, _ error) { for name := range spec.Maps { // Skip .rodata, .data, .bss, etc. sections if !strings.HasPrefix(name, ".") { maps = append(maps, name) } } for name := range spec.Programs { programs = append(programs, name) } types, err := collectCTypes(spec.Types, cTypes) if err != nil { return nil, nil, nil, fmt.Errorf("collect C types: %w", err) } // Collect map key and value types, unless we've been asked not to. if skipGlobalTypes { return maps, programs, types, nil } for _, typ := range collectMapTypes(spec.Maps) { switch btf.UnderlyingType(typ).(type) { case *btf.Datasec: // Avoid emitting .rodata, .bss, etc. for now. We might want to // name these types differently, etc. continue case *btf.Int: // Don't emit primitive types by default. continue } types = append(types, typ) } return maps, programs, types, nil } func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) { var result []btf.Type for _, cType := range names { typ, err := types.AnyTypeByName(cType) if err != nil { return nil, err } result = append(result, typ) } return result, nil } // collectMapTypes returns a list of all types used as map keys or values. func collectMapTypes(maps map[string]*ebpf.MapSpec) []btf.Type { var result []btf.Type for _, m := range maps { if m.Key != nil && m.Key.TypeName() != "" { result = append(result, m.Key) } if m.Value != nil && m.Value.TypeName() != "" { result = append(result, m.Value) } } return result } // sortTypes returns a list of types sorted by their (generated) Go type name. // // Duplicate Go type names are rejected. func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) { var types []btf.Type var names []string for typ, name := range typeNames { i := sort.SearchStrings(names, name) if i >= len(names) { types = append(types, typ) names = append(names, name) continue } if names[i] == name { return nil, fmt.Errorf("type name %q is used multiple times", name) } types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...) names = append(names[:i], append([]string{name}, names[i:]...)...) } return types, nil } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/output.tpl000066400000000000000000000064631456504511400225330ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. {{ with .Constraints }}//go:build {{ . }}{{ end }} package {{ .Package }} import ( "bytes" _ "embed" "fmt" "io" "{{ .Module }}" ) {{- if .Types }} {{- range $type := .Types }} {{ $.TypeDeclaration (index $.TypeNames $type) $type }} {{ end }} {{- end }} // {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}. func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader({{ .Name.Bytes }}) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load {{ .Name }}: %w", err) } return spec, err } // {{ .Name.LoadObjects }} loads {{ .Name }} and converts it into a struct. // // The following types are suitable as obj argument: // // *{{ .Name.Objects }} // *{{ .Name.Programs }} // *{{ .Name.Maps }} // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) { spec, err := {{ .Name.Load }}() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // {{ .Name.Specs }} contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.Specs }} struct { {{ .Name.ProgramSpecs }} {{ .Name.MapSpecs }} } // {{ .Name.Specs }} contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.ProgramSpecs }} struct { {{- range $name, $id := .Programs }} {{ $id }} *ebpf.ProgramSpec `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.MapSpecs }} contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.MapSpecs }} struct { {{- range $name, $id := .Maps }} {{ $id }} *ebpf.MapSpec `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.Objects }} contains all objects after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Objects }} struct { {{ .Name.Programs }} {{ .Name.Maps }} } func (o *{{ .Name.Objects }}) Close() error { return {{ .Name.CloseHelper }}( &o.{{ .Name.Programs }}, &o.{{ .Name.Maps }}, ) } // {{ .Name.Maps }} contains all maps after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Maps }} struct { {{- range $name, $id := .Maps }} {{ $id }} *ebpf.Map `ebpf:"{{ $name }}"` {{- end }} } func (m *{{ .Name.Maps }}) Close() error { return {{ .Name.CloseHelper }}( {{- range $id := .Maps }} m.{{ $id }}, {{- end }} ) } // {{ .Name.Programs }} contains all programs after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Programs }} struct { {{- range $name, $id := .Programs }} {{ $id }} *ebpf.Program `ebpf:"{{ $name }}"` {{- end }} } func (p *{{ .Name.Programs }}) Close() error { return {{ .Name.CloseHelper }}( {{- range $id := .Programs }} p.{{ $id }}, {{- end }} ) } func {{ .Name.CloseHelper }}(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. //go:embed {{ .File }} var {{ .Name.Bytes }} []byte golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/output_test.go000066400000000000000000000037501456504511400233740ustar00rootroot00000000000000package main import ( "fmt" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" qt "github.com/frankban/quicktest" "github.com/google/go-cmp/cmp" ) func TestOrderTypes(t *testing.T) { a := &btf.Int{} b := &btf.Int{} c := &btf.Int{} for _, test := range []struct { name string in map[btf.Type]string out []btf.Type }{ { "order", map[btf.Type]string{ a: "foo", b: "bar", c: "baz", }, []btf.Type{b, c, a}, }, } { t.Run(test.name, func(t *testing.T) { result, err := sortTypes(test.in) qt.Assert(t, err, qt.IsNil) qt.Assert(t, len(result), qt.Equals, len(test.out)) for i, o := range test.out { if result[i] != o { t.Fatalf("Index %d: expected %p got %p", i, o, result[i]) } } }) } for _, test := range []struct { name string in map[btf.Type]string }{ { "duplicate names", map[btf.Type]string{ a: "foo", b: "foo", }, }, } { t.Run(test.name, func(t *testing.T) { result, err := sortTypes(test.in) qt.Assert(t, err, qt.IsNotNil) qt.Assert(t, result, qt.IsNil) }) } } var typesEqual = qt.CmpEquals(cmp.Comparer(func(a, b btf.Type) bool { return a == b })) func TestCollectFromSpec(t *testing.T) { spec, err := ebpf.LoadCollectionSpec(fmt.Sprintf("testdata/minimal-%s.elf", internal.ClangEndian)) if err != nil { t.Fatal(err) } map1 := spec.Maps["map1"] maps, programs, types, err := collectFromSpec(spec, nil, false) if err != nil { t.Fatal(err) } qt.Assert(t, maps, qt.ContentEquals, []string{"map1"}) qt.Assert(t, programs, qt.ContentEquals, []string{"filter"}) qt.Assert(t, types, typesEqual, []btf.Type{map1.Key, map1.Value}) _, _, types, err = collectFromSpec(spec, nil, true) if err != nil { t.Fatal(err) } qt.Assert(t, types, typesEqual, ([]btf.Type)(nil)) _, _, types, err = collectFromSpec(spec, []string{"barfoo"}, true) if err != nil { t.Fatal(err) } qt.Assert(t, types, typesEqual, []btf.Type{map1.Value}) } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/testdata/000077500000000000000000000000001456504511400222525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/testdata/minimal-eb.elf000066400000000000000000000047401456504511400247610ustar00rootroot00000000000000ELF`@@ayMIT    !' . 2 @6`: B@L RW@[am  r      int__ARRAY_SIZE_TYPE__eHOOPYFROODbarfoobarbazboolongintlong long_Booltypekeyvaluemax_entriesmap1filtersocket./cmd/bpf2go/testdata/minimal.c return my_constant + struct_const.bar;char__licensemy_constantstruct_const.maps.rodatalicense L`yyl l$0l 8l>@!S~  0,@P`p .text.rel.BTF.extstruct_constmy_constant.relsocket.mapsfilter.llvm_addrsig__license.strtab.symtab.rodata.rel.BTFmap1]@1@@- @( Um8 y>u @H@   @P EoLegolang-github-cilium-ebpf-0.11.0/cmd/bpf2go/testdata/minimal-el.elf000066400000000000000000000047401456504511400247730ustar00rootroot00000000000000ELF`@@ayMIT    !' . 2 @6`: B@L RW@[am  r      int__ARRAY_SIZE_TYPE__eHOOPYFROODbarfoobarbazboolongintlong long_Booltypekeyvaluemax_entriesmap1filtersocket./cmd/bpf2go/testdata/minimal.c return my_constant + struct_const.bar;char__licensemy_constantstruct_const.maps.rodatalicense L`yy l$l0 l8l>@!S~  0,@P`p .text.rel.BTF.extstruct_constmy_constant.relsocket.mapsfilter.llvm_addrsig__license.strtab.symtab.rodata.rel.BTFmap1]@1@@- @( Um8 y>u @H@   @P ELoegolang-github-cilium-ebpf-0.11.0/cmd/bpf2go/testdata/minimal.c000066400000000000000000000010001456504511400240330ustar00rootroot00000000000000#include "../../../testdata/common.h" char __license[] __section("license") = "MIT"; enum e { HOOPY, FROOD }; typedef long long int longint; typedef struct { longint bar; _Bool baz; enum e boo; } barfoo; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, enum e); __type(value, barfoo); __uint(max_entries, 1); } map1 __section(".maps"); volatile const enum e my_constant = FROOD; volatile const barfoo struct_const; __section("socket") int filter() { return my_constant + struct_const.bar; } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/tools.go000066400000000000000000000027561456504511400221420ustar00rootroot00000000000000package main import ( "errors" "fmt" "runtime/debug" "strings" "unicode" "unicode/utf8" ) func splitCFlagsFromArgs(in []string) (args, cflags []string) { for i, arg := range in { if arg == "--" { return in[:i], in[i+1:] } } return in, nil } func splitArguments(in string) ([]string, error) { var ( result []string builder strings.Builder escaped bool delim = ' ' ) for _, r := range strings.TrimSpace(in) { if escaped { builder.WriteRune(r) escaped = false continue } switch r { case '\\': escaped = true case delim: current := builder.String() builder.Reset() if current != "" || delim != ' ' { // Only append empty words if they are not // delimited by spaces result = append(result, current) } delim = ' ' case '"', '\'', ' ': if delim == ' ' { delim = r continue } fallthrough default: builder.WriteRune(r) } } if delim != ' ' { return nil, fmt.Errorf("missing `%c`", delim) } if escaped { return nil, errors.New("unfinished escape") } // Add the last word if builder.Len() > 0 { result = append(result, builder.String()) } return result, nil } func toUpperFirst(str string) string { first, n := utf8.DecodeRuneInString(str) return string(unicode.ToUpper(first)) + str[n:] } func currentModule() string { bi, ok := debug.ReadBuildInfo() if !ok { // Fall back to constant since bazel doesn't support BuildInfo. return "github.com/cilium/ebpf" } return bi.Main.Path } golang-github-cilium-ebpf-0.11.0/cmd/bpf2go/tools_test.go000066400000000000000000000016501456504511400231710ustar00rootroot00000000000000package main import ( "reflect" "testing" ) func TestSplitArguments(t *testing.T) { testcases := []struct { in string out []string }{ {`foo`, []string{"foo"}}, {`foo bar`, []string{"foo", "bar"}}, {`foo bar`, []string{"foo", "bar"}}, {`\\`, []string{`\`}}, {`\\\`, nil}, {`foo\ bar`, []string{"foo bar"}}, {`foo "" bar`, []string{"foo", "", "bar"}}, {`"bar baz"`, []string{"bar baz"}}, {`'bar baz'`, []string{"bar baz"}}, {`'bar " " baz'`, []string{`bar " " baz`}}, {`"bar \" baz"`, []string{`bar " baz`}}, {`"`, nil}, {`'`, nil}, } for _, testcase := range testcases { have, err := splitArguments(testcase.in) if testcase.out == nil { if err == nil { t.Errorf("Test should fail for: %s", testcase.in) } } else if !reflect.DeepEqual(testcase.out, have) { t.Logf("Have: %q\n", have) t.Logf("Want: %q\n", testcase.out) t.Errorf("Test fails for: %s", testcase.in) } } } golang-github-cilium-ebpf-0.11.0/collection.go000066400000000000000000000557701456504511400212170ustar00rootroot00000000000000package ebpf import ( "encoding/binary" "errors" "fmt" "reflect" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/kconfig" ) // CollectionOptions control loading a collection into the kernel. // // Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. type CollectionOptions struct { Maps MapOptions Programs ProgramOptions // MapReplacements takes a set of Maps that will be used instead of // creating new ones when loading the CollectionSpec. // // For each given Map, there must be a corresponding MapSpec in // CollectionSpec.Maps, and its type, key/value size, max entries and flags // must match the values of the MapSpec. // // The given Maps are Clone()d before being used in the Collection, so the // caller can Close() them freely when they are no longer needed. MapReplacements map[string]*Map } // CollectionSpec describes a collection. type CollectionSpec struct { Maps map[string]*MapSpec Programs map[string]*ProgramSpec // Types holds type information about Maps and Programs. // Modifications to Types are currently undefined behaviour. Types *btf.Spec // ByteOrder specifies whether the ELF was compiled for // big-endian or little-endian architectures. ByteOrder binary.ByteOrder } // Copy returns a recursive copy of the spec. func (cs *CollectionSpec) Copy() *CollectionSpec { if cs == nil { return nil } cpy := CollectionSpec{ Maps: make(map[string]*MapSpec, len(cs.Maps)), Programs: make(map[string]*ProgramSpec, len(cs.Programs)), ByteOrder: cs.ByteOrder, Types: cs.Types, } for name, spec := range cs.Maps { cpy.Maps[name] = spec.Copy() } for name, spec := range cs.Programs { cpy.Programs[name] = spec.Copy() } return &cpy } // RewriteMaps replaces all references to specific maps. // // Use this function to use pre-existing maps instead of creating new ones // when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. // // Returns an error if a named map isn't used in at least one program. // // Deprecated: Pass CollectionOptions.MapReplacements when loading the Collection // instead. func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { for symbol, m := range maps { // have we seen a program that uses this symbol / map seen := false for progName, progSpec := range cs.Programs { err := progSpec.Instructions.AssociateMap(symbol, m) switch { case err == nil: seen = true case errors.Is(err, asm.ErrUnreferencedSymbol): // Not all programs need to use the map default: return fmt.Errorf("program %s: %w", progName, err) } } if !seen { return fmt.Errorf("map %s not referenced by any programs", symbol) } // Prevent NewCollection from creating rewritten maps delete(cs.Maps, symbol) } return nil } // MissingConstantsError is returned by [CollectionSpec.RewriteConstants]. type MissingConstantsError struct { // The constants missing from .rodata. Constants []string } func (m *MissingConstantsError) Error() string { return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", ")) } // RewriteConstants replaces the value of multiple constants. // // The constant must be defined like so in the C program: // // volatile const type foobar; // volatile const type foobar = default; // // Replacement values must be of the same length as the C sizeof(type). // If necessary, they are marshalled according to the same rules as // map values. // // From Linux 5.5 the verifier will use constants to eliminate dead code. // // Returns an error wrapping [MissingConstantsError] if a constant doesn't exist. func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { replaced := make(map[string]bool) for name, spec := range cs.Maps { if !strings.HasPrefix(name, ".rodata") { continue } b, ds, err := spec.dataSection() if errors.Is(err, errMapNoBTFValue) { // Data sections without a BTF Datasec are valid, but don't support // constant replacements. continue } if err != nil { return fmt.Errorf("map %s: %w", name, err) } // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice // to avoid any changes affecting other copies of the MapSpec. cpy := make([]byte, len(b)) copy(cpy, b) for _, v := range ds.Vars { vname := v.Type.TypeName() replacement, ok := consts[vname] if !ok { continue } if _, ok := v.Type.(*btf.Var); !ok { return fmt.Errorf("section %s: unexpected type %T for variable %s", name, v.Type, vname) } if replaced[vname] { return fmt.Errorf("section %s: duplicate variable %s", name, vname) } if int(v.Offset+v.Size) > len(cpy) { return fmt.Errorf("section %s: offset %d(+%d) for variable %s is out of bounds", name, v.Offset, v.Size, vname) } b, err := marshalBytes(replacement, int(v.Size)) if err != nil { return fmt.Errorf("marshaling constant replacement %s: %w", vname, err) } copy(cpy[v.Offset:v.Offset+v.Size], b) replaced[vname] = true } spec.Contents[0] = MapKV{Key: uint32(0), Value: cpy} } var missing []string for c := range consts { if !replaced[c] { missing = append(missing, c) } } if len(missing) != 0 { return fmt.Errorf("rewrite constants: %w", &MissingConstantsError{Constants: missing}) } return nil } // Assign the contents of a CollectionSpec to a struct. // // This function is a shortcut to manually checking the presence // of maps and programs in a CollectionSpec. Consider using bpf2go // if this sounds useful. // // 'to' must be a pointer to a struct. A field of the // struct is updated with values from Programs or Maps if it // has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. // The tag's value specifies the name of the program or map as // found in the CollectionSpec. // // struct { // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` // Bar *ebpf.MapSpec `ebpf:"bar_map"` // Ignored int // } // // Returns an error if any of the eBPF objects can't be found, or // if the same MapSpec or ProgramSpec is assigned multiple times. func (cs *CollectionSpec) Assign(to interface{}) error { // Assign() only supports assigning ProgramSpecs and MapSpecs, // so doesn't load any resources into the kernel. getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*ProgramSpec)(nil)): if p := cs.Programs[name]; p != nil { return p, nil } return nil, fmt.Errorf("missing program %q", name) case reflect.TypeOf((*MapSpec)(nil)): if m := cs.Maps[name]; m != nil { return m, nil } return nil, fmt.Errorf("missing map %q", name) default: return nil, fmt.Errorf("unsupported type %s", typ) } } return assignValues(to, getValue) } // LoadAndAssign loads Maps and Programs into the kernel and assigns them // to a struct. // // Omitting Map/Program.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. // // This function is a shortcut to manually checking the presence // of maps and programs in a CollectionSpec. Consider using bpf2go // if this sounds useful. // // 'to' must be a pointer to a struct. A field of the struct is updated with // a Program or Map if it has an `ebpf` tag and its type is *Program or *Map. // The tag's value specifies the name of the program or map as found in the // CollectionSpec. Before updating the struct, the requested objects and their // dependent resources are loaded into the kernel and populated with values if // specified. // // struct { // Foo *ebpf.Program `ebpf:"xdp_foo"` // Bar *ebpf.Map `ebpf:"bar_map"` // Ignored int // } // // opts may be nil. // // Returns an error if any of the fields can't be found, or // if the same Map or Program is assigned multiple times. func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { loader, err := newCollectionLoader(cs, opts) if err != nil { return err } defer loader.close() // Support assigning Programs and Maps, lazy-loading the required objects. assignedMaps := make(map[string]bool) assignedProgs := make(map[string]bool) getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*Program)(nil)): assignedProgs[name] = true return loader.loadProgram(name) case reflect.TypeOf((*Map)(nil)): assignedMaps[name] = true return loader.loadMap(name) default: return nil, fmt.Errorf("unsupported type %s", typ) } } // Load the Maps and Programs requested by the annotated struct. if err := assignValues(to, getValue); err != nil { return err } // Populate the requested maps. Has a chance of lazy-loading other dependent maps. if err := loader.populateMaps(); err != nil { return err } // Evaluate the loader's objects after all (lazy)loading has taken place. for n, m := range loader.maps { switch m.typ { case ProgramArray: // Require all lazy-loaded ProgramArrays to be assigned to the given object. // The kernel empties a ProgramArray once the last user space reference // to it closes, which leads to failed tail calls. Combined with the library // closing map fds via GC finalizers this can lead to surprising behaviour. // Only allow unassigned ProgramArrays when the library hasn't pre-populated // any entries from static value declarations. At this point, we know the map // is empty and there's no way for the caller to interact with the map going // forward. if !assignedMaps[n] && len(cs.Maps[n].Contents) > 0 { return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n) } } } // Prevent loader.cleanup() from closing assigned Maps and Programs. for m := range assignedMaps { delete(loader.maps, m) } for p := range assignedProgs { delete(loader.programs, p) } return nil } // Collection is a collection of Programs and Maps associated // with their symbols type Collection struct { Programs map[string]*Program Maps map[string]*Map } // NewCollection creates a Collection from the given spec, creating and // loading its declared resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func NewCollection(spec *CollectionSpec) (*Collection, error) { return NewCollectionWithOptions(spec, CollectionOptions{}) } // NewCollectionWithOptions creates a Collection from the given spec using // options, creating and loading its declared resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { loader, err := newCollectionLoader(spec, &opts) if err != nil { return nil, err } defer loader.close() // Create maps first, as their fds need to be linked into programs. for mapName := range spec.Maps { if _, err := loader.loadMap(mapName); err != nil { return nil, err } } for progName, prog := range spec.Programs { if prog.Type == UnspecifiedProgram { continue } if _, err := loader.loadProgram(progName); err != nil { return nil, err } } // Maps can contain Program and Map stubs, so populate them after // all Maps and Programs have been successfully loaded. if err := loader.populateMaps(); err != nil { return nil, err } // Prevent loader.cleanup from closing maps and programs. maps, progs := loader.maps, loader.programs loader.maps, loader.programs = nil, nil return &Collection{ progs, maps, }, nil } type collectionLoader struct { coll *CollectionSpec opts *CollectionOptions maps map[string]*Map programs map[string]*Program } func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) { if opts == nil { opts = &CollectionOptions{} } // Check for existing MapSpecs in the CollectionSpec for all provided replacement maps. for name, m := range opts.MapReplacements { spec, ok := coll.Maps[name] if !ok { return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name) } if err := spec.Compatible(m); err != nil { return nil, fmt.Errorf("using replacement map %s: %w", spec.Name, err) } } return &collectionLoader{ coll, opts, make(map[string]*Map), make(map[string]*Program), }, nil } // close all resources left over in the collectionLoader. func (cl *collectionLoader) close() { for _, m := range cl.maps { m.Close() } for _, p := range cl.programs { p.Close() } } func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { if m := cl.maps[mapName]; m != nil { return m, nil } mapSpec := cl.coll.Maps[mapName] if mapSpec == nil { return nil, fmt.Errorf("missing map %s", mapName) } if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok { // Clone the map to avoid closing user's map later on. m, err := replaceMap.Clone() if err != nil { return nil, err } cl.maps[mapName] = m return m, nil } m, err := newMapWithOptions(mapSpec, cl.opts.Maps) if err != nil { return nil, fmt.Errorf("map %s: %w", mapName, err) } cl.maps[mapName] = m return m, nil } func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { if prog := cl.programs[progName]; prog != nil { return prog, nil } progSpec := cl.coll.Programs[progName] if progSpec == nil { return nil, fmt.Errorf("unknown program %s", progName) } // Bail out early if we know the kernel is going to reject the program. // This skips loading map dependencies, saving some cleanup work later. if progSpec.Type == UnspecifiedProgram { return nil, fmt.Errorf("cannot load program %s: program type is unspecified", progName) } progSpec = progSpec.Copy() // Rewrite any reference to a valid map in the program's instructions, // which includes all of its dependencies. for i := range progSpec.Instructions { ins := &progSpec.Instructions[i] if !ins.IsLoadFromMap() || ins.Reference() == "" { continue } // Don't overwrite map loads containing non-zero map fd's, // they can be manually included by the caller. // Map FDs/IDs are placed in the lower 32 bits of Constant. if int32(ins.Constant) > 0 { continue } m, err := cl.loadMap(ins.Reference()) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } if err := ins.AssociateMap(m); err != nil { return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference(), err) } } prog, err := newProgramWithOptions(progSpec, cl.opts.Programs) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } cl.programs[progName] = prog return prog, nil } func (cl *collectionLoader) populateMaps() error { for mapName, m := range cl.maps { mapSpec, ok := cl.coll.Maps[mapName] if !ok { return fmt.Errorf("missing map spec %s", mapName) } // MapSpecs that refer to inner maps or programs within the same // CollectionSpec do so using strings. These strings are used as the key // to look up the respective object in the Maps or Programs fields. // Resolve those references to actual Map or Program resources that // have been loaded into the kernel. if mapSpec.Type.canStoreMap() || mapSpec.Type.canStoreProgram() { mapSpec = mapSpec.Copy() for i, kv := range mapSpec.Contents { objName, ok := kv.Value.(string) if !ok { continue } switch t := mapSpec.Type; { case t.canStoreProgram(): // loadProgram is idempotent and could return an existing Program. prog, err := cl.loadProgram(objName) if err != nil { return fmt.Errorf("loading program %s, for map %s: %w", objName, mapName, err) } mapSpec.Contents[i] = MapKV{kv.Key, prog} case t.canStoreMap(): // loadMap is idempotent and could return an existing Map. innerMap, err := cl.loadMap(objName) if err != nil { return fmt.Errorf("loading inner map %s, for map %s: %w", objName, mapName, err) } mapSpec.Contents[i] = MapKV{kv.Key, innerMap} } } } // Populate and freeze the map if specified. if err := m.finalize(mapSpec); err != nil { return fmt.Errorf("populating map %s: %w", mapName, err) } } return nil } // resolveKconfig resolves all variables declared in .kconfig and populates // m.Contents. Does nothing if the given m.Contents is non-empty. func resolveKconfig(m *MapSpec) error { ds, ok := m.Value.(*btf.Datasec) if !ok { return errors.New("map value is not a Datasec") } type configInfo struct { offset uint32 typ btf.Type } configs := make(map[string]configInfo) data := make([]byte, ds.Size) for _, vsi := range ds.Vars { v := vsi.Type.(*btf.Var) n := v.TypeName() switch n { case "LINUX_KERNEL_VERSION": if integer, ok := v.Type.(*btf.Int); !ok || integer.Size != 4 { return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type) } kv, err := internal.KernelVersion() if err != nil { return fmt.Errorf("getting kernel version: %w", err) } internal.NativeEndian.PutUint32(data[vsi.Offset:], kv.Kernel()) case "LINUX_HAS_SYSCALL_WRAPPER": if integer, ok := v.Type.(*btf.Int); !ok || integer.Size != 4 { return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type) } var value uint32 = 1 if err := haveSyscallWrapper(); errors.Is(err, ErrNotSupported) { value = 0 } else if err != nil { return fmt.Errorf("unable to derive a value for LINUX_HAS_SYSCALL_WRAPPER: %w", err) } internal.NativeEndian.PutUint32(data[vsi.Offset:], value) default: // Catch CONFIG_*. configs[n] = configInfo{ offset: vsi.Offset, typ: v.Type, } } } // We only parse kconfig file if a CONFIG_* variable was found. if len(configs) > 0 { f, err := kconfig.Find() if err != nil { return fmt.Errorf("cannot find a kconfig file: %w", err) } defer f.Close() filter := make(map[string]struct{}, len(configs)) for config := range configs { filter[config] = struct{}{} } kernelConfig, err := kconfig.Parse(f, filter) if err != nil { return fmt.Errorf("cannot parse kconfig file: %w", err) } for n, info := range configs { value, ok := kernelConfig[n] if !ok { return fmt.Errorf("config option %q does not exists for this kernel", n) } err := kconfig.PutValue(data[info.offset:], info.typ, value) if err != nil { return fmt.Errorf("problem adding value for %s: %w", n, err) } } } m.Contents = []MapKV{{uint32(0), data}} return nil } // LoadCollection reads an object file and creates and loads its declared // resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func LoadCollection(file string) (*Collection, error) { spec, err := LoadCollectionSpec(file) if err != nil { return nil, err } return NewCollection(spec) } // Close frees all maps and programs associated with the collection. // // The collection mustn't be used afterwards. func (coll *Collection) Close() { for _, prog := range coll.Programs { prog.Close() } for _, m := range coll.Maps { m.Close() } } // DetachMap removes the named map from the Collection. // // This means that a later call to Close() will not affect this map. // // Returns nil if no map of that name exists. func (coll *Collection) DetachMap(name string) *Map { m := coll.Maps[name] delete(coll.Maps, name) return m } // DetachProgram removes the named program from the Collection. // // This means that a later call to Close() will not affect this program. // // Returns nil if no program of that name exists. func (coll *Collection) DetachProgram(name string) *Program { p := coll.Programs[name] delete(coll.Programs, name) return p } // structField represents a struct field containing the ebpf struct tag. type structField struct { reflect.StructField value reflect.Value } // ebpfFields extracts field names tagged with 'ebpf' from a struct type. // Keep track of visited types to avoid infinite recursion. func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) { if visited == nil { visited = make(map[reflect.Type]bool) } structType := structVal.Type() if structType.Kind() != reflect.Struct { return nil, fmt.Errorf("%s is not a struct", structType) } if visited[structType] { return nil, fmt.Errorf("recursion on type %s", structType) } fields := make([]structField, 0, structType.NumField()) for i := 0; i < structType.NumField(); i++ { field := structField{structType.Field(i), structVal.Field(i)} // If the field is tagged, gather it and move on. name := field.Tag.Get("ebpf") if name != "" { fields = append(fields, field) continue } // If the field does not have an ebpf tag, but is a struct or a pointer // to a struct, attempt to gather its fields as well. var v reflect.Value switch field.Type.Kind() { case reflect.Ptr: if field.Type.Elem().Kind() != reflect.Struct { continue } if field.value.IsNil() { return nil, fmt.Errorf("nil pointer to %s", structType) } // Obtain the destination type of the pointer. v = field.value.Elem() case reflect.Struct: // Reference the value's type directly. v = field.value default: continue } inner, err := ebpfFields(v, visited) if err != nil { return nil, fmt.Errorf("field %s: %w", field.Name, err) } fields = append(fields, inner...) } return fields, nil } // assignValues attempts to populate all fields of 'to' tagged with 'ebpf'. // // getValue is called for every tagged field of 'to' and must return the value // to be assigned to the field with the given typ and name. func assignValues(to interface{}, getValue func(typ reflect.Type, name string) (interface{}, error)) error { toValue := reflect.ValueOf(to) if toValue.Type().Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer to struct", to) } if toValue.IsNil() { return fmt.Errorf("nil pointer to %T", to) } fields, err := ebpfFields(toValue.Elem(), nil) if err != nil { return err } type elem struct { // Either *Map or *Program typ reflect.Type name string } assigned := make(map[elem]string) for _, field := range fields { // Get string value the field is tagged with. tag := field.Tag.Get("ebpf") if strings.Contains(tag, ",") { return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) } // Check if the eBPF object with the requested // type and tag was already assigned elsewhere. e := elem{field.Type, tag} if af := assigned[e]; af != "" { return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af) } // Get the eBPF object referred to by the tag. value, err := getValue(field.Type, tag) if err != nil { return fmt.Errorf("field %s: %w", field.Name, err) } if !field.value.CanSet() { return fmt.Errorf("field %s: can't set value", field.Name) } field.value.Set(reflect.ValueOf(value)) assigned[e] = field.Name } return nil } golang-github-cilium-ebpf-0.11.0/collection_test.go000066400000000000000000000351131456504511400222430ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "reflect" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/fdtrace" qt "github.com/frankban/quicktest" ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestCollectionSpecNotModified(t *testing.T) { cs := CollectionSpec{ Maps: map[string]*MapSpec{ "my-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "test": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R1, 0, asm.DWord).WithReference("my-map"), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } coll, err := NewCollection(&cs) if err != nil { t.Fatal(err) } coll.Close() if cs.Programs["test"].Instructions[0].Constant != 0 { t.Error("Creating a collection modifies input spec") } } func TestCollectionSpecCopy(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "my-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "test": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R1, 0), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, Types: &btf.Spec{}, } cpy := cs.Copy() if cpy == cs { t.Error("Copy returned the same pointner") } if cpy.Maps["my-map"] == cs.Maps["my-map"] { t.Error("Copy returned same Maps") } if cpy.Programs["test"] == cs.Programs["test"] { t.Error("Copy returned same Programs") } if cpy.Types != cs.Types { t.Error("Copy returned different Types") } } func TestCollectionSpecLoadCopy(t *testing.T) { file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian) spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } spec2 := spec.Copy() var objs struct { Prog *Program `ebpf:"xdp_prog"` } err = spec.LoadAndAssign(&objs, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Loading original spec:", err) } defer objs.Prog.Close() if err := spec2.LoadAndAssign(&objs, nil); err != nil { t.Fatal("Loading copied spec:", err) } defer objs.Prog.Close() } func TestCollectionSpecRewriteMaps(t *testing.T) { insns := asm.Instructions{ // R1 map asm.LoadMapPtr(asm.R1, 0).WithReference("test-map"), // R2 key asm.Mov.Reg(asm.R2, asm.R10), asm.Add.Imm(asm.R2, -4), asm.StoreImm(asm.R2, 0, 0, asm.Word), // Lookup map[0] asm.FnMapLookupElem.Call(), asm.JEq.Imm(asm.R0, 0, "ret"), asm.LoadMem(asm.R0, asm.R0, 0, asm.Word), asm.Return().WithSymbol("ret"), } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "test-prog": { Type: SocketFilter, Instructions: insns, License: "MIT", }, }, } // Override the map with another one newMap, err := NewMap(cs.Maps["test-map"]) if err != nil { t.Fatal(err) } defer newMap.Close() err = newMap.Put(uint32(0), uint32(2)) if err != nil { t.Fatal(err) } err = cs.RewriteMaps(map[string]*Map{ "test-map": newMap, }) if err != nil { t.Fatal(err) } if cs.Maps["test-map"] != nil { t.Error("RewriteMaps doesn't remove map from CollectionSpec.Maps") } coll, err := NewCollection(cs) if err != nil { t.Fatal(err) } defer coll.Close() ret, _, err := coll.Programs["test-prog"].Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 2 { t.Fatal("new / override map not used") } } func TestCollectionSpecMapReplacements(t *testing.T) { insns := asm.Instructions{ // R1 map asm.LoadMapPtr(asm.R1, 0).WithReference("test-map"), // R2 key asm.Mov.Reg(asm.R2, asm.R10), asm.Add.Imm(asm.R2, -4), asm.StoreImm(asm.R2, 0, 0, asm.Word), // Lookup map[0] asm.FnMapLookupElem.Call(), asm.JEq.Imm(asm.R0, 0, "ret"), asm.LoadMem(asm.R0, asm.R0, 0, asm.Word), asm.Return().WithSymbol("ret"), } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "test-prog": { Type: SocketFilter, Instructions: insns, License: "MIT", }, }, } // Replace the map with another one newMap, err := NewMap(cs.Maps["test-map"]) if err != nil { t.Fatal(err) } defer newMap.Close() err = newMap.Put(uint32(0), uint32(2)) if err != nil { t.Fatal(err) } coll, err := NewCollectionWithOptions(cs, CollectionOptions{ MapReplacements: map[string]*Map{ "test-map": newMap, }, }) if err != nil { t.Fatal(err) } defer coll.Close() ret, _, err := coll.Programs["test-prog"].Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 2 { t.Fatal("new / override map not used") } // Check that newMap isn't closed when the collection is closed coll.Close() err = newMap.Put(uint32(0), uint32(3)) if err != nil { t.Fatalf("failed to update replaced map: %s", err) } } func TestCollectionSpecMapReplacements_NonExistingMap(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, } // Override non-existing map newMap, err := NewMap(cs.Maps["test-map"]) if err != nil { t.Fatal(err) } defer newMap.Close() coll, err := NewCollectionWithOptions(cs, CollectionOptions{ MapReplacements: map[string]*Map{ "non-existing-map": newMap, }, }) if err == nil { coll.Close() t.Fatal("Overriding a non existing map did not fail") } } func TestCollectionSpecMapReplacements_SpecMismatch(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, } // Override map with mismatching spec newMap, err := NewMap(&MapSpec{ Type: Array, KeySize: 4, ValueSize: 8, // this is different MaxEntries: 1, }) if err != nil { t.Fatal(err) } // Map fd is duplicated by MapReplacements, this one can be safely closed. defer newMap.Close() coll, err := NewCollectionWithOptions(cs, CollectionOptions{ MapReplacements: map[string]*Map{ "test-map": newMap, }, }) if err == nil { coll.Close() t.Fatal("Overriding a map with a mismatching spec did not fail") } if !errors.Is(err, ErrMapIncompatible) { t.Fatalf("Overriding a map with a mismatching spec failed with the wrong error") } } func TestCollectionRewriteConstants(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ ".rodata": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Value: &btf.Datasec{ Vars: []btf.VarSecinfo{ { Type: &btf.Var{ Name: "the_constant", Type: &btf.Int{Size: 4}, }, Offset: 0, Size: 4, }, }, }, Contents: []MapKV{ {Key: uint32(0), Value: []byte{1, 1, 1, 1}}, }, }, }, } err := cs.RewriteConstants(map[string]interface{}{ "fake_constant_one": uint32(1), "fake_constant_two": uint32(2), }) qt.Assert(t, err, qt.IsNotNil, qt.Commentf("RewriteConstants did not fail")) var mErr *MissingConstantsError if !errors.As(err, &mErr) { t.Fatal("Error doesn't wrap MissingConstantsError:", err) } qt.Assert(t, mErr.Constants, qt.ContentEquals, []string{"fake_constant_one", "fake_constant_two"}) err = cs.RewriteConstants(map[string]interface{}{ "the_constant": uint32(0x42424242), }) qt.Assert(t, err, qt.IsNil) qt.Assert(t, cs.Maps[".rodata"].Contents[0].Value, qt.ContentEquals, []byte{0x42, 0x42, 0x42, 0x42}) } func TestCollectionSpec_LoadAndAssign_LazyLoading(t *testing.T) { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "valid": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, "bogus": { Type: Array, MaxEntries: 0, }, }, Programs: map[string]*ProgramSpec{ "valid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, "bogus": { Type: SocketFilter, Instructions: asm.Instructions{ // Undefined return value is rejected asm.Return(), }, License: "MIT", }, }, } var objs struct { Prog *Program `ebpf:"valid"` Map *Map `ebpf:"valid"` } if err := spec.LoadAndAssign(&objs, nil); err != nil { t.Fatal("Assign loads a map or program that isn't requested in the struct:", err) } defer objs.Prog.Close() defer objs.Map.Close() if objs.Prog == nil { t.Error("Program is nil") } if objs.Map == nil { t.Error("Map is nil") } } func TestCollectionAssign(t *testing.T) { var specs struct { Program *ProgramSpec `ebpf:"prog1"` Map *MapSpec `ebpf:"map1"` } mapSpec := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, } progSpec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": mapSpec, }, Programs: map[string]*ProgramSpec{ "prog1": progSpec, }, } if err := cs.Assign(&specs); err != nil { t.Fatal("Can't assign spec:", err) } if specs.Program != progSpec { t.Fatalf("Expected Program to be %p, got %p", progSpec, specs.Program) } if specs.Map != mapSpec { t.Fatalf("Expected Map to be %p, got %p", mapSpec, specs.Map) } if err := cs.Assign(new(int)); err == nil { t.Fatal("Assign allows to besides *struct") } if err := cs.Assign(new(struct{ Foo int })); err != nil { t.Fatal("Assign doesn't ignore untagged fields") } unexported := new(struct { foo *MapSpec `ebpf:"map1"` }) if err := cs.Assign(unexported); err == nil { t.Error("Assign should return an error on unexported fields") } } func TestAssignValues(t *testing.T) { zero := func(t reflect.Type, name string) (interface{}, error) { return reflect.Zero(t).Interface(), nil } type t1 struct { Bar int `ebpf:"bar"` } type t2 struct { t1 Foo int `ebpf:"foo"` } type t2ptr struct { *t1 Foo int `ebpf:"foo"` } invalid := []struct { name string to interface{} }{ {"non-struct", 1}, {"non-pointer struct", t1{}}, {"pointer to non-struct", new(int)}, {"embedded nil pointer", &t2ptr{}}, {"unexported field", new(struct { foo int `ebpf:"foo"` })}, {"identical tag", new(struct { Foo1 int `ebpf:"foo"` Foo2 int `ebpf:"foo"` })}, } for _, testcase := range invalid { t.Run(testcase.name, func(t *testing.T) { if err := assignValues(testcase.to, zero); err == nil { t.Fatal("assignValues didn't return an error") } else { t.Log(err) } }) } valid := []struct { name string to interface{} }{ {"pointer to struct", new(t1)}, {"embedded struct", new(t2)}, {"embedded struct pointer", &t2ptr{t1: new(t1)}}, {"untagged field", new(struct{ Foo int })}, } for _, testcase := range valid { t.Run(testcase.name, func(t *testing.T) { if err := assignValues(testcase.to, zero); err != nil { t.Fatal("assignValues returned", err) } }) } } func TestIncompleteLoadAndAssign(t *testing.T) { spec := &CollectionSpec{ Programs: map[string]*ProgramSpec{ "valid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, "invalid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.Return(), }, License: "MIT", }, }, } s := struct { // Assignment to Valid should execute and succeed. Valid *Program `ebpf:"valid"` // Assignment to Invalid should fail and cause Valid's fd to be closed. Invalid *Program `ebpf:"invalid"` }{} if err := spec.LoadAndAssign(&s, nil); err == nil { t.Fatal("expected error loading invalid ProgramSpec") } if s.Valid == nil { t.Fatal("expected valid prog to be non-nil") } if fd := s.Valid.FD(); fd != -1 { t.Fatal("expected valid prog to have closed fd -1, got:", fd) } if s.Invalid != nil { t.Fatal("expected invalid prog to be nil due to never being assigned") } } func BenchmarkNewCollection(b *testing.B) { file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian) spec, err := LoadCollectionSpec(file) if err != nil { b.Fatal(err) } spec.Maps["array_of_hash_map"].InnerMap = spec.Maps["hash_map"] for _, m := range spec.Maps { m.Pinning = PinNone } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { coll, err := NewCollection(spec) if err != nil { b.Fatal(err) } coll.Close() } } func BenchmarkNewCollectionManyProgs(b *testing.B) { file := fmt.Sprintf("testdata/manyprogs-%s.elf", internal.ClangEndian) spec, err := LoadCollectionSpec(file) if err != nil { b.Fatal(err) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { coll, err := NewCollection(spec) if err != nil { b.Fatal(err) } coll.Close() } } func ExampleCollectionSpec_Assign() { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } type maps struct { Map *MapSpec `ebpf:"map1"` } var specs struct { maps Program *ProgramSpec `ebpf:"prog1"` } if err := spec.Assign(&specs); err != nil { panic(err) } fmt.Println(specs.Program.Type) fmt.Println(specs.Map.Type) // Output: SocketFilter // Array } func ExampleCollectionSpec_LoadAndAssign() { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } var objs struct { Program *Program `ebpf:"prog1"` Map *Map `ebpf:"map1"` } if err := spec.LoadAndAssign(&objs, nil); err != nil { panic(err) } defer objs.Program.Close() defer objs.Map.Close() fmt.Println(objs.Program.Type()) fmt.Println(objs.Map.Type()) // Output: SocketFilter // Array } golang-github-cilium-ebpf-0.11.0/doc.go000066400000000000000000000025161456504511400176170ustar00rootroot00000000000000// Package ebpf is a toolkit for working with eBPF programs. // // eBPF programs are small snippets of code which are executed directly // in a VM in the Linux kernel, which makes them very fast and flexible. // Many Linux subsystems now accept eBPF programs. This makes it possible // to implement highly application specific logic inside the kernel, // without having to modify the actual kernel itself. // // This package is designed for long-running processes which // want to use eBPF to implement part of their application logic. It has no // run-time dependencies outside of the library and the Linux kernel itself. // eBPF code should be compiled ahead of time using clang, and shipped with // your application as any other resource. // // Use the link subpackage to attach a loaded program to a hook in the kernel. // // Note that losing all references to Map and Program resources will cause // their underlying file descriptors to be closed, potentially removing those // objects from the kernel. Always retain a reference by e.g. deferring a // Close() of a Collection or LoadAndAssign object until application exit. // // Special care needs to be taken when handling maps of type ProgramArray, // as the kernel erases its contents when the last userspace or bpffs // reference disappears, regardless of the map being in active use. package ebpf golang-github-cilium-ebpf-0.11.0/elf_reader.go000066400000000000000000001166261456504511400211520ustar00rootroot00000000000000package ebpf import ( "bufio" "bytes" "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) type kconfigMetaKey struct{} type kconfigMeta struct { Map *MapSpec Offset uint32 } type kfuncMeta struct{} // elfCode is a convenience to reduce the amount of arguments that have to // be passed around explicitly. You should treat its contents as immutable. type elfCode struct { *internal.SafeELFFile sections map[elf.SectionIndex]*elfSection license string version uint32 btf *btf.Spec extInfo *btf.ExtInfos maps map[string]*MapSpec kfuncs map[string]*btf.Func kconfig *MapSpec } // LoadCollectionSpec parses an ELF file into a CollectionSpec. func LoadCollectionSpec(file string) (*CollectionSpec, error) { f, err := os.Open(file) if err != nil { return nil, err } defer f.Close() spec, err := LoadCollectionSpecFromReader(f) if err != nil { return nil, fmt.Errorf("file %s: %w", file, err) } return spec, nil } // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { f, err := internal.NewSafeELFFile(rd) if err != nil { return nil, err } // Checks if the ELF file is for BPF data. // Old LLVM versions set e_machine to EM_NONE. if f.File.Machine != unix.EM_NONE && f.File.Machine != elf.EM_BPF { return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.File.Machine) } var ( licenseSection *elf.Section versionSection *elf.Section sections = make(map[elf.SectionIndex]*elfSection) relSections = make(map[elf.SectionIndex]*elf.Section) ) // This is the target of relocations generated by inline assembly. sections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection) // Collect all the sections we're interested in. This includes relocations // which we parse later. for i, sec := range f.Sections { idx := elf.SectionIndex(i) switch { case strings.HasPrefix(sec.Name, "license"): licenseSection = sec case strings.HasPrefix(sec.Name, "version"): versionSection = sec case strings.HasPrefix(sec.Name, "maps"): sections[idx] = newElfSection(sec, mapSection) case sec.Name == ".maps": sections[idx] = newElfSection(sec, btfMapSection) case sec.Name == ".bss" || sec.Name == ".data" || strings.HasPrefix(sec.Name, ".rodata"): sections[idx] = newElfSection(sec, dataSection) case sec.Type == elf.SHT_REL: // Store relocations under the section index of the target relSections[elf.SectionIndex(sec.Info)] = sec case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: sections[idx] = newElfSection(sec, programSection) } } license, err := loadLicense(licenseSection) if err != nil { return nil, fmt.Errorf("load license: %w", err) } version, err := loadVersion(versionSection, f.ByteOrder) if err != nil { return nil, fmt.Errorf("load version: %w", err) } btfSpec, btfExtInfo, err := btf.LoadSpecAndExtInfosFromReader(rd) if err != nil && !errors.Is(err, btf.ErrNotFound) { return nil, fmt.Errorf("load BTF: %w", err) } ec := &elfCode{ SafeELFFile: f, sections: sections, license: license, version: version, btf: btfSpec, extInfo: btfExtInfo, maps: make(map[string]*MapSpec), kfuncs: make(map[string]*btf.Func), } symbols, err := f.Symbols() if err != nil { return nil, fmt.Errorf("load symbols: %v", err) } ec.assignSymbols(symbols) if err := ec.loadRelocations(relSections, symbols); err != nil { return nil, fmt.Errorf("load relocations: %w", err) } if err := ec.loadMaps(); err != nil { return nil, fmt.Errorf("load maps: %w", err) } if err := ec.loadBTFMaps(); err != nil { return nil, fmt.Errorf("load BTF maps: %w", err) } if err := ec.loadDataSections(); err != nil { return nil, fmt.Errorf("load data sections: %w", err) } if err := ec.loadKconfigSection(); err != nil { return nil, fmt.Errorf("load virtual .kconfig section: %w", err) } if err := ec.loadKsymsSection(); err != nil { return nil, fmt.Errorf("load virtual .ksyms section: %w", err) } // Finally, collect programs and link them. progs, err := ec.loadProgramSections() if err != nil { return nil, fmt.Errorf("load programs: %w", err) } return &CollectionSpec{ec.maps, progs, btfSpec, ec.ByteOrder}, nil } func loadLicense(sec *elf.Section) (string, error) { if sec == nil { return "", nil } data, err := sec.Data() if err != nil { return "", fmt.Errorf("section %s: %v", sec.Name, err) } return string(bytes.TrimRight(data, "\000")), nil } func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { if sec == nil { return 0, nil } var version uint32 if err := binary.Read(sec.Open(), bo, &version); err != nil { return 0, fmt.Errorf("section %s: %v", sec.Name, err) } return version, nil } type elfSectionKind int const ( undefSection elfSectionKind = iota mapSection btfMapSection programSection dataSection ) type elfSection struct { *elf.Section kind elfSectionKind // Offset from the start of the section to a symbol symbols map[uint64]elf.Symbol // Offset from the start of the section to a relocation, which points at // a symbol in another section. relocations map[uint64]elf.Symbol // The number of relocations pointing at this section. references int } func newElfSection(section *elf.Section, kind elfSectionKind) *elfSection { return &elfSection{ section, kind, make(map[uint64]elf.Symbol), make(map[uint64]elf.Symbol), 0, } } // assignSymbols takes a list of symbols and assigns them to their // respective sections, indexed by name. func (ec *elfCode) assignSymbols(symbols []elf.Symbol) { for _, symbol := range symbols { symType := elf.ST_TYPE(symbol.Info) symSection := ec.sections[symbol.Section] if symSection == nil { continue } // Anonymous symbols only occur in debug sections which we don't process // relocations for. Anonymous symbols are not referenced from other sections. if symbol.Name == "" { continue } // Older versions of LLVM don't tag symbols correctly, so keep // all NOTYPE ones. switch symSection.kind { case mapSection, btfMapSection, dataSection: if symType != elf.STT_NOTYPE && symType != elf.STT_OBJECT { continue } case programSection: if symType != elf.STT_NOTYPE && symType != elf.STT_FUNC { continue } // LLVM emits LBB_ (Local Basic Block) symbols that seem to be jump // targets within sections, but BPF has no use for them. if symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL && strings.HasPrefix(symbol.Name, "LBB") { continue } // Only collect symbols that occur in program/maps/data sections. default: continue } symSection.symbols[symbol.Value] = symbol } } // loadRelocations iterates .rel* sections and extracts relocation entries for // sections of interest. Makes sure relocations point at valid sections. func (ec *elfCode) loadRelocations(relSections map[elf.SectionIndex]*elf.Section, symbols []elf.Symbol) error { for idx, relSection := range relSections { section := ec.sections[idx] if section == nil { continue } rels, err := ec.loadSectionRelocations(relSection, symbols) if err != nil { return fmt.Errorf("relocation for section %q: %w", section.Name, err) } for _, rel := range rels { target := ec.sections[rel.Section] if target == nil { return fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported) } target.references++ } section.relocations = rels } return nil } // loadProgramSections iterates ec's sections and emits a ProgramSpec // for each function it finds. // // The resulting map is indexed by function name. func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) { progs := make(map[string]*ProgramSpec) // Generate a ProgramSpec for each function found in each program section. var export []string for _, sec := range ec.sections { if sec.kind != programSection { continue } if len(sec.symbols) == 0 { return nil, fmt.Errorf("section %v: missing symbols", sec.Name) } funcs, err := ec.loadFunctions(sec) if err != nil { return nil, fmt.Errorf("section %v: %w", sec.Name, err) } progType, attachType, progFlags, attachTo := getProgType(sec.Name) for name, insns := range funcs { spec := &ProgramSpec{ Name: name, Type: progType, Flags: progFlags, AttachType: attachType, AttachTo: attachTo, SectionName: sec.Name, License: ec.license, KernelVersion: ec.version, Instructions: insns, ByteOrder: ec.ByteOrder, } // Function names must be unique within a single ELF blob. if progs[name] != nil { return nil, fmt.Errorf("duplicate program name %s", name) } progs[name] = spec if spec.SectionName != ".text" { export = append(export, name) } } } flattenPrograms(progs, export) // Hide programs (e.g. library functions) that were not explicitly emitted // to an ELF section. These could be exposed in a separate CollectionSpec // field later to allow them to be modified. for n, p := range progs { if p.SectionName == ".text" { delete(progs, n) } } return progs, nil } // loadFunctions extracts instruction streams from the given program section // starting at each symbol in the section. The section's symbols must already // be narrowed down to STT_NOTYPE (emitted by clang <8) or STT_FUNC. // // The resulting map is indexed by function name. func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructions, error) { r := bufio.NewReader(section.Open()) // Decode the section's instruction stream. var insns asm.Instructions if err := insns.Unmarshal(r, ec.ByteOrder); err != nil { return nil, fmt.Errorf("decoding instructions for section %s: %w", section.Name, err) } if len(insns) == 0 { return nil, fmt.Errorf("no instructions found in section %s", section.Name) } iter := insns.Iterate() for iter.Next() { ins := iter.Ins offset := iter.Offset.Bytes() // Tag Symbol Instructions. if sym, ok := section.symbols[offset]; ok { *ins = ins.WithSymbol(sym.Name) } // Apply any relocations for the current instruction. // If no relocation is present, resolve any section-relative function calls. if rel, ok := section.relocations[offset]; ok { if err := ec.relocateInstruction(ins, rel); err != nil { return nil, fmt.Errorf("offset %d: relocating instruction: %w", offset, err) } } else { if err := referenceRelativeJump(ins, offset, section.symbols); err != nil { return nil, fmt.Errorf("offset %d: resolving relative jump: %w", offset, err) } } } if ec.extInfo != nil { ec.extInfo.Assign(insns, section.Name) } return splitSymbols(insns) } // referenceRelativeJump turns a relative jump to another bpf subprogram within // the same ELF section into a Reference Instruction. // // Up to LLVM 9, calls to subprograms within the same ELF section are sometimes // encoded using relative jumps instead of relocation entries. These jumps go // out of bounds of the current program, so their targets must be memoized // before the section's instruction stream is split. // // The relative jump Constant is blinded to -1 and the target Symbol is set as // the Instruction's Reference so it can be resolved by the linker. func referenceRelativeJump(ins *asm.Instruction, offset uint64, symbols map[uint64]elf.Symbol) error { if !ins.IsFunctionReference() || ins.Constant == -1 { return nil } tgt := jumpTarget(offset, *ins) sym := symbols[tgt].Name if sym == "" { return fmt.Errorf("no jump target found at offset %d", tgt) } *ins = ins.WithReference(sym) ins.Constant = -1 return nil } // jumpTarget takes ins' offset within an instruction stream (in bytes) // and returns its absolute jump destination (in bytes) within the // instruction stream. func jumpTarget(offset uint64, ins asm.Instruction) uint64 { // A relative jump instruction describes the amount of raw BPF instructions // to jump, convert the offset into bytes. dest := ins.Constant * asm.InstructionSize // The starting point of the jump is the end of the current instruction. dest += int64(offset + asm.InstructionSize) if dest < 0 { return 0 } return uint64(dest) } func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error { var ( typ = elf.ST_TYPE(rel.Info) bind = elf.ST_BIND(rel.Info) name = rel.Name ) target := ec.sections[rel.Section] switch target.kind { case mapSection, btfMapSection: if bind != elf.STB_GLOBAL { return fmt.Errorf("possible erroneous static qualifier on map definition: found reference to %q", name) } if typ != elf.STT_OBJECT && typ != elf.STT_NOTYPE { // STT_NOTYPE is generated on clang < 8 which doesn't tag // relocations appropriately. return fmt.Errorf("map load: incorrect relocation type %v", typ) } ins.Src = asm.PseudoMapFD case dataSection: var offset uint32 switch typ { case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: unsupported section relocation %s", name, bind) } // This is really a reference to a static symbol, which clang doesn't // emit a symbol table entry for. Instead it encodes the offset in // the instruction itself. offset = uint32(uint64(ins.Constant)) case elf.STT_OBJECT: // LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants. if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: unsupported object relocation %s", name, bind) } offset = uint32(rel.Value) case elf.STT_NOTYPE: // LLVM 7 emits NOTYPE-LOCAL symbols for anonymous constants. if bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: unsupported untyped relocation %s", name, bind) } offset = uint32(rel.Value) default: return fmt.Errorf("incorrect relocation type %v for direct map load", typ) } // We rely on using the name of the data section as the reference. It // would be nicer to keep the real name in case of an STT_OBJECT, but // it's not clear how to encode that into Instruction. name = target.Name // The kernel expects the offset in the second basic BPF instruction. ins.Constant = int64(uint64(offset) << 32) ins.Src = asm.PseudoMapValue case programSection: switch opCode := ins.OpCode; { case opCode.JumpOp() == asm.Call: if ins.Src != asm.PseudoCall { return fmt.Errorf("call: %s: incorrect source register", name) } switch typ { case elf.STT_NOTYPE, elf.STT_FUNC: if bind != elf.STB_GLOBAL { return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) } case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) } // The function we want to call is in the indicated section, // at the offset encoded in the instruction itself. Reverse // the calculation to find the real function we're looking for. // A value of -1 references the first instruction in the section. offset := int64(int32(ins.Constant)+1) * asm.InstructionSize sym, ok := target.symbols[uint64(offset)] if !ok { return fmt.Errorf("call: no symbol at offset %d", offset) } name = sym.Name ins.Constant = -1 default: return fmt.Errorf("call: %s: invalid symbol type %s", name, typ) } case opCode.IsDWordLoad(): switch typ { case elf.STT_FUNC: if bind != elf.STB_GLOBAL { return fmt.Errorf("load: %s: unsupported binding: %s", name, bind) } case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("load: %s: unsupported binding: %s", name, bind) } // ins.Constant already contains the offset in bytes from the // start of the section. This is different than a call to a // static function. default: return fmt.Errorf("load: %s: invalid symbol type %s", name, typ) } sym, ok := target.symbols[uint64(ins.Constant)] if !ok { return fmt.Errorf("load: no symbol at offset %d", ins.Constant) } name = sym.Name ins.Constant = -1 ins.Src = asm.PseudoFunc default: return fmt.Errorf("neither a call nor a load instruction: %v", ins) } // The Undefined section is used for 'virtual' symbols that aren't backed by // an ELF section. This includes symbol references from inline asm, forward // function declarations, as well as extern kfunc declarations using __ksym // and extern kconfig variables declared using __kconfig. case undefSection: if bind != elf.STB_GLOBAL { return fmt.Errorf("asm relocation: %s: unsupported binding: %s", name, bind) } if typ != elf.STT_NOTYPE { return fmt.Errorf("asm relocation: %s: unsupported type %s", name, typ) } kf := ec.kfuncs[name] switch { // If a Call instruction is found and the datasec has a btf.Func with a Name // that matches the symbol name we mark the instruction as a call to a kfunc. case kf != nil && ins.OpCode.JumpOp() == asm.Call: ins.Metadata.Set(kfuncMeta{}, kf) ins.Src = asm.PseudoKfuncCall ins.Constant = -1 // If no kconfig map is found, this must be a symbol reference from inline // asm (see testdata/loader.c:asm_relocation()) or a call to a forward // function declaration (see testdata/fwd_decl.c). Don't interfere, These // remain standard symbol references. // extern __kconfig reads are represented as dword loads that need to be // rewritten to pseudo map loads from .kconfig. If the map is present, // require it to contain the symbol to disambiguate between inline asm // relos and kconfigs. case ec.kconfig != nil && ins.OpCode.IsDWordLoad(): for _, vsi := range ec.kconfig.Value.(*btf.Datasec).Vars { if vsi.Type.(*btf.Var).Name != rel.Name { continue } ins.Src = asm.PseudoMapValue ins.Metadata.Set(kconfigMetaKey{}, &kconfigMeta{ec.kconfig, vsi.Offset}) return nil } return fmt.Errorf("kconfig %s not found in .kconfig", rel.Name) } default: return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported) } *ins = ins.WithReference(name) return nil } func (ec *elfCode) loadMaps() error { for _, sec := range ec.sections { if sec.kind != mapSection { continue } nSym := len(sec.symbols) if nSym == 0 { return fmt.Errorf("section %v: no symbols", sec.Name) } if sec.Size%uint64(nSym) != 0 { return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name) } var ( r = bufio.NewReader(sec.Open()) size = sec.Size / uint64(nSym) ) for i, offset := 0, uint64(0); i < nSym; i, offset = i+1, offset+size { mapSym, ok := sec.symbols[offset] if !ok { return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) } mapName := mapSym.Name if ec.maps[mapName] != nil { return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym) } lr := io.LimitReader(r, int64(size)) spec := MapSpec{ Name: SanitizeName(mapName, -1), } switch { case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: return fmt.Errorf("map %s: missing type", mapName) case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: return fmt.Errorf("map %s: missing key size", mapName) case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: return fmt.Errorf("map %s: missing value size", mapName) case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: return fmt.Errorf("map %s: missing max entries", mapName) case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: return fmt.Errorf("map %s: missing flags", mapName) } extra, err := io.ReadAll(lr) if err != nil { return fmt.Errorf("map %s: reading map tail: %w", mapName, err) } if len(extra) > 0 { spec.Extra = bytes.NewReader(extra) } if err := spec.clampPerfEventArraySize(); err != nil { return fmt.Errorf("map %s: %w", mapName, err) } ec.maps[mapName] = &spec } } return nil } // loadBTFMaps iterates over all ELF sections marked as BTF map sections // (like .maps) and parses them into MapSpecs. Dump the .maps section and // any relocations with `readelf -x .maps -r `. func (ec *elfCode) loadBTFMaps() error { for _, sec := range ec.sections { if sec.kind != btfMapSection { continue } if ec.btf == nil { return fmt.Errorf("missing BTF") } // Each section must appear as a DataSec in the ELF's BTF blob. var ds *btf.Datasec if err := ec.btf.TypeByName(sec.Name, &ds); err != nil { return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err) } // Open a Reader to the ELF's raw section bytes so we can assert that all // of them are zero on a per-map (per-Var) basis. For now, the section's // sole purpose is to receive relocations, so all must be zero. rs := sec.Open() for _, vs := range ds.Vars { // BPF maps are declared as and assigned to global variables, // so iterate over each Var in the DataSec and validate their types. v, ok := vs.Type.(*btf.Var) if !ok { return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type) } name := string(v.Name) // The BTF metadata for each Var contains the full length of the map // declaration, so read the corresponding amount of bytes from the ELF. // This way, we can pinpoint which map declaration contains unexpected // (and therefore unsupported) data. _, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size))) if err != nil { return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported) } if ec.maps[name] != nil { return fmt.Errorf("section %v: map %s already exists", sec.Name, name) } // Each Var representing a BTF map definition contains a Struct. mapStruct, ok := v.Type.(*btf.Struct) if !ok { return fmt.Errorf("expected struct, got %s", v.Type) } mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false) if err != nil { return fmt.Errorf("map %v: %w", name, err) } if err := mapSpec.clampPerfEventArraySize(); err != nil { return fmt.Errorf("map %v: %w", name, err) } ec.maps[name] = mapSpec } // Drain the ELF section reader to make sure all bytes are accounted for // with BTF metadata. i, err := io.Copy(io.Discard, rs) if err != nil { return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err) } if i > 0 { return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i) } } return nil } // mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing // a BTF map definition. The name and spec arguments will be copied to the // resulting MapSpec, and inner must be true on any resursive invocations. func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) { var ( key, value btf.Type keySize, valueSize uint32 mapType MapType flags, maxEntries uint32 pinType PinType innerMapSpec *MapSpec contents []MapKV err error ) for i, member := range def.Members { switch member.Name { case "type": mt, err := uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get type: %w", err) } mapType = MapType(mt) case "map_flags": flags, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF map flags: %w", err) } case "max_entries": maxEntries, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF map max entries: %w", err) } case "key": if keySize != 0 { return nil, errors.New("both key and key_size given") } pk, ok := member.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("key type is not a pointer: %T", member.Type) } key = pk.Target size, err := btf.Sizeof(pk.Target) if err != nil { return nil, fmt.Errorf("can't get size of BTF key: %w", err) } keySize = uint32(size) case "value": if valueSize != 0 { return nil, errors.New("both value and value_size given") } vk, ok := member.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("value type is not a pointer: %T", member.Type) } value = vk.Target size, err := btf.Sizeof(vk.Target) if err != nil { return nil, fmt.Errorf("can't get size of BTF value: %w", err) } valueSize = uint32(size) case "key_size": // Key needs to be nil and keySize needs to be 0 for key_size to be // considered a valid member. if key != nil || keySize != 0 { return nil, errors.New("both key and key_size given") } keySize, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF key size: %w", err) } case "value_size": // Value needs to be nil and valueSize needs to be 0 for value_size to be // considered a valid member. if value != nil || valueSize != 0 { return nil, errors.New("both value and value_size given") } valueSize, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF value size: %w", err) } case "pinning": if inner { return nil, errors.New("inner maps can't be pinned") } pinning, err := uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get pinning: %w", err) } pinType = PinType(pinning) case "values": // The 'values' field in BTF map definitions is used for declaring map // value types that are references to other BPF objects, like other maps // or programs. It is always expected to be an array of pointers. if i != len(def.Members)-1 { return nil, errors.New("'values' must be the last member in a BTF map definition") } if valueSize != 0 && valueSize != 4 { return nil, errors.New("value_size must be 0 or 4") } valueSize = 4 valueType, err := resolveBTFArrayMacro(member.Type) if err != nil { return nil, fmt.Errorf("can't resolve type of member 'values': %w", err) } switch t := valueType.(type) { case *btf.Struct: // The values member pointing to an array of structs means we're expecting // a map-in-map declaration. if mapType != ArrayOfMaps && mapType != HashOfMaps { return nil, errors.New("outer map needs to be an array or a hash of maps") } if inner { return nil, fmt.Errorf("nested inner maps are not supported") } // This inner map spec is used as a map template, but it needs to be // created as a traditional map before it can be used to do so. // libbpf names the inner map template '.inner', but we // opted for _inner to simplify validation logic. (dots only supported // on kernels 5.2 and up) // Pass the BTF spec from the parent object, since both parent and // child must be created from the same BTF blob (on kernels that support BTF). innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true) if err != nil { return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err) } case *btf.FuncProto: // The values member contains an array of function pointers, meaning an // autopopulated PROG_ARRAY. if mapType != ProgramArray { return nil, errors.New("map needs to be a program array") } default: return nil, fmt.Errorf("unsupported value type %q in 'values' field", t) } contents, err = resolveBTFValuesContents(es, vs, member) if err != nil { return nil, fmt.Errorf("resolving values contents: %w", err) } default: return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) } } return &MapSpec{ Name: SanitizeName(name, -1), Type: MapType(mapType), KeySize: keySize, ValueSize: valueSize, MaxEntries: maxEntries, Flags: flags, Key: key, Value: value, Pinning: pinType, InnerMap: innerMapSpec, Contents: contents, }, nil } // uintFromBTF resolves the __uint macro, which is a pointer to a sized // array, e.g. for int (*foo)[10], this function will return 10. func uintFromBTF(typ btf.Type) (uint32, error) { ptr, ok := typ.(*btf.Pointer) if !ok { return 0, fmt.Errorf("not a pointer: %v", typ) } arr, ok := ptr.Target.(*btf.Array) if !ok { return 0, fmt.Errorf("not a pointer to array: %v", typ) } return arr.Nelems, nil } // resolveBTFArrayMacro resolves the __array macro, which declares an array // of pointers to a given type. This function returns the target Type of // the pointers in the array. func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) { arr, ok := typ.(*btf.Array) if !ok { return nil, fmt.Errorf("not an array: %v", typ) } ptr, ok := arr.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("not an array of pointers: %v", typ) } return ptr.Target, nil } // resolveBTFValuesContents resolves relocations into ELF sections belonging // to btf.VarSecinfo's. This can be used on the 'values' member in BTF map // definitions to extract static declarations of map contents. func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) { // The elements of a .values pointer array are not encoded in BTF. // Instead, relocations are generated into each array index. // However, it's possible to leave certain array indices empty, so all // indices' offsets need to be checked for emitted relocations. // The offset of the 'values' member within the _struct_ (in bits) // is the starting point of the array. Convert to bytes. Add VarSecinfo // offset to get the absolute position in the ELF blob. start := member.Offset.Bytes() + vs.Offset // 'values' is encoded in BTF as a zero (variable) length struct // member, and its contents run until the end of the VarSecinfo. // Add VarSecinfo offset to get the absolute position in the ELF blob. end := vs.Size + vs.Offset // The size of an address in this section. This determines the width of // an index in the array. align := uint32(es.SectionHeader.Addralign) // Check if variable-length section is aligned. if (end-start)%align != 0 { return nil, errors.New("unaligned static values section") } elems := (end - start) / align if elems == 0 { return nil, nil } contents := make([]MapKV, 0, elems) // k is the array index, off is its corresponding ELF section offset. for k, off := uint32(0), start; k < elems; k, off = k+1, off+align { r, ok := es.relocations[uint64(off)] if !ok { continue } // Relocation exists for the current offset in the ELF section. // Emit a value stub based on the type of relocation to be replaced by // a real fd later in the pipeline before populating the map. // Map keys are encoded in MapKV entries, so empty array indices are // skipped here. switch t := elf.ST_TYPE(r.Info); t { case elf.STT_FUNC: contents = append(contents, MapKV{uint32(k), r.Name}) case elf.STT_OBJECT: contents = append(contents, MapKV{uint32(k), r.Name}) default: return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, r.Name) } } return contents, nil } func (ec *elfCode) loadDataSections() error { for _, sec := range ec.sections { if sec.kind != dataSection { continue } if sec.references == 0 { // Prune data sections which are not referenced by any // instructions. continue } mapSpec := &MapSpec{ Name: SanitizeName(sec.Name, -1), Type: Array, KeySize: 4, ValueSize: uint32(sec.Size), MaxEntries: 1, } switch sec.Type { // Only open the section if we know there's actual data to be read. case elf.SHT_PROGBITS: data, err := sec.Data() if err != nil { return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) } if uint64(len(data)) > math.MaxUint32 { return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) } mapSpec.Contents = []MapKV{{uint32(0), data}} case elf.SHT_NOBITS: // NOBITS sections like .bss contain only zeroes, and since data sections // are Arrays, the kernel already preallocates them. Skip reading zeroes // from the ELF. default: return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type) } // It is possible for a data section to exist without a corresponding BTF Datasec // if it only contains anonymous values like macro-defined arrays. if ec.btf != nil { var ds *btf.Datasec if ec.btf.TypeByName(sec.Name, &ds) == nil { // Assign the spec's key and BTF only if the Datasec lookup was successful. mapSpec.Key = &btf.Void{} mapSpec.Value = ds } } if strings.HasPrefix(sec.Name, ".rodata") { mapSpec.Flags = unix.BPF_F_RDONLY_PROG mapSpec.Freeze = true } ec.maps[sec.Name] = mapSpec } return nil } // loadKconfigSection handles the 'virtual' Datasec .kconfig that doesn't // have a corresponding ELF section and exist purely in BTF. func (ec *elfCode) loadKconfigSection() error { if ec.btf == nil { return nil } var ds *btf.Datasec err := ec.btf.TypeByName(".kconfig", &ds) if errors.Is(err, btf.ErrNotFound) { return nil } if err != nil { return err } if ds.Size == 0 { return errors.New("zero-length .kconfig") } ec.kconfig = &MapSpec{ Name: ".kconfig", Type: Array, KeySize: uint32(4), ValueSize: ds.Size, MaxEntries: 1, Flags: unix.BPF_F_RDONLY_PROG | unix.BPF_F_MMAPABLE, Freeze: true, Key: &btf.Int{Size: 4}, Value: ds, } return nil } // loadKsymsSection handles the 'virtual' Datasec .ksyms that doesn't // have a corresponding ELF section and exist purely in BTF. func (ec *elfCode) loadKsymsSection() error { if ec.btf == nil { return nil } var ds *btf.Datasec err := ec.btf.TypeByName(".ksyms", &ds) if errors.Is(err, btf.ErrNotFound) { return nil } if err != nil { return err } for _, v := range ds.Vars { // we have already checked the .ksyms Datasec to only contain Func Vars. ec.kfuncs[v.Type.TypeName()] = v.Type.(*btf.Func) } return nil } func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { types := []struct { prefix string progType ProgramType attachType AttachType progFlags uint32 }{ // Please update the types from libbpf.c and follow the order of it. // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c {"socket", SocketFilter, AttachNone, 0}, {"sk_reuseport/migrate", SkReuseport, AttachSkReuseportSelectOrMigrate, 0}, {"sk_reuseport", SkReuseport, AttachSkReuseportSelect, 0}, {"kprobe/", Kprobe, AttachNone, 0}, {"uprobe/", Kprobe, AttachNone, 0}, {"kretprobe/", Kprobe, AttachNone, 0}, {"uretprobe/", Kprobe, AttachNone, 0}, {"tc", SchedCLS, AttachNone, 0}, {"classifier", SchedCLS, AttachNone, 0}, {"action", SchedACT, AttachNone, 0}, {"tracepoint/", TracePoint, AttachNone, 0}, {"tp/", TracePoint, AttachNone, 0}, {"raw_tracepoint/", RawTracepoint, AttachNone, 0}, {"raw_tp/", RawTracepoint, AttachNone, 0}, {"raw_tracepoint.w/", RawTracepointWritable, AttachNone, 0}, {"raw_tp.w/", RawTracepointWritable, AttachNone, 0}, {"tp_btf/", Tracing, AttachTraceRawTp, 0}, {"fentry/", Tracing, AttachTraceFEntry, 0}, {"fmod_ret/", Tracing, AttachModifyReturn, 0}, {"fexit/", Tracing, AttachTraceFExit, 0}, {"fentry.s/", Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE}, {"fmod_ret.s/", Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE}, {"fexit.s/", Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE}, {"freplace/", Extension, AttachNone, 0}, {"lsm/", LSM, AttachLSMMac, 0}, {"lsm.s/", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE}, {"iter/", Tracing, AttachTraceIter, 0}, {"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE}, {"syscall", Syscall, AttachNone, 0}, {"xdp.frags_devmap/", XDP, AttachXDPDevMap, unix.BPF_F_XDP_HAS_FRAGS}, {"xdp_devmap/", XDP, AttachXDPDevMap, 0}, {"xdp.frags_cpumap/", XDP, AttachXDPCPUMap, unix.BPF_F_XDP_HAS_FRAGS}, {"xdp_cpumap/", XDP, AttachXDPCPUMap, 0}, {"xdp.frags", XDP, AttachNone, unix.BPF_F_XDP_HAS_FRAGS}, {"xdp", XDP, AttachNone, 0}, {"perf_event", PerfEvent, AttachNone, 0}, {"lwt_in", LWTIn, AttachNone, 0}, {"lwt_out", LWTOut, AttachNone, 0}, {"lwt_xmit", LWTXmit, AttachNone, 0}, {"lwt_seg6local", LWTSeg6Local, AttachNone, 0}, {"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, 0}, {"cgroup_skb/egress", CGroupSKB, AttachCGroupInetEgress, 0}, {"cgroup/skb", CGroupSKB, AttachNone, 0}, {"cgroup/sock_create", CGroupSock, AttachCGroupInetSockCreate, 0}, {"cgroup/sock_release", CGroupSock, AttachCgroupInetSockRelease, 0}, {"cgroup/sock", CGroupSock, AttachCGroupInetSockCreate, 0}, {"cgroup/post_bind4", CGroupSock, AttachCGroupInet4PostBind, 0}, {"cgroup/post_bind6", CGroupSock, AttachCGroupInet6PostBind, 0}, {"cgroup/dev", CGroupDevice, AttachCGroupDevice, 0}, {"sockops", SockOps, AttachCGroupSockOps, 0}, {"sk_skb/stream_parser", SkSKB, AttachSkSKBStreamParser, 0}, {"sk_skb/stream_verdict", SkSKB, AttachSkSKBStreamVerdict, 0}, {"sk_skb", SkSKB, AttachNone, 0}, {"sk_msg", SkMsg, AttachSkMsgVerdict, 0}, {"lirc_mode2", LircMode2, AttachLircMode2, 0}, {"flow_dissector", FlowDissector, AttachFlowDissector, 0}, {"cgroup/bind4", CGroupSockAddr, AttachCGroupInet4Bind, 0}, {"cgroup/bind6", CGroupSockAddr, AttachCGroupInet6Bind, 0}, {"cgroup/connect4", CGroupSockAddr, AttachCGroupInet4Connect, 0}, {"cgroup/connect6", CGroupSockAddr, AttachCGroupInet6Connect, 0}, {"cgroup/sendmsg4", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0}, {"cgroup/sendmsg6", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0}, {"cgroup/recvmsg4", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0}, {"cgroup/recvmsg6", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0}, {"cgroup/getpeername4", CGroupSockAddr, AttachCgroupInet4GetPeername, 0}, {"cgroup/getpeername6", CGroupSockAddr, AttachCgroupInet6GetPeername, 0}, {"cgroup/getsockname4", CGroupSockAddr, AttachCgroupInet4GetSockname, 0}, {"cgroup/getsockname6", CGroupSockAddr, AttachCgroupInet6GetSockname, 0}, {"cgroup/sysctl", CGroupSysctl, AttachCGroupSysctl, 0}, {"cgroup/getsockopt", CGroupSockopt, AttachCGroupGetsockopt, 0}, {"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0}, {"struct_ops+", StructOps, AttachNone, 0}, {"sk_lookup/", SkLookup, AttachSkLookup, 0}, {"seccomp", SocketFilter, AttachNone, 0}, {"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0}, {"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0}, } for _, t := range types { if !strings.HasPrefix(sectionName, t.prefix) { continue } if !strings.HasSuffix(t.prefix, "/") { return t.progType, t.attachType, t.progFlags, "" } return t.progType, t.attachType, t.progFlags, sectionName[len(t.prefix):] } return UnspecifiedProgram, AttachNone, 0, "" } func (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) { rels := make(map[uint64]elf.Symbol) if sec.Entsize < 16 { return nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name) } r := bufio.NewReader(sec.Open()) for off := uint64(0); off < sec.Size; off += sec.Entsize { ent := io.LimitReader(r, int64(sec.Entsize)) var rel elf.Rel64 if binary.Read(ent, ec.ByteOrder, &rel) != nil { return nil, fmt.Errorf("can't parse relocation at offset %v", off) } symNo := int(elf.R_SYM64(rel.Info) - 1) if symNo >= len(symbols) { return nil, fmt.Errorf("offset %d: symbol %d doesn't exist", off, symNo) } symbol := symbols[symNo] rels[rel.Off] = symbol } return rels, nil } golang-github-cilium-ebpf-0.11.0/elf_reader_test.go000066400000000000000000000736441456504511400222130ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "flag" "fmt" "os" "path/filepath" "strings" "syscall" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" qt "github.com/frankban/quicktest" ) func TestLoadCollectionSpec(t *testing.T) { cpus, err := internal.PossibleCPUs() if err != nil { t.Fatal(err) } coll := &CollectionSpec{ Maps: map[string]*MapSpec{ "hash_map": { Name: "hash_map", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 1, Flags: unix.BPF_F_NO_PREALLOC, }, "hash_map2": { Name: "hash_map2", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 2, }, "array_of_hash_map": { Name: "array_of_hash_map", Type: ArrayOfMaps, KeySize: 4, MaxEntries: 2, }, "perf_event_array": { Name: "perf_event_array", Type: PerfEventArray, MaxEntries: uint32(cpus), }, // Maps prefixed by btf_ are ignored when testing ELFs // that don't have BTF info embedded. (clang<9) "btf_pin": { Name: "btf_pin", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 1, Pinning: PinByName, }, "btf_outer_map": { Name: "btf_outer_map", Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 1, InnerMap: &MapSpec{ Name: "btf_outer_map_inner", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, "btf_outer_map_anon": { Name: "btf_outer_map_anon", Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 1, InnerMap: &MapSpec{ Name: "btf_outer_map_anon_inner", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, }, Programs: map[string]*ProgramSpec{ "xdp_prog": { Name: "xdp_prog", Type: XDP, SectionName: "xdp", License: "MIT", }, "no_relocation": { Name: "no_relocation", Type: SocketFilter, SectionName: "socket", License: "MIT", }, "asm_relocation": { Name: "asm_relocation", Type: SocketFilter, SectionName: "socket/2", License: "MIT", }, "data_sections": { Name: "data_sections", Type: SocketFilter, SectionName: "socket/3", License: "MIT", }, "global_fn3": { Name: "global_fn3", Type: UnspecifiedProgram, SectionName: "other", License: "MIT", }, "static_fn": { Name: "static_fn", Type: UnspecifiedProgram, SectionName: "static", License: "MIT", }, "anon_const": { Name: "anon_const", Type: SocketFilter, SectionName: "socket/4", License: "MIT", }, }, } defaultOpts := cmp.Options{ // Dummy Comparer that works with empty readers to support test cases. cmp.Comparer(func(a, b bytes.Reader) bool { if a.Len() == 0 && b.Len() == 0 { return true } return false }), cmpopts.IgnoreTypes(new(btf.Spec)), cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder", "Types"), cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"), cmpopts.IgnoreFields(MapSpec{}, "Key", "Value"), cmpopts.IgnoreUnexported(ProgramSpec{}), cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool { if key == ".bss" || key == ".data" || strings.HasPrefix(key, ".rodata") { return true } return false }), } ignoreBTFOpts := append(defaultOpts, cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool { return strings.HasPrefix(key, "btf_") }), ) testutils.Files(t, testutils.Glob(t, "testdata/loader-*.elf"), func(t *testing.T, file string) { have, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } opts := defaultOpts if have.Types != nil { err := have.RewriteConstants(map[string]interface{}{ "arg": uint32(1), "arg2": uint32(2), }) if err != nil { t.Fatal("Can't rewrite constant:", err) } err = have.RewriteConstants(map[string]interface{}{ "totallyBogus": uint32(1), }) if err == nil { t.Error("Rewriting a bogus constant doesn't fail") } } else { opts = ignoreBTFOpts } if diff := cmp.Diff(coll, have, opts...); diff != "" { t.Errorf("MapSpec mismatch (-want +got):\n%s", diff) } if have.ByteOrder != internal.NativeEndian { return } have.Maps["array_of_hash_map"].InnerMap = have.Maps["hash_map"] coll, err := NewCollectionWithOptions(have, CollectionOptions{ Maps: MapOptions{ PinPath: testutils.TempBPFFS(t), }, Programs: ProgramOptions{ LogLevel: LogLevelBranch, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer coll.Close() ret, _, err := coll.Programs["xdp_prog"].Test(internal.EmptyBPFContext) if err != nil { t.Fatal("Can't run program:", err) } if ret != 7 { t.Error("Unexpected return value:", ret) } }) } func BenchmarkELFLoader(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, _ = LoadCollectionSpec("testdata/loader-el.elf") } } func TestDataSections(t *testing.T) { file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian) coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } t.Log(coll.Programs["data_sections"].Instructions) var obj struct { Program *Program `ebpf:"data_sections"` } err = coll.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Program.Close() ret, _, err := obj.Program.Test(internal.EmptyBPFContext) if err != nil { t.Fatal(err) } if ret != 0 { t.Error("BPF assertion failed on line", ret) } } func TestInlineASMConstant(t *testing.T) { file := fmt.Sprintf("testdata/loader-%s.elf", internal.ClangEndian) coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } spec := coll.Programs["asm_relocation"] if spec.Instructions[0].Reference() != "MY_CONST" { t.Fatal("First instruction is not a reference to MY_CONST") } // -1 is used by the loader to find unrewritten maps. spec.Instructions[0].Constant = -1 t.Log(spec.Instructions) var obj struct { Program *Program `ebpf:"asm_relocation"` } err = coll.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } obj.Program.Close() } func TestCollectionSpecDetach(t *testing.T) { coll := Collection{ Maps: map[string]*Map{ "foo": new(Map), }, Programs: map[string]*Program{ "bar": new(Program), }, } foo := coll.DetachMap("foo") if foo == nil { t.Error("Program not returned from DetachMap") } if _, ok := coll.Programs["foo"]; ok { t.Error("DetachMap doesn't remove map from Maps") } bar := coll.DetachProgram("bar") if bar == nil { t.Fatal("Program not returned from DetachProgram") } if _, ok := coll.Programs["bar"]; ok { t.Error("DetachProgram doesn't remove program from Programs") } } func TestLoadInvalidMap(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/invalid_map-*.elf"), func(t *testing.T, file string) { cs, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't load CollectionSpec", err) } ms, ok := cs.Maps["invalid_map"] if !ok { t.Fatal("invalid_map not found in CollectionSpec") } m, err := NewMap(ms) t.Log(err) if err == nil { m.Close() t.Fatal("Creating a Map from a MapSpec with non-zero Extra is expected to fail.") } }) } func TestLoadInvalidMapMissingSymbol(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/invalid_map_static-el.elf"), func(t *testing.T, file string) { _, err := LoadCollectionSpec(file) t.Log(err) if err == nil { t.Fatal("Loading a map with static qualifier should fail") } }) } func TestLoadInitializedBTFMap(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) { coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } t.Run("NewCollection", func(t *testing.T) { if coll.ByteOrder != internal.NativeEndian { t.Skipf("Skipping %s collection", coll.ByteOrder) } tmp, err := NewCollection(coll) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("NewCollection failed:", err) } tmp.Close() }) t.Run("prog_array", func(t *testing.T) { m, ok := coll.Maps["prog_array_init"] if !ok { t.Fatal("map prog_array_init not found in program") } if len(m.Contents) != 1 { t.Error("expecting exactly 1 item in MapSpec contents") } p := m.Contents[0] if cmp.Equal(p.Key, 1) { t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key) } if _, ok := p.Value.(string); !ok { t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value) } if p.Value != "tail_1" { t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value) } }) t.Run("array_of_maps", func(t *testing.T) { m, ok := coll.Maps["outer_map_init"] if !ok { t.Fatal("map outer_map_init not found in program") } if len(m.Contents) != 1 { t.Error("expecting exactly 1 item in MapSpec contents") } if m.Key == nil { t.Error("Expected non-nil key") } if m.Value == nil { t.Error("Expected non-nil value") } if m.InnerMap.Key == nil { t.Error("Expected non-nil InnerMap key") } if m.InnerMap.Value == nil { t.Error("Expected non-nil InnerMap value") } p := m.Contents[0] if cmp.Equal(p.Key, 1) { t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key) } if _, ok := p.Value.(string); !ok { t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value) } if p.Value != "inner_map" { t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value) } }) }) } func TestLoadInvalidInitializedBTFMap(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/invalid_btf_map_init-*.elf"), func(t *testing.T, file string) { _, err := LoadCollectionSpec(file) t.Log(err) if !errors.Is(err, internal.ErrNotSupported) { t.Fatal("Loading an initialized BTF map should be unsupported") } }) } func TestStringSection(t *testing.T) { file := fmt.Sprintf("testdata/strings-%s.elf", internal.ClangEndian) spec, err := LoadCollectionSpec(file) if err != nil { t.Fatalf("load collection spec: %s", err) } for name := range spec.Maps { t.Log(name) } strMap := spec.Maps[".rodata.str1.1"] if strMap == nil { t.Fatal("Unable to find map '.rodata.str1.1' in loaded collection") } if !strMap.Freeze { t.Fatal("Read only data maps should be frozen") } if strMap.Flags != unix.BPF_F_RDONLY_PROG { t.Fatal("Read only data maps should have the prog-read-only flag set") } coll, err := NewCollection(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("new collection: %s", err) } defer coll.Close() prog := coll.Programs["filter"] if prog == nil { t.Fatal("program not found") } testMap := coll.Maps["my_map"] if testMap == nil { t.Fatal("test map not found") } _, err = prog.Run(&RunOptions{ Data: internal.EmptyBPFContext, // Min size for XDP programs }) if err != nil { t.Fatalf("prog run: %s", err) } key := []byte("This string is allocated in the string section\n\x00") var value uint32 if err = testMap.Lookup(&key, &value); err != nil { t.Fatalf("test map lookup: %s", err) } if value != 1 { t.Fatal("Test map value not 1!") } } func TestLoadRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API") testutils.Files(t, testutils.Glob(t, "testdata/raw_tracepoint-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } if spec.ByteOrder != internal.NativeEndian { return } coll, err := NewCollectionWithOptions(spec, CollectionOptions{ Programs: ProgramOptions{ LogLevel: LogLevelBranch, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create collection:", err) } coll.Close() }) } func TestTailCall(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { TailMain *Program `ebpf:"tail_main"` ProgArray *Map `ebpf:"prog_array_init"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.TailMain.Close() defer obj.ProgArray.Close() ret, _, err := obj.TailMain.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } // Expect the tail_1 tail call to be taken, returning value 42. if ret != 42 { t.Fatalf("Expected tail call to return value 42, got %d", ret) } }) } func TestKconfigKernelVersion(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/kconfig-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"kernel_version"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() ret, _, err := obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } v, err := internal.KernelVersion() if err != nil { t.Fatalf("getting kernel version: %s", err) } version := v.Kernel() if ret != version { t.Fatalf("Expected eBPF to return value %d, got %d", version, ret) } }) } func TestKconfigSyscallWrapper(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/kconfig-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"syscall_wrapper"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() ret, _, err := obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } var expected uint32 if testutils.IsKernelLessThan(t, "4.17") { expected = 0 } else { expected = 1 } if ret != expected { t.Fatalf("Expected eBPF to return value %d, got %d", expected, ret) } }) } func TestKconfigConfig(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/kconfig_config-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"kconfig"` ArrayMap *Map `ebpf:"array_map"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() defer obj.ArrayMap.Close() _, _, err = obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } var value uint64 err = obj.ArrayMap.Lookup(uint32(0), &value) if err != nil { t.Fatal(err) } // CONFIG_HZ must have a value. qt.Assert(t, value, qt.Not(qt.Equals), 0) }) } func TestKfunc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "bpf_kfunc_call_test_mem_len_pass1") testutils.Files(t, testutils.Glob(t, "testdata/kfunc-e*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"call_kfunc"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%v+", err) } defer obj.Main.Close() ret, _, err := obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 1 { t.Fatalf("Expected kfunc to return value 1, got %d", ret) } }) } func TestInvalidKfunc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "bpf_kfunc_call_test_mem_len_pass1") file := fmt.Sprintf("testdata/invalid-kfunc-%s.elf", internal.ClangEndian) coll, err := LoadCollection(file) if err == nil { coll.Close() t.Fatal("Expected an error") } var ike *incompatibleKfuncError if !errors.As(err, &ike) { t.Fatalf("Expected an error wrapping incompatibleKfuncError, got %T", err) } } func TestKfuncKmod(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "Kernel module function calls") if !haveTestmod(t) { t.Skip("bpf_testmod not loaded") } testutils.Files(t, testutils.Glob(t, "testdata/kfunc-kmod-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"call_kfunc"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%v+", err) } defer obj.Main.Close() ret, _, err := obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 1 { t.Fatalf("Expected kfunc to return value 1, got %d", ret) } }) } func TestSubprogRelocation(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem") testutils.Files(t, testutils.Glob(t, "testdata/subprog_reloc-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } var obj struct { Main *Program `ebpf:"fp_relocation"` HashMap *Map `ebpf:"hash_map"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() defer obj.HashMap.Close() ret, _, err := obj.Main.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 42 { t.Fatalf("Expected subprog reloc to return value 42, got %d", ret) } }) } func TestUnassignedProgArray(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if spec.ByteOrder != internal.NativeEndian { return } // tail_main references a ProgArray that is not being assigned // to this struct. Normally, this would clear all its entries // and make any tail calls into the ProgArray result in a miss. // The library needs to explicitly refuse such operations. var obj struct { TailMain *Program `ebpf:"tail_main"` // ProgArray *Map `ebpf:"prog_array_init"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err == nil { obj.TailMain.Close() t.Fatal("Expecting LoadAndAssign to return error") } }) } func TestIPRoute2Compat(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/iproute2_map_compat-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } if spec.ByteOrder != internal.NativeEndian { return } ms, ok := spec.Maps["hash_map"] if !ok { t.Fatal("Map hash_map not found") } var id, pinning, innerID, innerIndex uint32 if ms.Extra == nil { t.Fatal("missing extra bytes") } switch { case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil: t.Fatal("missing id") case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil: t.Fatal("missing pinning") case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil: t.Fatal("missing inner_id") case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil: t.Fatal("missing inner_idx") } if id != 0 || innerID != 0 || innerIndex != 0 { t.Fatal("expecting id, inner_id and inner_idx to be zero") } if pinning != 2 { t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)") } // iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS, // which needs to be be configured in this library using MapOptions.PinPath. // For the sake of the test, we use a tempdir on bpffs below. ms.Pinning = PinByName coll, err := NewCollectionWithOptions(spec, CollectionOptions{ Maps: MapOptions{ PinPath: testutils.TempBPFFS(t), }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create collection:", err) } coll.Close() }) } var ( elfPath = flag.String("elfs", os.Getenv("KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $KERNEL_SELFTESTS)") elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested") ) func TestLibBPFCompat(t *testing.T) { if *elfPath == "" { // Specify the path to the directory containing the eBPF for // the kernel's selftests if you want to run this test. // As of 5.2 that is tools/testing/selftests/bpf/ t.Skip("No path specified") } load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) { // Disable retrying a program load with the log enabled, it leads // to OOM kills. opts.Programs.LogDisabled = true for name, p := range spec.Programs { if p.Type != Extension { continue } targetProg, targetColl := loadTargetProgram(t, name, opts) defer targetColl.Close() p.AttachTarget = targetProg } coll, err := NewCollectionWithOptions(spec, opts) testutils.SkipIfNotSupported(t, err) var errno syscall.Errno if errors.As(err, &errno) { // This error is most likely from a syscall and caused by us not // replicating some fixups done in the selftests or the test // intentionally failing. This is expected, so skip the test // instead of failing. t.Skip("Skipping since the kernel rejected the program:", err) } if err == nil { coll.Close() } if !valid { if err == nil { t.Fatal("Expected an error during load") } } else if err != nil { t.Fatal("Error during loading:", err) } } files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern), // These files are only used as a source of btf. "btf__core_reloc_*", ) testutils.Files(t, files, func(t *testing.T, path string) { file := filepath.Base(path) switch file { case "test_map_in_map.o", "test_map_in_map.linked3.o", "test_select_reuseport_kern.o", "test_select_reuseport_kern.linked3.o": t.Skip("Skipping due to missing InnerMap in map definition") case "test_core_autosize.o": t.Skip("Skipping since the test generates dynamic BTF") case "test_static_linked.linked3.o": t.Skip("Skipping since .text contains 'subprog' twice") case "linked_maps.linked3.o", "linked_funcs.linked3.o", "linked_vars.linked3.o", "kprobe_multi.o", "kprobe_multi.linked3.o", "test_ksyms_weak.o", "test_ksyms_weak.llinked3.o", "test_ksyms_weak.llinked2.o", "test_ksyms_weak.llinked1.o", "test_ksyms_weak.linked3.o", "test_ksyms_module.o", "test_ksyms_module.llinked3.o", "test_ksyms_module.llinked2.o", "test_ksyms_module.llinked1.o", "test_ksyms_module.linked3.o", "test_ksyms.o", "test_ksyms.linked3.o": t.Skip("Skipping since weak relocations are not supported") case "bloom_filter_map.o", "bloom_filter_map.linked3.o", "bloom_filter_bench.o", "bloom_filter_bench.linked3.o": t.Skip("Skipping due to missing MapExtra field in MapSpec") case "netif_receive_skb.linked3.o": t.Skip("Skipping due to possible bug in upstream CO-RE generation") case "test_usdt.o", "test_usdt.linked3.o", "test_urandom_usdt.o", "test_urandom_usdt.linked3.o", "test_usdt_multispec.o": t.Skip("Skipping due to missing support for usdt.bpf.h") case "bpf_cubic.o", "bpf_cubic.linked3.o", "bpf_iter_tcp6.o", "bpf_iter_tcp6.linked3.o", "bpf_iter_tcp4.o", "bpf_iter_tcp4.linked3.o", "bpf_iter_ipv6_route.o", "bpf_iter_ipv6_route.linked3.o", "test_subskeleton_lib.o", "test_skeleton.o", "test_skeleton.linked3.o", "test_subskeleton_lib.linked3.o", "test_subskeleton.o", "test_subskeleton.linked3.o", "test_core_extern.linked3.o", "test_core_extern.o", "profiler1.linked3.o", "profiler3.o", "profiler3.linked3.o", "profiler2.o", "profiler2.linked3.o", "profiler1.o": t.Skip("Skipping due to using CONFIG_* variable which are not supported at the moment") } t.Parallel() spec, err := LoadCollectionSpec(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("Can't read %s: %s", file, err) } switch file { case "test_sk_assign.o": // Test contains a legacy iproute2 bpf_elf_map definition. for _, m := range spec.Maps { if m.Extra == nil || m.Extra.Len() == 0 { t.Fatalf("Expected extra bytes in map %s", m.Name) } m.Extra = nil } } var opts CollectionOptions for _, mapSpec := range spec.Maps { if mapSpec.Pinning != PinNone { opts.Maps.PinPath = testutils.TempBPFFS(t) break } } coreFiles := sourceOfBTF(t, path) if len(coreFiles) == 0 { // NB: test_core_reloc_kernel.o doesn't have dedicated BTF and // therefore goes via this code path. load(t, spec, opts, true) return } for _, coreFile := range coreFiles { name := filepath.Base(coreFile) t.Run(name, func(t *testing.T) { // Some files like btf__core_reloc_arrays___err_too_small.o // trigger an error on purpose. Use the name to infer whether // the test should succeed. var valid bool switch name { case "btf__core_reloc_existence___err_wrong_arr_kind.o", "btf__core_reloc_existence___err_wrong_arr_value_type.o", "btf__core_reloc_existence___err_wrong_int_kind.o", "btf__core_reloc_existence___err_wrong_int_sz.o", "btf__core_reloc_existence___err_wrong_int_type.o", "btf__core_reloc_existence___err_wrong_struct_type.o": // These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/ valid = true case "btf__core_reloc_ints___err_wrong_sz_16.o", "btf__core_reloc_ints___err_wrong_sz_32.o", "btf__core_reloc_ints___err_wrong_sz_64.o", "btf__core_reloc_ints___err_wrong_sz_8.o", "btf__core_reloc_arrays___err_wrong_val_type1.o", "btf__core_reloc_arrays___err_wrong_val_type2.o": // These tests are valid according to current libbpf behaviour, // see commit 42765ede5c54ca915de5bfeab83be97207e46f68. valid = true case "btf__core_reloc_type_id___missing_targets.o", "btf__core_reloc_flavors__err_wrong_name.o": valid = false case "btf__core_reloc_ints___err_bitfield.o": // Bitfields are now valid. valid = true default: valid = !strings.Contains(name, "___err_") } fh, err := os.Open(coreFile) if err != nil { t.Fatal(err) } defer fh.Close() btfSpec, err := btf.LoadSpec(coreFile) if err != nil { t.Fatal(err) } opts := opts // copy opts.Programs.KernelTypes = btfSpec load(t, spec, opts, valid) }) } }) } func loadTargetProgram(tb testing.TB, name string, opts CollectionOptions) (*Program, *Collection) { file := "test_pkt_access.o" program := "test_pkt_access" switch name { case "new_connect_v4_prog": file = "connect4_prog.o" program = "connect_v4_prog" case "new_do_bind": file = "connect4_prog.o" program = "connect_v4_prog" case "freplace_cls_redirect_test": file = "test_cls_redirect.o" program = "cls_redirect" case "new_handle_kprobe": file = "test_attach_probe.o" program = "handle_kprobe" case "test_pkt_md_access_new": file = "test_pkt_md_access.o" program = "test_pkt_md_access" default: } spec, err := LoadCollectionSpec(filepath.Join(*elfPath, file)) if err != nil { tb.Fatalf("Can't read %s: %s", file, err) } coll, err := NewCollectionWithOptions(spec, opts) if err != nil { tb.Fatalf("Can't load target: %s", err) } return coll.Programs[program], coll } func sourceOfBTF(tb testing.TB, path string) []string { const testPrefix = "test_core_reloc_" const btfPrefix = "btf__core_reloc_" dir, base := filepath.Split(path) if !strings.HasPrefix(base, testPrefix) { return nil } base = strings.TrimSuffix(base[len(testPrefix):], ".o") switch base { case "bitfields_direct", "bitfields_probed": base = "bitfields" } return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o")) } func TestGetProgType(t *testing.T) { type progTypeTestData struct { Pt ProgramType At AttachType Fl uint32 To string } testcases := map[string]progTypeTestData{ "socket/garbage": { Pt: SocketFilter, At: AttachNone, To: "", }, "kprobe/func": { Pt: Kprobe, At: AttachNone, To: "func", }, "xdp/foo": { Pt: XDP, At: AttachNone, To: "", }, "xdp.frags/foo": { Pt: XDP, At: AttachNone, To: "", Fl: unix.BPF_F_XDP_HAS_FRAGS, }, "xdp_devmap/foo": { Pt: XDP, At: AttachXDPDevMap, To: "foo", }, "xdp.frags_devmap/foo": { Pt: XDP, At: AttachXDPDevMap, To: "foo", Fl: unix.BPF_F_XDP_HAS_FRAGS, }, "cgroup_skb/ingress": { Pt: CGroupSKB, At: AttachCGroupInetIngress, To: "", }, "iter/bpf_map": { Pt: Tracing, At: AttachTraceIter, To: "bpf_map", }, "lsm.s/file_ioctl_sleepable": { Pt: LSM, At: AttachLSMMac, To: "file_ioctl_sleepable", Fl: unix.BPF_F_SLEEPABLE, }, "lsm/file_ioctl": { Pt: LSM, At: AttachLSMMac, To: "file_ioctl", }, "sk_skb/stream_verdict/foo": { Pt: SkSKB, At: AttachSkSKBStreamVerdict, To: "", }, "sk_skb/bar": { Pt: SkSKB, At: AttachNone, To: "", }, } for section, want := range testcases { pt, at, fl, to := getProgType(section) if diff := cmp.Diff(want, progTypeTestData{Pt: pt, At: at, Fl: fl, To: to}); diff != "" { t.Errorf("getProgType mismatch (-want +got):\n%s", diff) } } } golang-github-cilium-ebpf-0.11.0/example_sock_elf_test.go000066400000000000000000000177351456504511400234220ustar00rootroot00000000000000//go:build linux package ebpf_test import ( "bytes" "encoding/binary" "flag" "fmt" "syscall" "time" "unsafe" "github.com/cilium/ebpf" ) var program = [...]byte{ 0177, 0105, 0114, 0106, 0002, 0001, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0367, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0010, 0000, 0001, 0000, 0277, 0026, 0000, 0000, 0000, 0000, 0000, 0000, 0060, 0000, 0000, 0000, 0027, 0000, 0000, 0000, 0143, 0012, 0374, 0377, 0000, 0000, 0000, 0000, 0141, 0141, 0004, 0000, 0000, 0000, 0000, 0000, 0125, 0001, 0010, 0000, 0004, 0000, 0000, 0000, 0277, 0242, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0002, 0000, 0000, 0374, 0377, 0377, 0377, 0030, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0205, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0025, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0141, 0141, 0000, 0000, 0000, 0000, 0000, 0000, 0333, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0267, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0225, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0107, 0120, 0114, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0065, 0000, 0000, 0000, 0000, 0000, 0003, 0000, 0150, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0034, 0000, 0000, 0000, 0020, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0110, 0000, 0000, 0000, 0020, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0014, 0000, 0000, 0000, 0020, 0000, 0005, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0023, 0000, 0000, 0000, 0020, 0000, 0005, 0000, 0024, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0070, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0056, 0164, 0145, 0170, 0164, 0000, 0155, 0141, 0160, 0163, 0000, 0155, 0171, 0137, 0155, 0141, 0160, 0000, 0164, 0145, 0163, 0164, 0137, 0155, 0141, 0160, 0000, 0137, 0154, 0151, 0143, 0145, 0156, 0163, 0145, 0000, 0056, 0163, 0164, 0162, 0164, 0141, 0142, 0000, 0056, 0163, 0171, 0155, 0164, 0141, 0142, 0000, 0114, 0102, 0102, 0060, 0137, 0063, 0000, 0056, 0162, 0145, 0154, 0163, 0157, 0143, 0153, 0145, 0164, 0061, 0000, 0142, 0160, 0146, 0137, 0160, 0162, 0157, 0147, 0061, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0045, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0210, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0122, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0170, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0074, 0000, 0000, 0000, 0011, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0170, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0270, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0050, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0035, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0055, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0350, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0220, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0030, 0000, 0000, 0000, 0000, 0000, 0000, 0000, } // ExampleSocketELF demonstrates how to load an eBPF program from an ELF, // and attach it to a raw socket. func Example_socketELF() { const SO_ATTACH_BPF = 50 index := flag.Int("index", 0, "specify ethernet index") flag.Parse() spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(program[:])) if err != nil { panic(err) } var objs struct { Prog *ebpf.Program `ebpf:"bpf_prog1"` Stats *ebpf.Map `ebpf:"my_map"` } if err := spec.LoadAndAssign(&objs, nil); err != nil { panic(err) } defer objs.Prog.Close() defer objs.Stats.Close() sock, err := openRawSock(*index) if err != nil { panic(err) } defer syscall.Close(sock) if err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, SO_ATTACH_BPF, objs.Prog.FD()); err != nil { panic(err) } fmt.Printf("Filtering on eth index: %d\n", *index) fmt.Println("Packet stats:") for { const ( ICMP = 0x01 TCP = 0x06 UDP = 0x11 ) time.Sleep(time.Second) var icmp uint64 var tcp uint64 var udp uint64 err := objs.Stats.Lookup(uint32(ICMP), &icmp) if err != nil { panic(err) } err = objs.Stats.Lookup(uint32(TCP), &tcp) if err != nil { panic(err) } err = objs.Stats.Lookup(uint32(UDP), &udp) if err != nil { panic(err) } fmt.Printf("\r\033[m\tICMP: %d TCP: %d UDP: %d", icmp, tcp, udp) } } func openRawSock(index int) (int, error) { sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, int(htons(syscall.ETH_P_ALL))) if err != nil { return 0, err } sll := syscall.SockaddrLinklayer{ Ifindex: index, Protocol: htons(syscall.ETH_P_ALL), } if err := syscall.Bind(sock, &sll); err != nil { return 0, err } return sock, nil } // htons converts the unsigned short integer hostshort from host byte order to network byte order. func htons(i uint16) uint16 { b := make([]byte, 2) binary.BigEndian.PutUint16(b, i) return *(*uint16)(unsafe.Pointer(&b[0])) } golang-github-cilium-ebpf-0.11.0/example_sock_extract_dist_test.go000066400000000000000000000120521456504511400253340ustar00rootroot00000000000000package ebpf_test // This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf // // Copyright (c) 2015-2017 Cloudflare, Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of the Cloudflare, Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import ( "fmt" "net" "syscall" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" ) // ExampleExtractDistance shows how to attach an eBPF socket filter to // extract the network distance of an IP host. func Example_extractDistance() { filter, TTLs, err := newDistanceFilter() if err != nil { panic(err) } defer filter.Close() defer TTLs.Close() // Attach filter before the call to connect() dialer := net.Dialer{ Control: func(network, address string, c syscall.RawConn) (err error) { const SO_ATTACH_BPF = 50 err = c.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_ATTACH_BPF, filter.FD()) }) return err }, } conn, err := dialer.Dial("tcp", "1.1.1.1:53") if err != nil { panic(err) } conn.Close() minDist, err := minDistance(TTLs) if err != nil { panic(err) } fmt.Println("1.1.1.1:53 is", minDist, "hops away") } func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) { const ETH_P_IPV6 uint16 = 0x86DD ttls, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Hash, KeySize: 4, ValueSize: 8, MaxEntries: 4, }) if err != nil { return nil, nil, err } insns := asm.Instructions{ // r1 has ctx // r0 = ctx[16] (aka protocol) asm.LoadMem(asm.R0, asm.R1, 16, asm.Word), // Perhaps ipv6 asm.LoadImm(asm.R2, int64(ETH_P_IPV6), asm.DWord), asm.HostTo(asm.BE, asm.R2, asm.Half), asm.JEq.Reg(asm.R0, asm.R2, "ipv6"), // otherwise assume ipv4 // 8th byte in IPv4 is TTL // LDABS requires ctx in R6 asm.Mov.Reg(asm.R6, asm.R1), asm.LoadAbs(-0x100000+8, asm.Byte), asm.Ja.Label("store-ttl"), // 7th byte in IPv6 is Hop count // LDABS requires ctx in R6 asm.Mov.Reg(asm.R6, asm.R1).WithSymbol("ipv6"), asm.LoadAbs(-0x100000+7, asm.Byte), // stash the load result into FP[-4] asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).WithSymbol("store-ttl"), // stash the &FP[-4] into r2 asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), // r1 must point to map asm.LoadMapPtr(asm.R1, ttls.FD()), asm.FnMapLookupElem.Call(), // load ok? inc. Otherwise? jmp to mapupdate asm.JEq.Imm(asm.R0, 0, "update-map"), asm.Mov.Imm(asm.R1, 1), asm.StoreXAdd(asm.R0, asm.R1, asm.DWord), asm.Ja.Label("exit"), // MapUpdate // r1 has map ptr asm.LoadMapPtr(asm.R1, ttls.FD()).WithSymbol("update-map"), // r2 has key -> &FP[-4] asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), // r3 has value -> &FP[-16] , aka 1 asm.StoreImm(asm.RFP, -16, 1, asm.DWord), asm.Mov.Reg(asm.R3, asm.RFP), asm.Add.Imm(asm.R3, -16), // r4 has flags, 0 asm.Mov.Imm(asm.R4, 0), asm.FnMapUpdateElem.Call(), // set exit code to -1, don't trunc packet asm.Mov.Imm(asm.R0, -1).WithSymbol("exit"), asm.Return(), } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "distance_filter", Type: ebpf.SocketFilter, License: "GPL", Instructions: insns, }) if err != nil { ttls.Close() return nil, nil, err } return prog, ttls, nil } func minDistance(TTLs *ebpf.Map) (int, error) { var ( entries = TTLs.Iterate() ttl uint32 minDist uint32 = 255 count uint64 ) for entries.Next(&ttl, &count) { var dist uint32 switch { case ttl > 128: dist = 255 - ttl case ttl > 64: dist = 128 - ttl case ttl > 32: dist = 64 - ttl default: dist = 32 - ttl } if minDist > dist { minDist = dist } } return int(minDist), entries.Err() } golang-github-cilium-ebpf-0.11.0/features/000077500000000000000000000000001456504511400203355ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/features/doc.go000066400000000000000000000015671456504511400214420ustar00rootroot00000000000000// Package features allows probing for BPF features available to the calling process. // // In general, the error return values from feature probes in this package // all have the following semantics unless otherwise specified: // // err == nil: The feature is available. // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. // err != nil: Any errors encountered during probe execution, wrapped. // // Note that the latter case may include false negatives, and that resource // creation may succeed despite an error being returned. For example, some // map and program types cannot reliably be probed and will return an // inconclusive error. // // As a rule, only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached by the library and persist throughout any changes // to the process' environment, like capability changes. package features golang-github-cilium-ebpf-0.11.0/features/map.go000066400000000000000000000201261456504511400214420ustar00rootroot00000000000000package features import ( "errors" "fmt" "os" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // HaveMapType probes the running kernel for the availability of the specified map type. // // See the package documentation for the meaning of the error return value. func HaveMapType(mt ebpf.MapType) error { return haveMapTypeMatrix.Result(mt) } func probeCgroupStorageMap(mt sys.MapType) error { // keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16) // by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs return createMap(&sys.MapCreateAttr{ MapType: mt, ValueSize: 4, KeySize: uint32(8 + unsafe.Sizeof(int(0))), MaxEntries: 0, }) } func probeStorageMap(mt sys.MapType) error { // maxEntries needs to be 0 // BPF_F_NO_PREALLOC needs to be set // btf* fields need to be set // see alloc_check for local_storage map types err := createMap(&sys.MapCreateAttr{ MapType: mt, KeySize: 4, ValueSize: 4, MaxEntries: 0, MapFlags: unix.BPF_F_NO_PREALLOC, BtfKeyTypeId: 1, BtfValueTypeId: 1, BtfFd: ^uint32(0), }) if errors.Is(err, unix.EBADF) { // Triggered by BtfFd. return nil } return err } func probeNestedMap(mt sys.MapType) error { // assign invalid innerMapFd to pass validation check // will return EBADF err := probeMap(&sys.MapCreateAttr{ MapType: mt, InnerMapFd: ^uint32(0), }) if errors.Is(err, unix.EBADF) { return nil } return err } func probeMap(attr *sys.MapCreateAttr) error { if attr.KeySize == 0 { attr.KeySize = 4 } if attr.ValueSize == 0 { attr.ValueSize = 4 } attr.MaxEntries = 1 return createMap(attr) } func createMap(attr *sys.MapCreateAttr) error { fd, err := sys.MapCreate(attr) if err == nil { fd.Close() return nil } switch { // EINVAL occurs when attempting to create a map with an unknown type. // E2BIG occurs when MapCreateAttr contains non-zero bytes past the end // of the struct known by the running kernel, meaning the kernel is too old // to support the given map type. case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): return ebpf.ErrNotSupported } return err } var haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{ ebpf.Hash: {Version: "3.19"}, ebpf.Array: {Version: "3.19"}, ebpf.ProgramArray: {Version: "4.2"}, ebpf.PerfEventArray: {Version: "4.3"}, ebpf.PerCPUHash: {Version: "4.6"}, ebpf.PerCPUArray: {Version: "4.6"}, ebpf.StackTrace: { Version: "4.6", Fn: func() error { return probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STACK_TRACE, ValueSize: 8, // sizeof(uint64) }) }, }, ebpf.CGroupArray: {Version: "4.8"}, ebpf.LRUHash: {Version: "4.10"}, ebpf.LRUCPUHash: {Version: "4.10"}, ebpf.LPMTrie: { Version: "4.11", Fn: func() error { // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8 // BPF_F_NO_PREALLOC needs to be set return probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_LPM_TRIE, KeySize: 8, ValueSize: 8, MapFlags: unix.BPF_F_NO_PREALLOC, }) }, }, ebpf.ArrayOfMaps: { Version: "4.12", Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) }, }, ebpf.HashOfMaps: { Version: "4.12", Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) }, }, ebpf.DevMap: {Version: "4.14"}, ebpf.SockMap: {Version: "4.14"}, ebpf.CPUMap: {Version: "4.15"}, ebpf.XSKMap: {Version: "4.18"}, ebpf.SockHash: {Version: "4.18"}, ebpf.CGroupStorage: { Version: "4.19", Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) }, }, ebpf.ReusePortSockArray: {Version: "4.19"}, ebpf.PerCPUCGroupStorage: { Version: "4.20", Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) }, }, ebpf.Queue: { Version: "4.20", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_QUEUE, KeySize: 0, ValueSize: 4, MaxEntries: 1, }) }, }, ebpf.Stack: { Version: "4.20", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STACK, KeySize: 0, ValueSize: 4, MaxEntries: 1, }) }, }, ebpf.SkStorage: { Version: "5.2", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) }, }, ebpf.DevMapHash: {Version: "5.4"}, ebpf.StructOpsMap: { Version: "5.6", Fn: func() error { // StructOps requires setting a vmlinux type id, but id 1 will always // resolve to some type of integer. This will cause ENOTSUPP. err := probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STRUCT_OPS, BtfVmlinuxValueTypeId: 1, }) if errors.Is(err, sys.ENOTSUPP) { // ENOTSUPP means the map type is at least known to the kernel. return nil } return err }, }, ebpf.RingBuf: { Version: "5.8", Fn: func() error { // keySize and valueSize need to be 0 // maxEntries needs to be power of 2 and PAGE_ALIGNED return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_RINGBUF, KeySize: 0, ValueSize: 0, MaxEntries: uint32(os.Getpagesize()), }) }, }, ebpf.InodeStorage: { Version: "5.10", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) }, }, ebpf.TaskStorage: { Version: "5.11", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) }, }, } func init() { for mt, ft := range haveMapTypeMatrix { ft.Name = mt.String() if ft.Fn == nil { // Avoid referring to the loop variable in the closure. mt := sys.MapType(mt) ft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) } } } } // MapFlags document which flags may be feature probed. type MapFlags = sys.MapFlags // Flags which may be feature probed. const ( BPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC BPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG BPF_F_MMAPABLE = sys.BPF_F_MMAPABLE BPF_F_INNER_MAP = sys.BPF_F_INNER_MAP ) // HaveMapFlag probes the running kernel for the availability of the specified map flag. // // Returns an error if flag is not one of the flags declared in this package. // See the package documentation for the meaning of the error return value. func HaveMapFlag(flag MapFlags) (err error) { return haveMapFlagsMatrix.Result(flag) } func probeMapFlag(attr *sys.MapCreateAttr) error { // For now, we do not check if the map type is supported because we only support // probing for flags defined on arrays and hashs that are always supported. // In the future, if we allow probing on flags defined on newer types, checking for map type // support will be required. if attr.MapType == sys.BPF_MAP_TYPE_UNSPEC { attr.MapType = sys.BPF_MAP_TYPE_ARRAY } attr.KeySize = 4 attr.ValueSize = 4 attr.MaxEntries = 1 fd, err := sys.MapCreate(attr) if err == nil { fd.Close() } else if errors.Is(err, unix.EINVAL) { // EINVAL occurs when attempting to create a map with an unknown type or an unknown flag. err = ebpf.ErrNotSupported } return err } var haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{ BPF_F_NO_PREALLOC: { Version: "4.6", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_HASH, MapFlags: BPF_F_NO_PREALLOC, }) }, }, BPF_F_RDONLY_PROG: { Version: "5.2", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_RDONLY_PROG, }) }, }, BPF_F_WRONLY_PROG: { Version: "5.2", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_WRONLY_PROG, }) }, }, BPF_F_MMAPABLE: { Version: "5.5", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_MMAPABLE, }) }, }, BPF_F_INNER_MAP: { Version: "5.10", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_INNER_MAP, }) }, }, } func init() { for mf, ft := range haveMapFlagsMatrix { ft.Name = fmt.Sprint(mf) } } golang-github-cilium-ebpf-0.11.0/features/map_test.go000066400000000000000000000011171456504511400225000ustar00rootroot00000000000000package features import ( "errors" "math" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveMapType(t *testing.T) { testutils.CheckFeatureMatrix(t, haveMapTypeMatrix) } func TestHaveMapFlag(t *testing.T) { testutils.CheckFeatureMatrix(t, haveMapFlagsMatrix) } func TestHaveMapTypeInvalid(t *testing.T) { if err := HaveMapType(ebpf.MapType(math.MaxUint32)); err == nil { t.Fatal("Expected an error") } else if errors.Is(err, internal.ErrNotSupported) { t.Fatal("Got ErrNotSupported:", err) } } golang-github-cilium-ebpf-0.11.0/features/misc.go000066400000000000000000000046131456504511400216230ustar00rootroot00000000000000package features import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" ) // HaveLargeInstructions probes the running kernel if more than 4096 instructions // per program are supported. // // Upstream commit c04c0d2b968a ("bpf: increase complexity limit and maximum program size"). // // See the package documentation for the meaning of the error return value. var HaveLargeInstructions = internal.NewFeatureTest(">4096 instructions", "5.2", func() error { const maxInsns = 4096 insns := make(asm.Instructions, maxInsns, maxInsns+1) for i := range insns { insns[i] = asm.Mov.Imm(asm.R0, 1) } insns = append(insns, asm.Return()) return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: insns, }) }) // HaveBoundedLoops probes the running kernel if bounded loops are supported. // // Upstream commit 2589726d12a1 ("bpf: introduce bounded loops"). // // See the package documentation for the meaning of the error return value. var HaveBoundedLoops = internal.NewFeatureTest("bounded loops", "5.3", func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 10), asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"), asm.JNE.Imm(asm.R0, 0, "loop"), asm.Return(), }, }) }) // HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported. // // Upstream commit 92b31a9af73b ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions"). // // See the package documentation for the meaning of the error return value. var HaveV2ISA = internal.NewFeatureTest("v2 ISA", "4.14", func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), asm.Return().WithSymbol("exit"), }, }) }) // HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported. // // Upstream commit 092ed0968bb6 ("bpf: verifier support JMP32"). // // See the package documentation for the meaning of the error return value. var HaveV3ISA = internal.NewFeatureTest("v3 ISA", "5.1", func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm32(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), asm.Return().WithSymbol("exit"), }, }) }) golang-github-cilium-ebpf-0.11.0/features/misc_test.go000066400000000000000000000006741456504511400226650ustar00rootroot00000000000000package features import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveLargeInstructions(t *testing.T) { testutils.CheckFeatureTest(t, HaveLargeInstructions) } func TestHaveBoundedLoops(t *testing.T) { testutils.CheckFeatureTest(t, HaveBoundedLoops) } func TestHaveV2ISA(t *testing.T) { testutils.CheckFeatureTest(t, HaveV2ISA) } func TestHaveV3ISA(t *testing.T) { testutils.CheckFeatureTest(t, HaveV3ISA) } golang-github-cilium-ebpf-0.11.0/features/prog.go000066400000000000000000000173421456504511400216420ustar00rootroot00000000000000package features import ( "errors" "fmt" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // HaveProgType probes the running kernel for the availability of the specified program type. // // Deprecated: use HaveProgramType() instead. var HaveProgType = HaveProgramType // HaveProgramType probes the running kernel for the availability of the specified program type. // // See the package documentation for the meaning of the error return value. func HaveProgramType(pt ebpf.ProgramType) (err error) { return haveProgramTypeMatrix.Result(pt) } func probeProgram(spec *ebpf.ProgramSpec) error { if spec.Instructions == nil { spec.Instructions = asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } } prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ LogDisabled: true, }) if err == nil { prog.Close() } switch { // EINVAL occurs when attempting to create a program with an unknown type. // E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end // of the struct known by the running kernel, meaning the kernel is too old // to support the given prog type. case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): err = ebpf.ErrNotSupported } return err } var haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{ ebpf.SocketFilter: {Version: "3.19"}, ebpf.Kprobe: {Version: "4.1"}, ebpf.SchedCLS: {Version: "4.1"}, ebpf.SchedACT: {Version: "4.1"}, ebpf.TracePoint: {Version: "4.7"}, ebpf.XDP: {Version: "4.8"}, ebpf.PerfEvent: {Version: "4.9"}, ebpf.CGroupSKB: {Version: "4.10"}, ebpf.CGroupSock: {Version: "4.10"}, ebpf.LWTIn: {Version: "4.10"}, ebpf.LWTOut: {Version: "4.10"}, ebpf.LWTXmit: {Version: "4.10"}, ebpf.SockOps: {Version: "4.13"}, ebpf.SkSKB: {Version: "4.14"}, ebpf.CGroupDevice: {Version: "4.15"}, ebpf.SkMsg: {Version: "4.17"}, ebpf.RawTracepoint: {Version: "4.17"}, ebpf.CGroupSockAddr: { Version: "4.17", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSockAddr, AttachType: ebpf.AttachCGroupInet4Connect, }) }, }, ebpf.LWTSeg6Local: {Version: "4.18"}, ebpf.LircMode2: {Version: "4.18"}, ebpf.SkReuseport: {Version: "4.19"}, ebpf.FlowDissector: {Version: "4.20"}, ebpf.CGroupSysctl: {Version: "5.2"}, ebpf.RawTracepointWritable: {Version: "5.2"}, ebpf.CGroupSockopt: { Version: "5.3", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSockopt, AttachType: ebpf.AttachCGroupGetsockopt, }) }, }, ebpf.Tracing: { Version: "5.5", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Tracing, AttachType: ebpf.AttachTraceFEntry, AttachTo: "bpf_init", }) }, }, ebpf.StructOps: { Version: "5.6", Fn: func() error { err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.StructOps, License: "GPL", }) if errors.Is(err, sys.ENOTSUPP) { // ENOTSUPP means the program type is at least known to the kernel. return nil } return err }, }, ebpf.Extension: { Version: "5.6", Fn: func() error { // create btf.Func to add to first ins of target and extension so both progs are btf powered btfFn := btf.Func{ Name: "a", Type: &btf.FuncProto{ Return: &btf.Int{}, }, Linkage: btf.GlobalFunc, } insns := asm.Instructions{ btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn), asm.Return(), } // create target prog prog, err := ebpf.NewProgramWithOptions( &ebpf.ProgramSpec{ Type: ebpf.XDP, Instructions: insns, }, ebpf.ProgramOptions{ LogDisabled: true, }, ) if err != nil { return err } defer prog.Close() // probe for Extension prog with target return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Extension, Instructions: insns, AttachTarget: prog, AttachTo: btfFn.Name, }) }, }, ebpf.LSM: { Version: "5.7", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.LSM, AttachType: ebpf.AttachLSMMac, AttachTo: "file_mprotect", License: "GPL", }) }, }, ebpf.SkLookup: { Version: "5.9", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SkLookup, AttachType: ebpf.AttachSkLookup, }) }, }, ebpf.Syscall: { Version: "5.14", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Syscall, Flags: unix.BPF_F_SLEEPABLE, }) }, }, } func init() { for key, ft := range haveProgramTypeMatrix { ft.Name = key.String() if ft.Fn == nil { key := key // avoid the dreaded loop variable problem ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) } } } } type helperKey struct { typ ebpf.ProgramType helper asm.BuiltinFunc } var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest { return &internal.FeatureTest{ Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ), Fn: func() error { return haveProgramHelper(key.typ, key.helper) }, } }) // HaveProgramHelper probes the running kernel for the availability of the specified helper // function to a specified program type. // Return values have the following semantics: // // err == nil: The feature is available. // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. // err != nil: Any errors encountered during probe execution, wrapped. // // Note that the latter case may include false negatives, and that program creation may // succeed despite an error being returned. // Only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached and persist throughout any process capability changes. func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { if helper > helper.Max() { return os.ErrInvalid } return helperCache.Result(helperKey{pt, helper}) } func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { if ok := helperProbeNotImplemented(pt); ok { return fmt.Errorf("no feature probe for %v/%v", pt, helper) } if err := HaveProgramType(pt); err != nil { return err } spec := &ebpf.ProgramSpec{ Type: pt, Instructions: asm.Instructions{ helper.Call(), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", } switch pt { case ebpf.CGroupSockAddr: spec.AttachType = ebpf.AttachCGroupInet4Connect case ebpf.CGroupSockopt: spec.AttachType = ebpf.AttachCGroupGetsockopt case ebpf.SkLookup: spec.AttachType = ebpf.AttachSkLookup case ebpf.Syscall: spec.Flags = unix.BPF_F_SLEEPABLE } prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ LogDisabled: true, }) if err == nil { prog.Close() } switch { // EACCES occurs when attempting to create a program probe with a helper // while the register args when calling this helper aren't set up properly. // We interpret this as the helper being available, because the verifier // returns EINVAL if the helper is not supported by the running kernel. case errors.Is(err, unix.EACCES): // TODO: possibly we need to check verifier output here to be sure err = nil // EINVAL occurs when attempting to create a program with an unknown helper. case errors.Is(err, unix.EINVAL): // TODO: possibly we need to check verifier output here to be sure err = ebpf.ErrNotSupported } return err } func helperProbeNotImplemented(pt ebpf.ProgramType) bool { switch pt { case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing: return true } return false } golang-github-cilium-ebpf-0.11.0/features/prog_test.go000066400000000000000000000075431456504511400227030ustar00rootroot00000000000000package features import ( "errors" "fmt" "math" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/fdtrace" ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestHaveProgramType(t *testing.T) { testutils.CheckFeatureMatrix(t, haveProgramTypeMatrix) } func TestHaveProgramTypeInvalid(t *testing.T) { if err := HaveProgramType(ebpf.ProgramType(math.MaxUint32)); err == nil { t.Fatal("Expected an error") } else if errors.Is(err, internal.ErrNotSupported) { t.Fatal("Got ErrNotSupported:", err) } } func TestHaveProgramHelper(t *testing.T) { type testCase struct { prog ebpf.ProgramType helper asm.BuiltinFunc expected error version string } // Referencing linux kernel commits to track the kernel version required to pass these test cases. // They cases are derived from libbpf's selftests and helper/prog combinations that are // probed for in cilium/cilium. testCases := []testCase{ {ebpf.Kprobe, asm.FnMapLookupElem, nil, "3.19"}, // d0003ec01c66 {ebpf.SocketFilter, asm.FnKtimeGetCoarseNs, nil, "5.11"}, // d05512618056 {ebpf.SchedCLS, asm.FnSkbVlanPush, nil, "4.3"}, // 4e10df9a60d9 {ebpf.Kprobe, asm.FnSkbVlanPush, ebpf.ErrNotSupported, "4.3"}, // 4e10df9a60d9 {ebpf.Kprobe, asm.FnSysBpf, ebpf.ErrNotSupported, "5.14"}, // 79a7f8bdb159 {ebpf.Syscall, asm.FnSysBpf, nil, "5.14"}, // 79a7f8bdb159 {ebpf.XDP, asm.FnJiffies64, nil, "5.5"}, // 5576b991e9c1 {ebpf.XDP, asm.FnKtimeGetBootNs, nil, "5.7"}, // 71d19214776e {ebpf.SchedCLS, asm.FnSkbChangeHead, nil, "5.8"}, // 6f3f65d80dac {ebpf.SchedCLS, asm.FnRedirectNeigh, nil, "5.10"}, // b4ab31414970 {ebpf.SchedCLS, asm.FnSkbEcnSetCe, nil, "5.1"}, // f7c917ba11a6 {ebpf.SchedACT, asm.FnSkAssign, nil, "5.6"}, // cf7fbe660f2d {ebpf.SchedACT, asm.FnFibLookup, nil, "4.18"}, // 87f5fc7e48dd {ebpf.Kprobe, asm.FnFibLookup, ebpf.ErrNotSupported, "4.18"}, // 87f5fc7e48dd {ebpf.CGroupSockAddr, asm.FnGetsockopt, nil, "5.8"}, // beecf11bc218 {ebpf.CGroupSockAddr, asm.FnSkLookupTcp, nil, "4.20"}, // 6acc9b432e67 {ebpf.CGroupSockAddr, asm.FnGetNetnsCookie, nil, "5.7"}, // f318903c0bf4 {ebpf.CGroupSock, asm.FnGetNetnsCookie, nil, "5.7"}, // f318903c0bf4 {ebpf.Kprobe, asm.FnKtimeGetCoarseNs, ebpf.ErrNotSupported, "5.16"}, // 5e0bc3082e2e {ebpf.CGroupSockAddr, asm.FnGetCgroupClassid, nil, "5.7"}, // 5a52ae4e32a6 {ebpf.Kprobe, asm.FnGetBranchSnapshot, nil, "5.16"}, // 856c02dbce4f {ebpf.SchedCLS, asm.FnSkbSetTstamp, nil, "5.18"}, // 9bb984f28d5b } for _, tc := range testCases { t.Run(fmt.Sprintf("%s/%s", tc.prog.String(), tc.helper.String()), func(t *testing.T) { feature := fmt.Sprintf("helper %s for program type %s", tc.helper.String(), tc.prog.String()) testutils.SkipOnOldKernel(t, tc.version, feature) err := HaveProgramHelper(tc.prog, tc.helper) if !errors.Is(err, tc.expected) { t.Fatalf("%s/%s: %v", tc.prog.String(), tc.helper.String(), err) } }) } } func TestHelperProbeNotImplemented(t *testing.T) { // Currently we don't support probing helpers for Tracing, Extension, LSM and StructOps programs. // For each of those test the availability of the FnMapLookupElem helper and expect it to fail. for _, pt := range []ebpf.ProgramType{ebpf.Tracing, ebpf.Extension, ebpf.LSM, ebpf.StructOps} { t.Run(pt.String(), func(t *testing.T) { if err := HaveProgramHelper(pt, asm.FnMapLookupElem); err == nil { t.Fatal("Expected an error") } }) } } golang-github-cilium-ebpf-0.11.0/features/version.go000066400000000000000000000011651456504511400223540ustar00rootroot00000000000000package features import "github.com/cilium/ebpf/internal" // LinuxVersionCode returns the version of the currently running kernel // as defined in the LINUX_VERSION_CODE compile-time macro. It is represented // in the format described by the KERNEL_VERSION macro from linux/version.h. // // Do not use the version to make assumptions about the presence of certain // kernel features, always prefer feature probes in this package. Some // distributions backport or disable eBPF features. func LinuxVersionCode() (uint32, error) { v, err := internal.KernelVersion() if err != nil { return 0, err } return v.Kernel(), nil } golang-github-cilium-ebpf-0.11.0/fuzz_test.go000066400000000000000000000007051456504511400211050ustar00rootroot00000000000000package ebpf import ( "bytes" "debug/elf" "testing" ) func FuzzLoadCollectionSpec(f *testing.F) { f.Add([]byte(elf.ELFMAG)) f.Fuzz(func(t *testing.T, data []byte) { if len(data) < len(elf.ELFMAG) { t.Skip("input can't be valid ELF") } spec, err := LoadCollectionSpecFromReader(bytes.NewReader(data)) if err != nil { if spec != nil { t.Fatal("spec is not nil") } } else if spec == nil { t.Fatal("spec is nil") } }) } golang-github-cilium-ebpf-0.11.0/go.mod000066400000000000000000000005331456504511400176260ustar00rootroot00000000000000module github.com/cilium/ebpf go 1.19 require ( github.com/frankban/quicktest v1.14.5 github.com/google/go-cmp v0.5.9 golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 golang.org/x/sys v0.6.0 ) require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect ) golang-github-cilium-ebpf-0.11.0/go.sum000066400000000000000000000025611456504511400176560ustar00rootroot00000000000000github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-cilium-ebpf-0.11.0/helpers_test.go000066400000000000000000000011271456504511400215500ustar00rootroot00000000000000package ebpf import ( "errors" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/testutils" ) func haveTestmod(tb testing.TB) bool { haveTestmod := false if !testutils.IsKernelLessThan(tb, "5.11") { // See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744 testmod, err := btf.FindHandle(func(info *btf.HandleInfo) bool { return info.IsModule() && info.Name == "bpf_testmod" }) if err != nil && !errors.Is(err, btf.ErrNotFound) { tb.Fatal(err) } haveTestmod = testmod != nil testmod.Close() } return haveTestmod } golang-github-cilium-ebpf-0.11.0/info.go000066400000000000000000000224361456504511400200100ustar00rootroot00000000000000package ebpf import ( "bufio" "bytes" "encoding/hex" "errors" "fmt" "io" "os" "strings" "syscall" "time" "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // MapInfo describes a map. type MapInfo struct { Type MapType id MapID KeySize uint32 ValueSize uint32 MaxEntries uint32 Flags uint32 // Name as supplied by user space at load time. Available from 4.15. Name string } func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { var info sys.MapInfo err := sys.ObjInfo(fd, &info) if errors.Is(err, syscall.EINVAL) { return newMapInfoFromProc(fd) } if err != nil { return nil, err } return &MapInfo{ MapType(info.Type), MapID(info.Id), info.KeySize, info.ValueSize, info.MaxEntries, uint32(info.MapFlags), unix.ByteSliceToString(info.Name[:]), }, nil } func newMapInfoFromProc(fd *sys.FD) (*MapInfo, error) { var mi MapInfo err := scanFdInfo(fd, map[string]interface{}{ "map_type": &mi.Type, "key_size": &mi.KeySize, "value_size": &mi.ValueSize, "max_entries": &mi.MaxEntries, "map_flags": &mi.Flags, }) if err != nil { return nil, err } return &mi, nil } // ID returns the map ID. // // Available from 4.13. // // The bool return value indicates whether this optional field is available. func (mi *MapInfo) ID() (MapID, bool) { return mi.id, mi.id > 0 } // programStats holds statistics of a program. type programStats struct { // Total accumulated runtime of the program ins ns. runtime time.Duration // Total number of times the program was called. runCount uint64 } // ProgramInfo describes a program. type ProgramInfo struct { Type ProgramType id ProgramID // Truncated hash of the BPF bytecode. Available from 4.13. Tag string // Name as supplied by user space at load time. Available from 4.15. Name string createdByUID uint32 haveCreatedByUID bool btf btf.ID stats *programStats maps []MapID insns []byte } func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { var info sys.ProgInfo err := sys.ObjInfo(fd, &info) if errors.Is(err, syscall.EINVAL) { return newProgramInfoFromProc(fd) } if err != nil { return nil, err } pi := ProgramInfo{ Type: ProgramType(info.Type), id: ProgramID(info.Id), Tag: hex.EncodeToString(info.Tag[:]), Name: unix.ByteSliceToString(info.Name[:]), btf: btf.ID(info.BtfId), stats: &programStats{ runtime: time.Duration(info.RunTimeNs), runCount: info.RunCnt, }, } // Start with a clean struct for the second call, otherwise we may get EFAULT. var info2 sys.ProgInfo if info.NrMapIds > 0 { pi.maps = make([]MapID, info.NrMapIds) info2.NrMapIds = info.NrMapIds info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0])) } else if haveProgramInfoMapIDs() == nil { // This program really has no associated maps. pi.maps = make([]MapID, 0) } else { // The kernel doesn't report associated maps. pi.maps = nil } // createdByUID and NrMapIds were introduced in the same kernel version. if pi.maps != nil { pi.createdByUID = info.CreatedByUid pi.haveCreatedByUID = true } if info.XlatedProgLen > 0 { pi.insns = make([]byte, info.XlatedProgLen) info2.XlatedProgLen = info.XlatedProgLen info2.XlatedProgInsns = sys.NewSlicePointer(pi.insns) } if info.NrMapIds > 0 || info.XlatedProgLen > 0 { if err := sys.ObjInfo(fd, &info2); err != nil { return nil, err } } return &pi, nil } func newProgramInfoFromProc(fd *sys.FD) (*ProgramInfo, error) { var info ProgramInfo err := scanFdInfo(fd, map[string]interface{}{ "prog_type": &info.Type, "prog_tag": &info.Tag, }) if errors.Is(err, errMissingFields) { return nil, &internal.UnsupportedFeatureError{ Name: "reading program info from /proc/self/fdinfo", MinimumVersion: internal.Version{4, 10, 0}, } } if err != nil { return nil, err } return &info, nil } // ID returns the program ID. // // Available from 4.13. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) ID() (ProgramID, bool) { return pi.id, pi.id > 0 } // CreatedByUID returns the Uid that created the program. // // Available from 4.15. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) CreatedByUID() (uint32, bool) { return pi.createdByUID, pi.haveCreatedByUID } // BTFID returns the BTF ID associated with the program. // // The ID is only valid as long as the associated program is kept alive. // Available from 5.0. // // The bool return value indicates whether this optional field is available and // populated. (The field may be available but not populated if the kernel // supports the field but the program was loaded without BTF information.) func (pi *ProgramInfo) BTFID() (btf.ID, bool) { return pi.btf, pi.btf > 0 } // RunCount returns the total number of times the program was called. // // Can return 0 if the collection of statistics is not enabled. See EnableStats(). // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) RunCount() (uint64, bool) { if pi.stats != nil { return pi.stats.runCount, true } return 0, false } // Runtime returns the total accumulated runtime of the program. // // Can return 0 if the collection of statistics is not enabled. See EnableStats(). // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) Runtime() (time.Duration, bool) { if pi.stats != nil { return pi.stats.runtime, true } return time.Duration(0), false } // Instructions returns the 'xlated' instruction stream of the program // after it has been verified and rewritten by the kernel. These instructions // cannot be loaded back into the kernel as-is, this is mainly used for // inspecting loaded programs for troubleshooting, dumping, etc. // // For example, map accesses are made to reference their kernel map IDs, // not the FDs they had when the program was inserted. Note that before // the introduction of bpf_insn_prepare_dump in kernel 4.16, xlated // instructions were not sanitized, making the output even less reusable // and less likely to round-trip or evaluate to the same program Tag. // // The first instruction is marked as a symbol using the Program's name. // // Available from 4.13. Requires CAP_BPF or equivalent. func (pi *ProgramInfo) Instructions() (asm.Instructions, error) { // If the calling process is not BPF-capable or if the kernel doesn't // support getting xlated instructions, the field will be zero. if len(pi.insns) == 0 { return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) } r := bytes.NewReader(pi.insns) var insns asm.Instructions if err := insns.Unmarshal(r, internal.NativeEndian); err != nil { return nil, fmt.Errorf("unmarshaling instructions: %w", err) } // Tag the first instruction with the name of the program, if available. insns[0] = insns[0].WithSymbol(pi.Name) return insns, nil } // MapIDs returns the maps related to the program. // // Available from 4.15. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { return pi.maps, pi.maps != nil } func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) if err != nil { return err } defer fh.Close() if err := scanFdInfoReader(fh, fields); err != nil { return fmt.Errorf("%s: %w", fh.Name(), err) } return nil } var errMissingFields = errors.New("missing fields") func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { var ( scanner = bufio.NewScanner(r) scanned int ) for scanner.Scan() { parts := strings.SplitN(scanner.Text(), "\t", 2) if len(parts) != 2 { continue } name := strings.TrimSuffix(parts[0], ":") field, ok := fields[string(name)] if !ok { continue } if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 { return fmt.Errorf("can't parse field %s: %v", name, err) } scanned++ } if err := scanner.Err(); err != nil { return err } if len(fields) > 0 && scanned == 0 { return ErrNotSupported } if scanned != len(fields) { return errMissingFields } return nil } // EnableStats starts the measuring of the runtime // and run counts of eBPF programs. // // Collecting statistics can have an impact on the performance. // // Requires at least 5.8. func EnableStats(which uint32) (io.Closer, error) { fd, err := sys.EnableStats(&sys.EnableStatsAttr{ Type: which, }) if err != nil { return nil, err } return fd, nil } var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", "4.15", func() error { prog, err := progLoad(asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, SocketFilter, "MIT") if err != nil { return err } defer prog.Close() err = sys.ObjInfo(prog, &sys.ProgInfo{ // NB: Don't need to allocate MapIds since the program isn't using // any maps. NrMapIds: 1, }) if errors.Is(err, unix.EINVAL) { // Most likely the syscall doesn't exist. return internal.ErrNotSupported } if errors.Is(err, unix.E2BIG) { // We've hit check_uarg_tail_zero on older kernels. return internal.ErrNotSupported } return err }) golang-github-cilium-ebpf-0.11.0/info_test.go000066400000000000000000000213321456504511400210410ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "os" "strings" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestMapInfoFromProc(t *testing.T) { hash, err := NewMap(&MapSpec{ Name: "testing", Type: Hash, KeySize: 4, ValueSize: 5, MaxEntries: 2, Flags: unix.BPF_F_NO_PREALLOC, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer hash.Close() info, err := newMapInfoFromProc(hash.fd) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't get map info:", err) } if info.Type != Hash { t.Error("Expected Hash, got", info.Type) } if info.KeySize != 4 { t.Error("Expected KeySize of 4, got", info.KeySize) } if info.ValueSize != 5 { t.Error("Expected ValueSize of 5, got", info.ValueSize) } if info.MaxEntries != 2 { t.Error("Expected MaxEntries of 2, got", info.MaxEntries) } if info.Flags != unix.BPF_F_NO_PREALLOC { t.Errorf("Expected Flags to be %d, got %d", unix.BPF_F_NO_PREALLOC, info.Flags) } if info.Name != "" && info.Name != "testing" { t.Error("Expected name to be testing, got", info.Name) } if _, ok := info.ID(); ok { t.Error("Expected ID to not be available") } nested, err := NewMap(&MapSpec{ Type: ArrayOfMaps, KeySize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer nested.Close() _, err = newMapInfoFromProc(nested.fd) if err != nil { t.Fatal("Can't get nested map info from /proc:", err) } } func TestProgramInfo(t *testing.T) { prog := mustSocketFilter(t) for name, fn := range map[string]func(*sys.FD) (*ProgramInfo, error){ "generic": newProgramInfoFromFd, "proc": newProgramInfoFromProc, } { t.Run(name, func(t *testing.T) { info, err := fn(prog.fd) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't get program info:", err) } if info.Type != SocketFilter { t.Error("Expected Type to be SocketFilter, got", info.Type) } if info.Name != "" && info.Name != "test" { t.Error("Expected Name to be test, got", info.Name) } if want := "d7edec644f05498d"; info.Tag != want { t.Errorf("Expected Tag to be %s, got %s", want, info.Tag) } if id, ok := info.ID(); ok && id == 0 { t.Error("Expected a valid ID:", id) } else if name == "proc" && ok { t.Error("Expected ID to not be available") } if name == "proc" { _, ok := info.CreatedByUID() qt.Assert(t, ok, qt.IsFalse) } else { uid, ok := info.CreatedByUID() if testutils.IsKernelLessThan(t, "4.15") { qt.Assert(t, ok, qt.IsFalse) } else { qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, uid, qt.Equals, uint32(os.Getuid())) } } }) } } func TestProgramInfoMapIDs(t *testing.T) { arr, err := NewMap(&MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) qt.Assert(t, err, qt.IsNil) defer arr.Close() prog, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R0, arr.FD()), asm.LoadImm(asm.R0, 2, asm.DWord), asm.Return(), }, License: "MIT", }) qt.Assert(t, err, qt.IsNil) defer prog.Close() info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, err, qt.IsNil) ids, ok := info.MapIDs() switch { case testutils.IsKernelLessThan(t, "4.15"): qt.Assert(t, ok, qt.IsFalse) qt.Assert(t, ids, qt.HasLen, 0) default: qt.Assert(t, ok, qt.IsTrue) mapInfo, err := arr.Info() qt.Assert(t, err, qt.IsNil) mapID, ok := mapInfo.ID() qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, ids, qt.ContentEquals, []MapID{mapID}) } } func TestProgramInfoMapIDsNoMaps(t *testing.T) { prog, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) qt.Assert(t, err, qt.IsNil) defer prog.Close() info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, err, qt.IsNil) ids, ok := info.MapIDs() switch { case testutils.IsKernelLessThan(t, "4.15"): qt.Assert(t, ok, qt.IsFalse) qt.Assert(t, ids, qt.HasLen, 0) default: qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, ids, qt.HasLen, 0) } } func TestScanFdInfoReader(t *testing.T) { tests := []struct { fields map[string]interface{} valid bool }{ {nil, true}, {map[string]interface{}{"foo": new(string)}, true}, {map[string]interface{}{"zap": new(string)}, false}, {map[string]interface{}{"foo": new(int)}, false}, } for _, test := range tests { err := scanFdInfoReader(strings.NewReader("foo:\tbar\n"), test.fields) if test.valid { if err != nil { t.Errorf("fields %v returns an error: %s", test.fields, err) } } else { if err == nil { t.Errorf("fields %v doesn't return an error", test.fields) } } } } // TestStats loads a BPF program once and executes back-to-back test runs // of the program. See testStats for details. func TestStats(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF_ENABLE_STATS") prog := mustSocketFilter(t) pi, err := prog.Info() if err != nil { t.Errorf("failed to get ProgramInfo: %v", err) } rc, ok := pi.RunCount() if !ok { t.Errorf("expected run count info to be available") } if rc != 0 { t.Errorf("expected a run count of 0 but got %d", rc) } rt, ok := pi.Runtime() if !ok { t.Errorf("expected runtime info to be available") } if rt != 0 { t.Errorf("expected a runtime of 0ns but got %v", rt) } if err := testStats(prog); err != nil { t.Error(err) } } // BenchmarkStats is a benchmark of TestStats. See testStats for details. func BenchmarkStats(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF_ENABLE_STATS") prog := mustSocketFilter(b) for n := 0; n < b.N; n++ { if err := testStats(prog); err != nil { b.Fatal(fmt.Errorf("iter %d: %w", n, err)) } } } // testStats implements the behaviour under test for TestStats // and BenchmarkStats. First, a test run is executed with runtime statistics // enabled, followed by another with runtime stats disabled. Counters are only // expected to increase on the runs where runtime stats are enabled. // // Due to runtime behaviour on Go 1.14 and higher, the syscall backing // (*Program).Test() could be invoked multiple times for each call to Test(), // resulting in RunCount incrementing by more than one. Expecting RunCount to // be of a specific value after a call to Test() is therefore not possible. // See https://golang.org/doc/go1.14#runtime for more details. func testStats(prog *Program) error { in := internal.EmptyBPFContext stats, err := EnableStats(uint32(unix.BPF_STATS_RUN_TIME)) if err != nil { return fmt.Errorf("failed to enable stats: %v", err) } defer stats.Close() // Program execution with runtime statistics enabled. // Should increase both runtime and run counter. if _, _, err := prog.Test(in); err != nil { return fmt.Errorf("failed to trigger program: %v", err) } pi, err := prog.Info() if err != nil { return fmt.Errorf("failed to get ProgramInfo: %v", err) } rc, ok := pi.RunCount() if !ok { return errors.New("expected run count info to be available") } if rc < 1 { return fmt.Errorf("expected a run count of at least 1 but got %d", rc) } // Store the run count for the next invocation. lc := rc rt, ok := pi.Runtime() if !ok { return errors.New("expected runtime info to be available") } if rt == 0 { return errors.New("expected a runtime other than 0ns") } // Store the runtime value for the next invocation. lt := rt if err := stats.Close(); err != nil { return fmt.Errorf("failed to disable statistics: %v", err) } // Second program execution, with runtime statistics gathering disabled. // Total runtime and run counters are not expected to increase. if _, _, err := prog.Test(in); err != nil { return fmt.Errorf("failed to trigger program: %v", err) } pi, err = prog.Info() if err != nil { return fmt.Errorf("failed to get ProgramInfo: %v", err) } rc, ok = pi.RunCount() if !ok { return errors.New("expected run count info to be available") } if rc != lc { return fmt.Errorf("run count unexpectedly increased over previous value (current: %v, prev: %v)", rc, lc) } rt, ok = pi.Runtime() if !ok { return errors.New("expected runtime info to be available") } if rt != lt { return fmt.Errorf("runtime unexpectedly increased over the previous value (current: %v, prev: %v)", rt, lt) } return nil } func TestHaveProgramInfoMapIDs(t *testing.T) { testutils.CheckFeatureTest(t, haveProgramInfoMapIDs) } golang-github-cilium-ebpf-0.11.0/internal/000077500000000000000000000000001456504511400203335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/align.go000066400000000000000000000003331456504511400217530ustar00rootroot00000000000000package internal import "golang.org/x/exp/constraints" // Align returns 'n' updated to 'alignment' boundary. func Align[I constraints.Integer](n, alignment I) I { return (n + alignment - 1) / alignment * alignment } golang-github-cilium-ebpf-0.11.0/internal/buffer.go000066400000000000000000000014541456504511400221370ustar00rootroot00000000000000package internal import ( "bytes" "sync" ) var bytesBufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } // NewBuffer retrieves a [bytes.Buffer] from a pool an re-initialises it. // // The returned buffer should be passed to [PutBuffer]. func NewBuffer(buf []byte) *bytes.Buffer { wr := bytesBufferPool.Get().(*bytes.Buffer) // Reinitialize the Buffer with a new backing slice since it is returned to // the caller by wr.Bytes() below. Pooling is faster despite calling // NewBuffer. The pooled alloc is still reused, it only needs to be zeroed. *wr = *bytes.NewBuffer(buf) return wr } // PutBuffer releases a buffer to the pool. func PutBuffer(buf *bytes.Buffer) { // Release reference to the backing buffer. *buf = *bytes.NewBuffer(nil) bytesBufferPool.Put(buf) } golang-github-cilium-ebpf-0.11.0/internal/cmd/000077500000000000000000000000001456504511400210765ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/cmd/gentypes/000077500000000000000000000000001456504511400227345ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/cmd/gentypes/.gitignore000066400000000000000000000000111456504511400247140ustar00rootroot00000000000000gentypes golang-github-cilium-ebpf-0.11.0/internal/cmd/gentypes/main.go000066400000000000000000000453321456504511400242160ustar00rootroot00000000000000// Program gentypes reads a compressed vmlinux .BTF section and generates // syscall bindings from it. // // Output is written to "types.go". package main import ( "bytes" "errors" "fmt" "os" "sort" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) type syscallRetval int const ( retError syscallRetval = iota retFd ) func main() { if err := run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } } func run(args []string) error { if len(args) != 1 { return fmt.Errorf("expect location of compressed vmlinux .BTF as argument") } raw, err := internal.ReadAllCompressed(args[0]) if err != nil { return err } spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) if err != nil { return err } output, err := generateTypes(spec) if err != nil { return err } w, err := os.Create("types.go") if err != nil { return err } defer w.Close() return internal.WriteFormatted(output, w) } func generateTypes(spec *btf.Spec) ([]byte, error) { objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}} linkID := &btf.Int{Size: 4} btfID := &btf.Int{Size: 4} typeID := &btf.Int{Size: 4} pointer := &btf.Int{Size: 8} logLevel := &btf.Int{Size: 4} mapFlags := &btf.Int{Size: 4} gf := &btf.GoFormatter{ Names: map[btf.Type]string{ objName: internal.GoTypeName(sys.ObjName{}), linkID: internal.GoTypeName(sys.LinkID(0)), btfID: internal.GoTypeName(sys.BTFID(0)), typeID: internal.GoTypeName(sys.TypeID(0)), pointer: internal.GoTypeName(sys.Pointer{}), logLevel: internal.GoTypeName(sys.LogLevel(0)), mapFlags: internal.GoTypeName(sys.MapFlags(0)), }, Identifier: internal.Identifier, EnumIdentifier: func(name, element string) string { return element }, } w := bytes.NewBuffer(nil) w.WriteString(`// Code generated by internal/cmd/gentypes; DO NOT EDIT. package sys import ( "unsafe" ) `) enums := []struct { goType string cType string }{ {"Cmd", "bpf_cmd"}, {"MapType", "bpf_map_type"}, {"ProgType", "bpf_prog_type"}, {"AttachType", "bpf_attach_type"}, {"LinkType", "bpf_link_type"}, {"StatsType", "bpf_stats_type"}, {"SkAction", "sk_action"}, {"StackBuildIdStatus", "bpf_stack_build_id_status"}, {"FunctionId", "bpf_func_id"}, {"AdjRoomMode", "bpf_adj_room_mode"}, {"HdrStartOff", "bpf_hdr_start_off"}, {"RetCode", "bpf_ret_code"}, {"XdpAction", "xdp_action"}, } sort.Slice(enums, func(i, j int) bool { return enums[i].goType < enums[j].goType }) enumTypes := make(map[string]btf.Type) for _, o := range enums { fmt.Println("enum", o.goType) var t *btf.Enum if err := spec.TypeByName(o.cType, &t); err != nil { return nil, err } // Add the enum as a predeclared type so that generated structs // refer to the Go types. if name := gf.Names[t]; name != "" { return nil, fmt.Errorf("type %q is already declared as %s", o.cType, name) } gf.Names[t] = o.goType enumTypes[o.goType] = t decl, err := gf.TypeDeclaration(o.goType, t) if err != nil { return nil, fmt.Errorf("generate %q: %w", o.goType, err) } w.WriteString(decl) w.WriteRune('\n') } // Assorted structs structs := []struct { goType string cType string patches []patch }{ { "ProgInfo", "bpf_prog_info", []patch{ replace(objName, "name"), replace(pointer, "xlated_prog_insns"), replace(pointer, "map_ids"), replace(btfID, "btf_id"), }, }, { "MapInfo", "bpf_map_info", []patch{ replace(objName, "name"), replace(mapFlags, "map_flags"), replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), }, }, { "BtfInfo", "bpf_btf_info", []patch{ replace(pointer, "btf", "name"), replace(btfID, "id"), }, }, { "LinkInfo", "bpf_link_info", []patch{ replace(enumTypes["LinkType"], "type"), replace(linkID, "id"), name(3, "extra"), replaceWithBytes("extra"), }, }, {"FuncInfo", "bpf_func_info", nil}, {"LineInfo", "bpf_line_info", nil}, {"XdpMd", "xdp_md", nil}, { "SkLookup", "bpf_sk_lookup", []patch{ choose(0, "cookie"), replaceWithBytes("remote_ip4", "remote_ip6", "local_ip4", "local_ip6"), }, }, } sort.Slice(structs, func(i, j int) bool { return structs[i].goType < structs[j].goType }) for _, s := range structs { fmt.Println("struct", s.goType) var t *btf.Struct if err := spec.TypeByName(s.cType, &t); err != nil { return nil, err } if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { return nil, fmt.Errorf("output %q: %w", s.goType, err) } } // Attrs attrs := []struct { goType string ret syscallRetval cType string cmd string patches []patch }{ { "MapCreate", retFd, "map_create", "BPF_MAP_CREATE", []patch{ replace(objName, "map_name"), replace(enumTypes["MapType"], "map_type"), replace(mapFlags, "map_flags"), replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), }, }, { "MapLookupElem", retError, "map_elem", "BPF_MAP_LOOKUP_ELEM", []patch{choose(2, "value"), replace(pointer, "key", "value")}, }, { "MapLookupAndDeleteElem", retError, "map_elem", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", []patch{choose(2, "value"), replace(pointer, "key", "value")}, }, { "MapUpdateElem", retError, "map_elem", "BPF_MAP_UPDATE_ELEM", []patch{choose(2, "value"), replace(pointer, "key", "value")}, }, { "MapDeleteElem", retError, "map_elem", "BPF_MAP_DELETE_ELEM", []patch{choose(2, "value"), replace(pointer, "key", "value")}, }, { "MapGetNextKey", retError, "map_elem", "BPF_MAP_GET_NEXT_KEY", []patch{ choose(2, "next_key"), replace(pointer, "key", "next_key"), truncateAfter("next_key"), }, }, { "MapFreeze", retError, "map_elem", "BPF_MAP_FREEZE", []patch{truncateAfter("map_fd")}, }, { "MapLookupBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_BATCH", []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapLookupAndDeleteBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapUpdateBatch", retError, "map_elem_batch", "BPF_MAP_UPDATE_BATCH", []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapDeleteBatch", retError, "map_elem_batch", "BPF_MAP_DELETE_BATCH", []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, }, { "ProgLoad", retFd, "prog_load", "BPF_PROG_LOAD", []patch{ replace(objName, "prog_name"), replace(enumTypes["ProgType"], "prog_type"), replace(enumTypes["AttachType"], "expected_attach_type"), replace(logLevel, "log_level"), replace(pointer, "insns", "license", "log_buf", "func_info", "line_info", "fd_array", "core_relos", ), replace(typeID, "attach_btf_id"), choose(20, "attach_btf_obj_fd"), }, }, { "ProgBindMap", retError, "prog_bind_map", "BPF_PROG_BIND_MAP", nil, }, { "ObjPin", retError, "obj_pin", "BPF_OBJ_PIN", []patch{replace(pointer, "pathname")}, }, { "ObjGet", retFd, "obj_pin", "BPF_OBJ_GET", []patch{replace(pointer, "pathname")}, }, { "ProgAttach", retError, "prog_attach", "BPF_PROG_ATTACH", nil, }, { "ProgDetach", retError, "prog_attach", "BPF_PROG_DETACH", []patch{truncateAfter("attach_type")}, }, { "ProgRun", retError, "prog_run", "BPF_PROG_TEST_RUN", []patch{replace(pointer, "data_in", "data_out", "ctx_in", "ctx_out")}, }, { "ProgGetNextId", retError, "obj_next_id", "BPF_PROG_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), truncateAfter("next_id"), }, }, { "MapGetNextId", retError, "obj_next_id", "BPF_MAP_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), truncateAfter("next_id"), }, }, { "BtfGetNextId", retError, "obj_next_id", "BPF_BTF_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), replace(btfID, "id", "next_id"), truncateAfter("next_id"), }, }, // These piggy back on the obj_next_id decl, but only support the // first field... { "BtfGetFdById", retFd, "obj_next_id", "BPF_BTF_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "MapGetFdById", retFd, "obj_next_id", "BPF_MAP_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "ProgGetFdById", retFd, "obj_next_id", "BPF_PROG_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "ObjGetInfoByFd", retError, "info_by_fd", "BPF_OBJ_GET_INFO_BY_FD", []patch{replace(pointer, "info")}, }, { "RawTracepointOpen", retFd, "raw_tracepoint_open", "BPF_RAW_TRACEPOINT_OPEN", []patch{replace(pointer, "name")}, }, { "BtfLoad", retFd, "btf_load", "BPF_BTF_LOAD", []patch{replace(pointer, "btf", "btf_log_buf")}, }, { "LinkCreate", retFd, "link_create", "BPF_LINK_CREATE", []patch{ replace(enumTypes["AttachType"], "attach_type"), choose(4, "target_btf_id"), replace(typeID, "target_btf_id"), }, }, { "LinkCreateIter", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 1), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, replace(pointer, "iter_info"), }, }, { "LinkCreatePerfEvent", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 2), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, }, }, { "LinkCreateKprobeMulti", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 3), replace(enumTypes["AttachType"], "attach_type"), modify(func(m *btf.Member) error { return rename("flags", "kprobe_multi_flags")(m.Type.(*btf.Struct)) }, "kprobe_multi"), flattenAnon, replace(pointer, "cookies"), replace(pointer, "addrs"), replace(pointer, "syms"), rename("cnt", "count"), }, }, { "LinkCreateTracing", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 4), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, replace(btfID, "target_btf_id"), }, }, { "LinkUpdate", retError, "link_update", "BPF_LINK_UPDATE", nil, }, { "EnableStats", retFd, "enable_stats", "BPF_ENABLE_STATS", nil, }, { "IterCreate", retFd, "iter_create", "BPF_ITER_CREATE", nil, }, { "ProgQuery", retError, "prog_query", "BPF_PROG_QUERY", []patch{ replace(enumTypes["AttachType"], "attach_type"), replace(pointer, "prog_ids"), rename("prog_cnt", "prog_count"), }, }, } sort.Slice(attrs, func(i, j int) bool { return attrs[i].goType < attrs[j].goType }) var bpfAttr *btf.Union if err := spec.TypeByName("bpf_attr", &bpfAttr); err != nil { return nil, err } attrTypes, err := splitUnion(bpfAttr, types{ {"map_create", "map_type"}, {"map_elem", "map_fd"}, {"map_elem_batch", "batch"}, {"prog_load", "prog_type"}, {"obj_pin", "pathname"}, {"prog_attach", "target_fd"}, {"prog_run", "test"}, {"obj_next_id", ""}, {"info_by_fd", "info"}, {"prog_query", "query"}, {"raw_tracepoint_open", "raw_tracepoint"}, {"btf_load", "btf"}, {"task_fd_query", "task_fd_query"}, {"link_create", "link_create"}, {"link_update", "link_update"}, {"link_detach", "link_detach"}, {"enable_stats", "enable_stats"}, {"iter_create", "iter_create"}, {"prog_bind_map", "prog_bind_map"}, }) if err != nil { return nil, fmt.Errorf("splitting bpf_attr: %w", err) } for _, s := range attrs { fmt.Println("attr", s.goType) t := attrTypes[s.cType] if t == nil { return nil, fmt.Errorf("unknown attr %q", s.cType) } goAttrType := s.goType + "Attr" if err := outputPatchedStruct(gf, w, goAttrType, t, s.patches); err != nil { return nil, fmt.Errorf("output %q: %w", goAttrType, err) } switch s.ret { case retError: fmt.Fprintf(w, "func %s(attr *%s) error { _, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); return err }\n\n", s.goType, goAttrType, s.cmd) case retFd: fmt.Fprintf(w, "func %s(attr *%s) (*FD, error) { fd, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); if err != nil { return nil, err }; return NewFD(int(fd)) }\n\n", s.goType, goAttrType, s.cmd) } } // Link info type specific linkInfoExtraTypes := []struct { goType string cType string patches []patch }{ {"CgroupLinkInfo", "cgroup", []patch{replace(enumTypes["AttachType"], "attach_type")}}, {"IterLinkInfo", "iter", []patch{replace(pointer, "target_name"), truncateAfter("target_name_len")}}, {"NetNsLinkInfo", "netns", []patch{replace(enumTypes["AttachType"], "attach_type")}}, {"RawTracepointLinkInfo", "raw_tracepoint", []patch{replace(pointer, "tp_name")}}, {"TracingLinkInfo", "tracing", []patch{ replace(enumTypes["AttachType"], "attach_type"), replace(typeID, "target_btf_id")}, }, {"XDPLinkInfo", "xdp", nil}, } sort.Slice(linkInfoExtraTypes, func(i, j int) bool { return linkInfoExtraTypes[i].goType < linkInfoExtraTypes[j].goType }) var bpfLinkInfo *btf.Struct if err := spec.TypeByName("bpf_link_info", &bpfLinkInfo); err != nil { return nil, err } member := bpfLinkInfo.Members[len(bpfLinkInfo.Members)-1] bpfLinkInfoUnion, ok := member.Type.(*btf.Union) if !ok { return nil, fmt.Errorf("there is not type-specific union") } linkInfoTypes, err := splitUnion(bpfLinkInfoUnion, types{ {"raw_tracepoint", "raw_tracepoint"}, {"tracing", "tracing"}, {"cgroup", "cgroup"}, {"iter", "iter"}, {"netns", "netns"}, {"xdp", "xdp"}, }) if err != nil { return nil, fmt.Errorf("splitting linkInfo: %w", err) } for _, s := range linkInfoExtraTypes { t := linkInfoTypes[s.cType] if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { return nil, fmt.Errorf("output %q: %w", s.goType, err) } } return w.Bytes(), nil } func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error { s = btf.Copy(s, nil).(*btf.Struct) for i, p := range patches { if err := p(s); err != nil { return fmt.Errorf("patch %d: %w", i, err) } } decl, err := gf.TypeDeclaration(id, s) if err != nil { return err } w.WriteString(decl) w.WriteString("\n\n") return nil } type types []struct { name string cFieldOrFirstMember string } func splitUnion(union *btf.Union, types types) (map[string]*btf.Struct, error) { structs := make(map[string]*btf.Struct) for i, t := range types { member := union.Members[i] s, ok := member.Type.(*btf.Struct) if !ok { return nil, fmt.Errorf("%q: %s is not a struct", t.name, member.Type) } if member.Name == "" { // This is an anonymous struct, check the name of the first member instead. if name := s.Members[0].Name; name != t.cFieldOrFirstMember { return nil, fmt.Errorf("first field of %q is %q, not %q", t.name, name, t.cFieldOrFirstMember) } } else if member.Name != t.cFieldOrFirstMember { return nil, fmt.Errorf("name for %q is %q, not %q", t.name, member.Name, t.cFieldOrFirstMember) } structs[t.name] = s } return structs, nil } type patch func(*btf.Struct) error func modify(fn func(*btf.Member) error, members ...string) patch { return func(s *btf.Struct) error { want := make(map[string]bool) for _, name := range members { want[name] = true } for i, m := range s.Members { if want[m.Name] { if err := fn(&s.Members[i]); err != nil { return err } delete(want, m.Name) } } if len(want) == 0 { return nil } var missing []string for name := range want { missing = append(missing, name) } sort.Strings(missing) return fmt.Errorf("missing members: %v", strings.Join(missing, ", ")) } } func modifyNth(fn func(*btf.Member) error, indices ...int) patch { return func(s *btf.Struct) error { for _, i := range indices { if i >= len(s.Members) { return fmt.Errorf("index %d is out of bounds", i) } if err := fn(&s.Members[i]); err != nil { return fmt.Errorf("member #%d: %w", i, err) } } return nil } } func replace(t btf.Type, members ...string) patch { return modify(func(m *btf.Member) error { m.Type = t return nil }, members...) } func choose(member int, name string) patch { return modifyNth(func(m *btf.Member) error { union, ok := m.Type.(*btf.Union) if !ok { return fmt.Errorf("member %d is %s, not a union", member, m.Type) } for _, um := range union.Members { if um.Name == name { m.Name = um.Name m.Type = um.Type return nil } } return fmt.Errorf("%s has no member %q", union, name) }, member) } func chooseNth(member int, n int) patch { return modifyNth(func(m *btf.Member) error { union, ok := m.Type.(*btf.Union) if !ok { return fmt.Errorf("member %d is %s, not a union", member, m.Type) } if n >= len(union.Members) { return fmt.Errorf("member %d is out of bounds", n) } um := union.Members[n] m.Name = um.Name m.Type = um.Type return nil }, member) } func flattenAnon(s *btf.Struct) error { for i := range s.Members { m := &s.Members[i] cs, ok := m.Type.(*btf.Struct) if !ok || cs.TypeName() != "" { continue } for j := range cs.Members { cs.Members[j].Offset += m.Offset } newMembers := make([]btf.Member, 0, len(s.Members)+len(cs.Members)-1) newMembers = append(newMembers, s.Members[:i]...) newMembers = append(newMembers, cs.Members...) newMembers = append(newMembers, s.Members[i+1:]...) s.Members = newMembers } return nil } func truncateAfter(name string) patch { return func(s *btf.Struct) error { for i, m := range s.Members { if m.Name != name { continue } size, err := btf.Sizeof(m.Type) if err != nil { return err } s.Members = s.Members[:i+1] s.Size = m.Offset.Bytes() + uint32(size) return nil } return fmt.Errorf("no member %q", name) } } func rename(from, to string) patch { return func(s *btf.Struct) error { for i, m := range s.Members { if m.Name == from { s.Members[i].Name = to return nil } } return fmt.Errorf("no member named %q", from) } } func name(member int, name string) patch { return modifyNth(func(m *btf.Member) error { if m.Name != "" { return fmt.Errorf("member already has name %q", m.Name) } m.Name = name return nil }, member) } func replaceWithBytes(members ...string) patch { return modify(func(m *btf.Member) error { if m.BitfieldSize != 0 { return errors.New("replaceWithBytes: member is a bitfield") } size, err := btf.Sizeof(m.Type) if err != nil { return fmt.Errorf("replaceWithBytes: size of %s: %w", m.Type, err) } m.Type = &btf.Array{ Type: &btf.Int{Size: 1}, Nelems: uint32(size), } return nil }, members...) } golang-github-cilium-ebpf-0.11.0/internal/cpu.go000066400000000000000000000023331456504511400214520ustar00rootroot00000000000000package internal import ( "fmt" "os" "strings" ) // PossibleCPUs returns the max number of CPUs a system may possibly have // Logical CPU numbers must be of the form 0-n var PossibleCPUs = Memoize(func() (int, error) { return parseCPUsFromFile("/sys/devices/system/cpu/possible") }) func parseCPUsFromFile(path string) (int, error) { spec, err := os.ReadFile(path) if err != nil { return 0, err } n, err := parseCPUs(string(spec)) if err != nil { return 0, fmt.Errorf("can't parse %s: %v", path, err) } return n, nil } // parseCPUs parses the number of cpus from a string produced // by bitmap_list_string() in the Linux kernel. // Multiple ranges are rejected, since they can't be unified // into a single number. // This is the format of /sys/devices/system/cpu/possible, it // is not suitable for /sys/devices/system/cpu/online, etc. func parseCPUs(spec string) (int, error) { if strings.Trim(spec, "\n") == "0" { return 1, nil } var low, high int n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high) if n != 2 || err != nil { return 0, fmt.Errorf("invalid format: %s", spec) } if low != 0 { return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec) } // cpus is 0 indexed return high + 1, nil } golang-github-cilium-ebpf-0.11.0/internal/cpu_test.go000066400000000000000000000010061456504511400225050ustar00rootroot00000000000000package internal import ( "testing" ) func TestParseCPUs(t *testing.T) { for str, result := range map[string]int{ "0-1": 2, "0-2\n": 3, "0": 1, } { n, err := parseCPUs(str) if err != nil { t.Errorf("Can't parse `%s`: %v", str, err) } else if n != result { t.Error("Parsing", str, "returns", n, "instead of", result) } } for _, str := range []string{ "0,3-4", "0-", "1,", "", } { _, err := parseCPUs(str) if err == nil { t.Error("Parsed invalid format:", str) } } } golang-github-cilium-ebpf-0.11.0/internal/deque.go000066400000000000000000000033401456504511400217650ustar00rootroot00000000000000package internal import "math/bits" // Deque implements a double ended queue. type Deque[T any] struct { elems []T read, write uint64 mask uint64 } // Reset clears the contents of the deque while retaining the backing buffer. func (dq *Deque[T]) Reset() { var zero T for i := dq.read; i < dq.write; i++ { dq.elems[i&dq.mask] = zero } dq.read, dq.write = 0, 0 } func (dq *Deque[T]) Empty() bool { return dq.read == dq.write } // Push adds an element to the end. func (dq *Deque[T]) Push(e T) { dq.Grow(1) dq.elems[dq.write&dq.mask] = e dq.write++ } // Shift returns the first element or the zero value. func (dq *Deque[T]) Shift() T { var zero T if dq.Empty() { return zero } index := dq.read & dq.mask t := dq.elems[index] dq.elems[index] = zero dq.read++ return t } // Pop returns the last element or the zero value. func (dq *Deque[T]) Pop() T { var zero T if dq.Empty() { return zero } dq.write-- index := dq.write & dq.mask t := dq.elems[index] dq.elems[index] = zero return t } // Grow the deque's capacity, if necessary, to guarantee space for another n // elements. func (dq *Deque[T]) Grow(n int) { have := dq.write - dq.read need := have + uint64(n) if need < have { panic("overflow") } if uint64(len(dq.elems)) >= need { return } // Round up to the new power of two which is at least 8. // See https://jameshfisher.com/2018/03/30/round-up-power-2/ capacity := 1 << (64 - bits.LeadingZeros64(need-1)) if capacity < 8 { capacity = 8 } elems := make([]T, have, capacity) pivot := dq.read & dq.mask copied := copy(elems, dq.elems[pivot:]) copy(elems[copied:], dq.elems[:pivot]) dq.elems = elems[:capacity] dq.mask = uint64(capacity) - 1 dq.read, dq.write = 0, have } golang-github-cilium-ebpf-0.11.0/internal/deque_test.go000066400000000000000000000025111456504511400230230ustar00rootroot00000000000000package internal import "testing" func TestDeque(t *testing.T) { t.Run("pop", func(t *testing.T) { var dq Deque[int] dq.Push(1) dq.Push(2) if dq.Pop() != 2 { t.Error("Didn't pop 2 first") } if dq.Pop() != 1 { t.Error("Didn't pop 1 second") } if dq.Pop() != 0 { t.Error("Didn't pop zero") } }) t.Run("shift", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) if td.Shift() != 1 { t.Error("Didn't shift 1 first") } if td.Shift() != 2 { t.Error("Didn't shift b second") } if td.Shift() != 0 { t.Error("Didn't shift zero") } }) t.Run("push", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) td.Shift() for i := 1; i <= 12; i++ { td.Push(i) } if td.Shift() != 2 { t.Error("Didn't shift 2 first") } for i := 1; i <= 12; i++ { if v := td.Shift(); v != i { t.Fatalf("Shifted %d at pos %d", v, i) } } }) t.Run("grow", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) td.Push(3) td.Shift() td.Grow(7) if len(td.elems) < 9 { t.Fatal("Expected at least 9 elements, got", len(td.elems)) } if cap(td.elems)&(cap(td.elems)-1) != 0 { t.Fatalf("Capacity %d is not a power of two", cap(td.elems)) } if td.Shift() != 2 || td.Shift() != 3 { t.Fatal("Elements don't match after grow") } }) } golang-github-cilium-ebpf-0.11.0/internal/elf.go000066400000000000000000000040541456504511400214330ustar00rootroot00000000000000package internal import ( "debug/elf" "fmt" "io" ) type SafeELFFile struct { *elf.File } // NewSafeELFFile reads an ELF safely. // // Any panic during parsing is turned into an error. This is necessary since // there are a bunch of unfixed bugs in debug/elf. // // https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+debug%2Felf+in%3Atitle func NewSafeELFFile(r io.ReaderAt) (safe *SafeELFFile, err error) { defer func() { r := recover() if r == nil { return } safe = nil err = fmt.Errorf("reading ELF file panicked: %s", r) }() file, err := elf.NewFile(r) if err != nil { return nil, err } return &SafeELFFile{file}, nil } // OpenSafeELFFile reads an ELF from a file. // // It works like NewSafeELFFile, with the exception that safe.Close will // close the underlying file. func OpenSafeELFFile(path string) (safe *SafeELFFile, err error) { defer func() { r := recover() if r == nil { return } safe = nil err = fmt.Errorf("reading ELF file panicked: %s", r) }() file, err := elf.Open(path) if err != nil { return nil, err } return &SafeELFFile{file}, nil } // Symbols is the safe version of elf.File.Symbols. func (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) { defer func() { r := recover() if r == nil { return } syms = nil err = fmt.Errorf("reading ELF symbols panicked: %s", r) }() syms, err = se.File.Symbols() return } // DynamicSymbols is the safe version of elf.File.DynamicSymbols. func (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) { defer func() { r := recover() if r == nil { return } syms = nil err = fmt.Errorf("reading ELF dynamic symbols panicked: %s", r) }() syms, err = se.File.DynamicSymbols() return } // SectionsByType returns all sections in the file with the specified section type. func (se *SafeELFFile) SectionsByType(typ elf.SectionType) []*elf.Section { sections := make([]*elf.Section, 0, 1) for _, section := range se.Sections { if section.Type == typ { sections = append(sections, section) } } return sections } golang-github-cilium-ebpf-0.11.0/internal/endian_be.go000066400000000000000000000006471456504511400225750ustar00rootroot00000000000000//go:build armbe || arm64be || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 package internal import "encoding/binary" // NativeEndian is set to either binary.BigEndian or binary.LittleEndian, // depending on the host's endianness. var NativeEndian binary.ByteOrder = binary.BigEndian // ClangEndian is set to either "el" or "eb" depending on the host's endianness. const ClangEndian = "eb" golang-github-cilium-ebpf-0.11.0/internal/endian_le.go000066400000000000000000000006731456504511400226060ustar00rootroot00000000000000//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || ppc64le || riscv64 package internal import "encoding/binary" // NativeEndian is set to either binary.BigEndian or binary.LittleEndian, // depending on the host's endianness. var NativeEndian binary.ByteOrder = binary.LittleEndian // ClangEndian is set to either "el" or "eb" depending on the host's endianness. const ClangEndian = "el" golang-github-cilium-ebpf-0.11.0/internal/epoll/000077500000000000000000000000001456504511400214465ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/epoll/poller.go000066400000000000000000000117761456504511400233060ustar00rootroot00000000000000package epoll import ( "fmt" "math" "os" "runtime" "sync" "time" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) // Poller waits for readiness notifications from multiple file descriptors. // // The wait can be interrupted by calling Close. type Poller struct { // mutexes protect the fields declared below them. If you need to // acquire both at once you must lock epollMu before eventMu. epollMu sync.Mutex epollFd int eventMu sync.Mutex event *eventFd } func New() (*Poller, error) { epollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) if err != nil { return nil, fmt.Errorf("create epoll fd: %v", err) } p := &Poller{epollFd: epollFd} p.event, err = newEventFd() if err != nil { unix.Close(epollFd) return nil, err } if err := p.Add(p.event.raw, 0); err != nil { unix.Close(epollFd) p.event.close() return nil, fmt.Errorf("add eventfd: %w", err) } runtime.SetFinalizer(p, (*Poller).Close) return p, nil } // Close the poller. // // Interrupts any calls to Wait. Multiple calls to Close are valid, but subsequent // calls will return os.ErrClosed. func (p *Poller) Close() error { runtime.SetFinalizer(p, nil) // Interrupt Wait() via the event fd if it's currently blocked. if err := p.wakeWait(); err != nil { return err } // Acquire the lock. This ensures that Wait isn't running. p.epollMu.Lock() defer p.epollMu.Unlock() // Prevent other calls to Close(). p.eventMu.Lock() defer p.eventMu.Unlock() if p.epollFd != -1 { unix.Close(p.epollFd) p.epollFd = -1 } if p.event != nil { p.event.close() p.event = nil } return nil } // Add an fd to the poller. // // id is returned by Wait in the unix.EpollEvent.Pad field any may be zero. It // must not exceed math.MaxInt32. // // Add is blocked by Wait. func (p *Poller) Add(fd int, id int) error { if int64(id) > math.MaxInt32 { return fmt.Errorf("unsupported id: %d", id) } p.epollMu.Lock() defer p.epollMu.Unlock() if p.epollFd == -1 { return fmt.Errorf("epoll add: %w", os.ErrClosed) } // The representation of EpollEvent isn't entirely accurate. // Pad is fully useable, not just padding. Hence we stuff the // id in there, which allows us to identify the event later (e.g., // in case of perf events, which CPU sent it). event := unix.EpollEvent{ Events: unix.EPOLLIN, Fd: int32(fd), Pad: int32(id), } if err := unix.EpollCtl(p.epollFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil { return fmt.Errorf("add fd to epoll: %v", err) } return nil } // Wait for events. // // Returns the number of pending events or an error wrapping os.ErrClosed if // Close is called, or os.ErrDeadlineExceeded if EpollWait timeout. func (p *Poller) Wait(events []unix.EpollEvent, deadline time.Time) (int, error) { p.epollMu.Lock() defer p.epollMu.Unlock() if p.epollFd == -1 { return 0, fmt.Errorf("epoll wait: %w", os.ErrClosed) } for { timeout := int(-1) if !deadline.IsZero() { msec := time.Until(deadline).Milliseconds() if msec < 0 { // Deadline is in the past. msec = 0 } else if msec > math.MaxInt { // Deadline is too far in the future. msec = math.MaxInt } timeout = int(msec) } n, err := unix.EpollWait(p.epollFd, events, timeout) if temp, ok := err.(temporaryError); ok && temp.Temporary() { // Retry the syscall if we were interrupted, see https://github.com/golang/go/issues/20400 continue } if err != nil { return 0, err } if n == 0 { return 0, fmt.Errorf("epoll wait: %w", os.ErrDeadlineExceeded) } for _, event := range events[:n] { if int(event.Fd) == p.event.raw { // Since we don't read p.event the event is never cleared and // we'll keep getting this wakeup until Close() acquires the // lock and sets p.epollFd = -1. return 0, fmt.Errorf("epoll wait: %w", os.ErrClosed) } } return n, nil } } type temporaryError interface { Temporary() bool } // wakeWait unblocks Wait if it's epoll_wait. func (p *Poller) wakeWait() error { p.eventMu.Lock() defer p.eventMu.Unlock() if p.event == nil { return fmt.Errorf("epoll wake: %w", os.ErrClosed) } return p.event.add(1) } // eventFd wraps a Linux eventfd. // // An eventfd acts like a counter: writes add to the counter, reads retrieve // the counter and reset it to zero. Reads also block if the counter is zero. // // See man 2 eventfd. type eventFd struct { file *os.File // prefer raw over file.Fd(), since the latter puts the file into blocking // mode. raw int } func newEventFd() (*eventFd, error) { fd, err := unix.Eventfd(0, unix.O_CLOEXEC|unix.O_NONBLOCK) if err != nil { return nil, err } file := os.NewFile(uintptr(fd), "event") return &eventFd{file, fd}, nil } func (efd *eventFd) close() error { return efd.file.Close() } func (efd *eventFd) add(n uint64) error { var buf [8]byte internal.NativeEndian.PutUint64(buf[:], 1) _, err := efd.file.Write(buf[:]) return err } func (efd *eventFd) read() (uint64, error) { var buf [8]byte _, err := efd.file.Read(buf[:]) return internal.NativeEndian.Uint64(buf[:]), err } golang-github-cilium-ebpf-0.11.0/internal/epoll/poller_test.go000066400000000000000000000044451456504511400243400ustar00rootroot00000000000000package epoll import ( "errors" "math" "os" "testing" "time" "github.com/cilium/ebpf/internal/unix" ) func TestPoller(t *testing.T) { t.Parallel() event, poller := mustNewPoller(t) done := make(chan struct{}, 1) read := func() { defer func() { done <- struct{}{} }() events := make([]unix.EpollEvent, 1) n, err := poller.Wait(events, time.Time{}) if errors.Is(err, os.ErrClosed) { return } if err != nil { t.Error("Error from wait:", err) return } if n != 1 { t.Errorf("Got %d instead of 1 events", n) } if e := events[0]; e.Pad != 42 { t.Errorf("Incorrect value in EpollEvent.Pad: %d != 42", e.Pad) } } if err := event.add(1); err != nil { t.Fatal(err) } go read() select { case <-done: case <-time.After(time.Second): t.Fatal("Timed out") } if _, err := event.read(); err != nil { t.Fatal(err) } go read() select { case <-done: t.Fatal("Wait doesn't block") case <-time.After(time.Second): } if err := poller.Close(); err != nil { t.Fatal("Close returns an error:", err) } select { case <-done: case <-time.After(time.Second): t.Fatal("Close doesn't unblock Wait") } if err := poller.Close(); !errors.Is(err, os.ErrClosed) { t.Fatal("Closing a second time doesn't return ErrClosed:", err) } } func TestPollerDeadline(t *testing.T) { t.Parallel() _, poller := mustNewPoller(t) events := make([]unix.EpollEvent, 1) _, err := poller.Wait(events, time.Now().Add(-time.Second)) if !errors.Is(err, os.ErrDeadlineExceeded) { t.Fatal("Expected os.ErrDeadlineExceeded on deadline in the past, got", err) } done := make(chan struct{}) go func() { defer close(done) _, err := poller.Wait(events, time.Now().Add(math.MaxInt64)) if !errors.Is(err, os.ErrClosed) { t.Error("Expected os.ErrClosed when interrupting deadline, got", err) } }() // Wait for the goroutine to enter the syscall. time.Sleep(time.Second) poller.Close() <-done } func mustNewPoller(t *testing.T) (*eventFd, *Poller) { t.Helper() event, err := newEventFd() if err != nil { t.Fatal(err) } t.Cleanup(func() { event.close() }) poller, err := New() if err != nil { t.Fatal(err) } t.Cleanup(func() { poller.Close() }) if err := poller.Add(event.raw, 42); err != nil { t.Fatal("Can't add fd:", err) } return event, poller } golang-github-cilium-ebpf-0.11.0/internal/errors.go000066400000000000000000000115001456504511400221730ustar00rootroot00000000000000package internal import ( "bytes" "fmt" "io" "strings" ) // ErrorWithLog wraps err in a VerifierError that includes the parsed verifier // log buffer. // // The default error output is a summary of the full log. The latter can be // accessed via VerifierError.Log or by formatting the error, see Format. func ErrorWithLog(source string, err error, log []byte, truncated bool) *VerifierError { const whitespace = "\t\r\v\n " // Convert verifier log C string by truncating it on the first 0 byte // and trimming trailing whitespace before interpreting as a Go string. if i := bytes.IndexByte(log, 0); i != -1 { log = log[:i] } log = bytes.Trim(log, whitespace) if len(log) == 0 { return &VerifierError{source, err, nil, truncated} } logLines := bytes.Split(log, []byte{'\n'}) lines := make([]string, 0, len(logLines)) for _, line := range logLines { // Don't remove leading white space on individual lines. We rely on it // when outputting logs. lines = append(lines, string(bytes.TrimRight(line, whitespace))) } return &VerifierError{source, err, lines, truncated} } // VerifierError includes information from the eBPF verifier. // // It summarises the log output, see Format if you want to output the full contents. type VerifierError struct { source string // The error which caused this error. Cause error // The verifier output split into lines. Log []string // Whether the log output is truncated, based on several heuristics. Truncated bool } func (le *VerifierError) Unwrap() error { return le.Cause } func (le *VerifierError) Error() string { log := le.Log if n := len(log); n > 0 && strings.HasPrefix(log[n-1], "processed ") { // Get rid of "processed 39 insns (limit 1000000) ..." from summary. log = log[:n-1] } var b strings.Builder fmt.Fprintf(&b, "%s: %s", le.source, le.Cause.Error()) n := len(log) if n == 0 { return b.String() } lines := log[n-1:] if n >= 2 && (includePreviousLine(log[n-1]) || le.Truncated) { // Add one more line of context if it aids understanding the error. lines = log[n-2:] } for _, line := range lines { b.WriteString(": ") b.WriteString(strings.TrimSpace(line)) } omitted := len(le.Log) - len(lines) if omitted == 0 && !le.Truncated { return b.String() } b.WriteString(" (") if le.Truncated { b.WriteString("truncated") } if omitted > 0 { if le.Truncated { b.WriteString(", ") } fmt.Fprintf(&b, "%d line(s) omitted", omitted) } b.WriteString(")") return b.String() } // includePreviousLine returns true if the given line likely is better // understood with additional context from the preceding line. func includePreviousLine(line string) bool { // We need to find a good trade off between understandable error messages // and too much complexity here. Checking the string prefix is ok, requiring // regular expressions to do it is probably overkill. if strings.HasPrefix(line, "\t") { // [13] STRUCT drm_rect size=16 vlen=4 // \tx1 type_id=2 return true } if len(line) >= 2 && line[0] == 'R' && line[1] >= '0' && line[1] <= '9' { // 0: (95) exit // R0 !read_ok return true } if strings.HasPrefix(line, "invalid bpf_context access") { // 0: (79) r6 = *(u64 *)(r1 +0) // func '__x64_sys_recvfrom' arg0 type FWD is not a struct // invalid bpf_context access off=0 size=8 return true } return false } // Format the error. // // Understood verbs are %s and %v, which are equivalent to calling Error(). %v // allows outputting additional information using the following flags: // // %+v: Output the first lines, or all lines if no width is given. // %-v: Output the last lines, or all lines if no width is given. // // Use width to specify how many lines to output. Use the '-' flag to output // lines from the end of the log instead of the beginning. func (le *VerifierError) Format(f fmt.State, verb rune) { switch verb { case 's': _, _ = io.WriteString(f, le.Error()) case 'v': n, haveWidth := f.Width() if !haveWidth || n > len(le.Log) { n = len(le.Log) } if !f.Flag('+') && !f.Flag('-') { if haveWidth { _, _ = io.WriteString(f, "%!v(BADWIDTH)") return } _, _ = io.WriteString(f, le.Error()) return } if f.Flag('+') && f.Flag('-') { _, _ = io.WriteString(f, "%!v(BADFLAG)") return } fmt.Fprintf(f, "%s: %s:", le.source, le.Cause.Error()) omitted := len(le.Log) - n lines := le.Log[:n] if f.Flag('-') { // Print last instead of first lines. lines = le.Log[len(le.Log)-n:] if omitted > 0 { fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted) } } for _, line := range lines { fmt.Fprintf(f, "\n\t%s", line) } if !f.Flag('-') { if omitted > 0 { fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted) } } if le.Truncated { fmt.Fprintf(f, "\n\t(truncated)") } default: fmt.Fprintf(f, "%%!%c(BADVERB)", verb) } } golang-github-cilium-ebpf-0.11.0/internal/errors_test.go000066400000000000000000000065751456504511400232520ustar00rootroot00000000000000package internal import ( "errors" "os" "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestVerifierErrorWhitespace(t *testing.T) { b := []byte("unreachable insn 28") b = append(b, 0xa, // \n 0xd, // \r 0x9, // \t 0x20, // space 0, 0, // trailing NUL bytes ) err := ErrorWithLog("frob", errors.New("test"), b, false) qt.Assert(t, err.Error(), qt.Equals, "frob: test: unreachable insn 28") for _, log := range [][]byte{ nil, []byte("\x00"), []byte(" "), } { err = ErrorWithLog("frob", errors.New("test"), log, false) qt.Assert(t, err.Error(), qt.Equals, "frob: test", qt.Commentf("empty log %q has incorrect format", log)) } } func TestVerifierErrorWrapping(t *testing.T) { ve := ErrorWithLog("frob", unix.ENOENT, nil, false) qt.Assert(t, ve, qt.ErrorIs, unix.ENOENT, qt.Commentf("should wrap provided error")) qt.Assert(t, ve.Truncated, qt.IsFalse, qt.Commentf("verifier log should not be marked as truncated")) ve = ErrorWithLog("frob", unix.EINVAL, nil, true) qt.Assert(t, ve, qt.ErrorIs, unix.EINVAL, qt.Commentf("should wrap provided error")) qt.Assert(t, ve.Truncated, qt.IsTrue, qt.Commentf("verifier log should be marked as truncated")) ve = ErrorWithLog("frob", unix.EINVAL, []byte("foo"), false) qt.Assert(t, ve, qt.ErrorIs, unix.EINVAL, qt.Commentf("should wrap provided error")) qt.Assert(t, ve.Error(), qt.Contains, "foo", qt.Commentf("verifier log should appear in error string")) ve = ErrorWithLog("frob", unix.ENOSPC, []byte("foo"), true) qt.Assert(t, ve, qt.ErrorIs, unix.ENOSPC, qt.Commentf("should wrap provided error")) qt.Assert(t, ve.Error(), qt.Contains, "foo", qt.Commentf("verifier log should appear in error string")) qt.Assert(t, ve.Truncated, qt.IsTrue, qt.Commentf("verifier log should be marked truncated")) } func TestVerifierErrorSummary(t *testing.T) { // Suppress the last line containing 'processed ... insns'. errno524 := readErrorFromFile(t, "testdata/errno524.log") qt.Assert(t, errno524.Error(), qt.Contains, "JIT doesn't support bpf-to-bpf calls") qt.Assert(t, errno524.Error(), qt.Not(qt.Contains), "processed 39 insns") // Include the previous line if the current one starts with a tab. invalidMember := readErrorFromFile(t, "testdata/invalid-member.log") qt.Assert(t, invalidMember.Error(), qt.Contains, "STRUCT task_struct size=7744 vlen=218: cpus_mask type_id=109 bitfield_size=0 bits_offset=7744 Invalid member") // Only include the last line. issue43 := readErrorFromFile(t, "testdata/issue-43.log") qt.Assert(t, issue43.Error(), qt.Contains, "[11] FUNC helper_func2 type_id=10 vlen != 0") qt.Assert(t, issue43.Error(), qt.Not(qt.Contains), "[10] FUNC_PROTO (anon) return=3 args=(3 arg)") // Include instruction that caused invalid register access. invalidR0 := readErrorFromFile(t, "testdata/invalid-R0.log") qt.Assert(t, invalidR0.Error(), qt.Contains, "0: (95) exit: R0 !read_ok") // Include symbol that doesn't match context type. invalidCtx := readErrorFromFile(t, "testdata/invalid-ctx-access.log") qt.Assert(t, invalidCtx.Error(), qt.Contains, "func '__x64_sys_recvfrom' arg0 type FWD is not a struct: invalid bpf_context access off=0 size=8") } func readErrorFromFile(tb testing.TB, file string) *VerifierError { tb.Helper() contents, err := os.ReadFile(file) if err != nil { tb.Fatal("Read file:", err) } return ErrorWithLog("file", unix.EINVAL, contents, false) } golang-github-cilium-ebpf-0.11.0/internal/feature.go000066400000000000000000000104201456504511400223120ustar00rootroot00000000000000package internal import ( "errors" "fmt" "sync" ) // ErrNotSupported indicates that a feature is not supported by the current kernel. var ErrNotSupported = errors.New("not supported") // UnsupportedFeatureError is returned by FeatureTest() functions. type UnsupportedFeatureError struct { // The minimum Linux mainline version required for this feature. // Used for the error string, and for sanity checking during testing. MinimumVersion Version // The name of the feature that isn't supported. Name string } func (ufe *UnsupportedFeatureError) Error() string { if ufe.MinimumVersion.Unspecified() { return fmt.Sprintf("%s not supported", ufe.Name) } return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion) } // Is indicates that UnsupportedFeatureError is ErrNotSupported. func (ufe *UnsupportedFeatureError) Is(target error) bool { return target == ErrNotSupported } // FeatureTest caches the result of a [FeatureTestFn]. // // Fields should not be modified after creation. type FeatureTest struct { // The name of the feature being detected. Name string // Version in in the form Major.Minor[.Patch]. Version string // The feature test itself. Fn FeatureTestFn mu sync.RWMutex done bool result error } // FeatureTestFn is used to determine whether the kernel supports // a certain feature. // // The return values have the following semantics: // // err == ErrNotSupported: the feature is not available // err == nil: the feature is available // err != nil: the test couldn't be executed type FeatureTestFn func() error // NewFeatureTest is a convenient way to create a single [FeatureTest]. func NewFeatureTest(name, version string, fn FeatureTestFn) func() error { ft := &FeatureTest{ Name: name, Version: version, Fn: fn, } return ft.execute } // execute the feature test. // // The result is cached if the test is conclusive. // // See [FeatureTestFn] for the meaning of the returned error. func (ft *FeatureTest) execute() error { ft.mu.RLock() result, done := ft.result, ft.done ft.mu.RUnlock() if done { return result } ft.mu.Lock() defer ft.mu.Unlock() // The test may have been executed by another caller while we were // waiting to acquire ft.mu. if ft.done { return ft.result } err := ft.Fn() if err == nil { ft.done = true return nil } if errors.Is(err, ErrNotSupported) { var v Version if ft.Version != "" { v, err = NewVersion(ft.Version) if err != nil { return fmt.Errorf("feature %s: %w", ft.Name, err) } } ft.done = true ft.result = &UnsupportedFeatureError{ MinimumVersion: v, Name: ft.Name, } return ft.result } // We couldn't execute the feature test to a point // where it could make a determination. // Don't cache the result, just return it. return fmt.Errorf("detect support for %s: %w", ft.Name, err) } // FeatureMatrix groups multiple related feature tests into a map. // // Useful when there is a small number of discrete features which are known // at compile time. // // It must not be modified concurrently with calling [FeatureMatrix.Result]. type FeatureMatrix[K comparable] map[K]*FeatureTest // Result returns the outcome of the feature test for the given key. // // It's safe to call this function concurrently. func (fm FeatureMatrix[K]) Result(key K) error { ft, ok := fm[key] if !ok { return fmt.Errorf("no feature probe for %v", key) } return ft.execute() } // FeatureCache caches a potentially unlimited number of feature probes. // // Useful when there is a high cardinality for a feature test. type FeatureCache[K comparable] struct { mu sync.RWMutex newTest func(K) *FeatureTest features map[K]*FeatureTest } func NewFeatureCache[K comparable](newTest func(K) *FeatureTest) *FeatureCache[K] { return &FeatureCache[K]{ newTest: newTest, features: make(map[K]*FeatureTest), } } func (fc *FeatureCache[K]) Result(key K) error { // NB: Executing the feature test happens without fc.mu taken. return fc.retrieve(key).execute() } func (fc *FeatureCache[K]) retrieve(key K) *FeatureTest { fc.mu.RLock() ft := fc.features[key] fc.mu.RUnlock() if ft != nil { return ft } fc.mu.Lock() defer fc.mu.Unlock() if ft := fc.features[key]; ft != nil { return ft } ft = fc.newTest(key) fc.features[key] = ft return ft } golang-github-cilium-ebpf-0.11.0/internal/feature_test.go000066400000000000000000000023711456504511400233570ustar00rootroot00000000000000package internal import ( "errors" "strings" "testing" "github.com/cilium/ebpf/internal/testutils/fdtrace" ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestFeatureTest(t *testing.T) { var called bool fn := NewFeatureTest("foo", "1.0", func() error { called = true return nil }) if called { t.Error("Function was called too early") } err := fn() if !called { t.Error("Function wasn't called") } if err != nil { t.Error("Unexpected negative result:", err) } fn = NewFeatureTest("bar", "2.1.1", func() error { return ErrNotSupported }) err = fn() if err == nil { t.Fatal("Unexpected positive result") } fte, ok := err.(*UnsupportedFeatureError) if !ok { t.Fatal("Result is not a *UnsupportedFeatureError") } if !strings.Contains(fte.Error(), "2.1.1") { t.Error("UnsupportedFeatureError.Error doesn't contain version") } if !errors.Is(err, ErrNotSupported) { t.Error("UnsupportedFeatureError is not ErrNotSupported") } err2 := fn() if err != err2 { t.Error("Didn't cache an error wrapping ErrNotSupported") } fn = NewFeatureTest("bar", "2.1.1", func() error { return errors.New("foo") }) err1, err2 := fn(), fn() if err1 == err2 { t.Error("Cached result of unsuccessful execution") } } golang-github-cilium-ebpf-0.11.0/internal/io.go000066400000000000000000000064271456504511400213020ustar00rootroot00000000000000package internal import ( "bufio" "bytes" "compress/gzip" "errors" "fmt" "io" "os" "path/filepath" "sync" ) // NewBufferedSectionReader wraps an io.ReaderAt in an appropriately-sized // buffered reader. It is a convenience function for reading subsections of // ELF sections while minimizing the amount of read() syscalls made. // // Syscall overhead is non-negligible in continuous integration context // where ELFs might be accessed over virtual filesystems with poor random // access performance. Buffering reads makes sense because (sub)sections // end up being read completely anyway. // // Use instead of the r.Seek() + io.LimitReader() pattern. func NewBufferedSectionReader(ra io.ReaderAt, off, n int64) *bufio.Reader { // Clamp the size of the buffer to one page to avoid slurping large parts // of a file into memory. bufio.NewReader uses a hardcoded default buffer // of 4096. Allow arches with larger pages to allocate more, but don't // allocate a fixed 4k buffer if we only need to read a small segment. buf := n if ps := int64(os.Getpagesize()); n > ps { buf = ps } return bufio.NewReaderSize(io.NewSectionReader(ra, off, n), int(buf)) } // DiscardZeroes makes sure that all written bytes are zero // before discarding them. type DiscardZeroes struct{} func (DiscardZeroes) Write(p []byte) (int, error) { for _, b := range p { if b != 0 { return 0, errors.New("encountered non-zero byte") } } return len(p), nil } // ReadAllCompressed decompresses a gzipped file into memory. func ReadAllCompressed(file string) ([]byte, error) { fh, err := os.Open(file) if err != nil { return nil, err } defer fh.Close() gz, err := gzip.NewReader(fh) if err != nil { return nil, err } defer gz.Close() return io.ReadAll(gz) } // ReadUint64FromFile reads a uint64 from a file. // // format specifies the contents of the file in fmt.Scanf syntax. func ReadUint64FromFile(format string, path ...string) (uint64, error) { filename := filepath.Join(path...) data, err := os.ReadFile(filename) if err != nil { return 0, fmt.Errorf("reading file %q: %w", filename, err) } var value uint64 n, err := fmt.Fscanf(bytes.NewReader(data), format, &value) if err != nil { return 0, fmt.Errorf("parsing file %q: %w", filename, err) } if n != 1 { return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n) } return value, nil } type uint64FromFileKey struct { format, path string } var uint64FromFileCache = struct { sync.RWMutex values map[uint64FromFileKey]uint64 }{ values: map[uint64FromFileKey]uint64{}, } // ReadUint64FromFileOnce is like readUint64FromFile but memoizes the result. func ReadUint64FromFileOnce(format string, path ...string) (uint64, error) { filename := filepath.Join(path...) key := uint64FromFileKey{format, filename} uint64FromFileCache.RLock() if value, ok := uint64FromFileCache.values[key]; ok { uint64FromFileCache.RUnlock() return value, nil } uint64FromFileCache.RUnlock() value, err := ReadUint64FromFile(format, filename) if err != nil { return 0, err } uint64FromFileCache.Lock() defer uint64FromFileCache.Unlock() if value, ok := uint64FromFileCache.values[key]; ok { // Someone else got here before us, use what is cached. return value, nil } uint64FromFileCache.values[key] = value return value, nil } golang-github-cilium-ebpf-0.11.0/internal/io_test.go000066400000000000000000000006011456504511400223250ustar00rootroot00000000000000package internal import ( "bytes" "io" "testing" ) func TestDiscardZero(t *testing.T) { _, err := io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{0, 0, 0})) if err != nil { t.Error("Returned an error even though input was zero:", err) } _, err = io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{1})) if err == nil { t.Error("No error even though input is non-zero") } } golang-github-cilium-ebpf-0.11.0/internal/kconfig/000077500000000000000000000000001456504511400217535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/kconfig/kconfig.go000066400000000000000000000152471456504511400237330ustar00rootroot00000000000000package kconfig import ( "bufio" "bytes" "compress/gzip" "fmt" "io" "math" "os" "strconv" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" ) // Find find a kconfig file on the host. // It first reads from /boot/config- of the current running kernel and tries // /proc/config.gz if nothing was found in /boot. // If none of the file provide a kconfig, it returns an error. func Find() (*os.File, error) { kernelRelease, err := internal.KernelRelease() if err != nil { return nil, fmt.Errorf("cannot get kernel release: %w", err) } path := "/boot/config-" + kernelRelease f, err := os.Open(path) if err == nil { return f, nil } f, err = os.Open("/proc/config.gz") if err == nil { return f, nil } return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path) } // Parse parses the kconfig file for which a reader is given. // All the CONFIG_* which are in filter and which are set set will be // put in the returned map as key with their corresponding value as map value. // If filter is nil, no filtering will occur. // If the kconfig file is not valid, error will be returned. func Parse(source io.ReaderAt, filter map[string]struct{}) (map[string]string, error) { var r io.Reader zr, err := gzip.NewReader(io.NewSectionReader(source, 0, math.MaxInt64)) if err != nil { r = io.NewSectionReader(source, 0, math.MaxInt64) } else { // Source is gzip compressed, transparently decompress. r = zr } ret := make(map[string]string, len(filter)) s := bufio.NewScanner(r) for s.Scan() { line := s.Bytes() err = processKconfigLine(line, ret, filter) if err != nil { return nil, fmt.Errorf("cannot parse line: %w", err) } if filter != nil && len(ret) == len(filter) { break } } if err := s.Err(); err != nil { return nil, fmt.Errorf("cannot parse: %w", err) } if zr != nil { return ret, zr.Close() } return ret, nil } // Golang translation of libbpf bpf_object__process_kconfig_line(): // https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/libbpf.c#L1874 // It does the same checks but does not put the data inside the BPF map. func processKconfigLine(line []byte, m map[string]string, filter map[string]struct{}) error { // Ignore empty lines and "# CONFIG_* is not set". if !bytes.HasPrefix(line, []byte("CONFIG_")) { return nil } key, value, found := bytes.Cut(line, []byte{'='}) if !found { return fmt.Errorf("line %q does not contain separator '='", line) } if len(value) == 0 { return fmt.Errorf("line %q has no value", line) } if filter != nil { // NB: map[string(key)] gets special optimisation help from the compiler // and doesn't allocate. Don't turn this into a variable. _, ok := filter[string(key)] if !ok { return nil } } // This can seem odd, but libbpf only sets the value the first time the key is // met: // https://github.com/torvalds/linux/blob/0d85b27b0cc6/tools/lib/bpf/libbpf.c#L1906-L1908 _, ok := m[string(key)] if !ok { m[string(key)] = string(value) } return nil } // PutValue translates the value given as parameter depending on the BTF // type, the translated value is then written to the byte array. func PutValue(data []byte, typ btf.Type, value string) error { typ = btf.UnderlyingType(typ) switch value { case "y", "n", "m": return putValueTri(data, typ, value) default: if strings.HasPrefix(value, `"`) { return putValueString(data, typ, value) } return putValueNumber(data, typ, value) } } // Golang translation of libbpf_tristate enum: // https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/bpf_helpers.h#L169 type triState int const ( TriNo triState = 0 TriYes triState = 1 TriModule triState = 2 ) func putValueTri(data []byte, typ btf.Type, value string) error { switch v := typ.(type) { case *btf.Int: if v.Encoding != btf.Bool { return fmt.Errorf("cannot add tri value, expected btf.Bool, got: %v", v.Encoding) } if v.Size != 1 { return fmt.Errorf("cannot add tri value, expected size of 1 byte, got: %d", v.Size) } switch value { case "y": data[0] = 1 case "n": data[0] = 0 default: return fmt.Errorf("cannot use %q for btf.Bool", value) } case *btf.Enum: if v.Name != "libbpf_tristate" { return fmt.Errorf("cannot use enum %q, only libbpf_tristate is supported", v.Name) } var tri triState switch value { case "y": tri = TriYes case "m": tri = TriModule case "n": tri = TriNo default: return fmt.Errorf("value %q is not support for libbpf_tristate", value) } internal.NativeEndian.PutUint64(data, uint64(tri)) default: return fmt.Errorf("cannot add number value, expected btf.Int or btf.Enum, got: %T", v) } return nil } func putValueString(data []byte, typ btf.Type, value string) error { array, ok := typ.(*btf.Array) if !ok { return fmt.Errorf("cannot add string value, expected btf.Array, got %T", array) } contentType, ok := btf.UnderlyingType(array.Type).(*btf.Int) if !ok { return fmt.Errorf("cannot add string value, expected array of btf.Int, got %T", contentType) } // Any Int, which is not bool, of one byte could be used to store char: // https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638 if contentType.Size != 1 && contentType.Encoding != btf.Bool { return fmt.Errorf("cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v", contentType.Size) } if !strings.HasPrefix(value, `"`) || !strings.HasSuffix(value, `"`) { return fmt.Errorf(`value %q must start and finish with '"'`, value) } str := strings.Trim(value, `"`) // We need to trim string if the bpf array is smaller. if uint32(len(str)) >= array.Nelems { str = str[:array.Nelems] } // Write the string content to .kconfig. copy(data, str) return nil } func putValueNumber(data []byte, typ btf.Type, value string) error { integer, ok := typ.(*btf.Int) if !ok { return fmt.Errorf("cannot add number value, expected *btf.Int, got: %T", integer) } size := integer.Size sizeInBits := size * 8 var n uint64 var err error if integer.Encoding == btf.Signed { parsed, e := strconv.ParseInt(value, 0, int(sizeInBits)) n = uint64(parsed) err = e } else { parsed, e := strconv.ParseUint(value, 0, int(sizeInBits)) n = uint64(parsed) err = e } if err != nil { return fmt.Errorf("cannot parse value: %w", err) } switch size { case 1: data[0] = byte(n) case 2: internal.NativeEndian.PutUint16(data, uint16(n)) case 4: internal.NativeEndian.PutUint32(data, uint32(n)) case 8: internal.NativeEndian.PutUint64(data, uint64(n)) default: return fmt.Errorf("size (%d) is not valid, expected: 1, 2, 4 or 8", size) } return nil } golang-github-cilium-ebpf-0.11.0/internal/kconfig/kconfig_test.go000066400000000000000000000166571456504511400250000ustar00rootroot00000000000000package kconfig import ( "bytes" "encoding/binary" "os" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" qt "github.com/frankban/quicktest" ) func BenchmarkParse(b *testing.B) { f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { b.Fatal(err) } defer f.Close() b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { _, err := Parse(f, nil) if err != nil { b.Fatal(err) } } } func BenchmarkParseFiltered(b *testing.B) { f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { b.Fatal(err) } defer f.Close() b.ReportAllocs() b.ResetTimer() // CONFIG_ARCH_USE_MEMTEST is the last CONFIG_ in the file. // So, we will easily be able to see how many allocated bytes the filtering // permits reducing compared to unfiltered benchmark. filter := map[string]struct{}{"CONFIG_ARCH_USE_MEMTEST": {}} for n := 0; n < b.N; n++ { _, err := Parse(f, filter) if err != nil { b.Fatal(err) } } } func TestParse(t *testing.T) { t.Parallel() f, err := os.Open("testdata/test.kconfig") if err != nil { t.Fatal("Error reading /testdata/test.kconfig: ", err) } defer f.Close() config, err := Parse(f, nil) if err != nil { t.Fatal("Error parsing kconfig: ", err) } expected := map[string]string{ "CONFIG_TRISTATE": "m", "CONFIG_BOOL": "y", "CONFIG_CHAR": "100", "CONFIG_USHORT": "30000", "CONFIG_INT": "123456", "CONFIG_ULONG": "0xDEADBEEFC0DE", "CONFIG_STR": `"abracad"`, "CONFIG_FOO": `"foo"`, } qt.Assert(t, config, qt.DeepEquals, expected) } func TestParseFiltered(t *testing.T) { t.Parallel() f, err := os.Open("testdata/test.kconfig") if err != nil { t.Fatal("Error reading /testdata/test.kconfig: ", err) } defer f.Close() filter := map[string]struct{}{"CONFIG_FOO": {}} config, err := Parse(f, filter) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } expected := map[string]string{"CONFIG_FOO": `"foo"`} qt.Assert(t, config, qt.DeepEquals, expected) } func TestParseGzipped(t *testing.T) { t.Parallel() f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) } defer f.Close() _, err = Parse(f, nil) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } } func TestParseGzippedFiltered(t *testing.T) { t.Parallel() f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) } defer f.Close() filter := map[string]struct{}{"CONFIG_HZ": {}} config, err := Parse(f, filter) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } expected := map[string]string{"CONFIG_HZ": "1000"} qt.Assert(t, config, qt.DeepEquals, expected) } func TestProcessKconfigBadLine(t *testing.T) { t.Parallel() m := make(map[string]string) err := processKconfigLine([]byte("CONFIG_FOO"), m, nil) qt.Assert(t, err, qt.IsNotNil, qt.Commentf("line has no '='")) err = processKconfigLine([]byte("CONFIG_FOO="), m, nil) qt.Assert(t, err, qt.IsNotNil, qt.Commentf("line has no value")) } func TestPutValue(t *testing.T) { t.Parallel() type testCase struct { typ btf.Type value string expected any comment string } cases := []testCase{ { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "n", expected: int8(0), }, { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "y", expected: int8(1), }, { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "foo", comment: "Bad value", }, { typ: &btf.Int{}, comment: "Encoding is not Bool", }, { typ: &btf.Int{ Encoding: btf.Bool, }, comment: "Size is not 1", }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "y", expected: int64(TriYes), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "n", expected: int64(TriNo), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "m", expected: int64(TriModule), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "foo", comment: "Bad value", }, { typ: &btf.Enum{ Name: "error", }, comment: "Enum name is wrong", }, { typ: &btf.Array{}, value: "y", comment: "Type is not btf.Int", }, { typ: &btf.Int{ Size: 1, }, value: "255", expected: uint8(255), }, { typ: &btf.Int{ Size: 2, }, value: "0xcafe", expected: uint16(0xcafe), }, { typ: &btf.Int{ Size: 2, }, value: "0755", expected: uint16(0755), }, { typ: &btf.Int{ Size: 4, Encoding: btf.Signed, }, value: "-2147483648", expected: int32(-2147483648), }, { typ: &btf.Int{ Size: 4, Encoding: btf.Signed, }, value: "+2147483647", expected: int32(+2147483647), }, { typ: &btf.Int{ Size: 4, }, value: "0xcafec0de", expected: uint32(0xcafec0de), }, { typ: &btf.Int{ Size: 8, Encoding: btf.Signed, }, value: "+1000000000000", expected: int64(1000000000000), }, { typ: &btf.Int{ Size: 8, }, value: "1000000000000", expected: uint64(1000000000000), }, { typ: &btf.Int{ Size: 1, }, value: "foo", comment: "Value is not an int", }, { typ: &btf.Array{}, value: "1", comment: "Type is not btf.Int", }, { typ: &btf.Int{ Size: 16, }, value: "1", comment: "Size is wrong", }, { typ: &btf.Typedef{ Type: &btf.Int{ Size: 1, }, }, value: "1", expected: uint8(1), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Char, }, Nelems: 6, }, value: `"foobar"`, expected: []byte("foobar"), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Unsigned, }, Nelems: 3, }, value: `"foobar"`, expected: []byte("foo"), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Signed, }, Nelems: 2, }, value: `"42"`, expected: []byte("42"), }, { typ: &btf.Int{}, value: `"foo"`, comment: "Type is not btf.Array", }, { typ: &btf.Array{}, value: `"foo"`, comment: "Type is not btf.Array of btf.Int", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Bool, }, }, comment: "Type is not btf.Array of btf.Int of size 1 which is not btf.Bool", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 4, Encoding: btf.Char, }, }, value: `"foo"`, comment: "Type is not btf.Array of btf.Char of size 1", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Char, }, }, value: `"foo`, comment: `Value does not start and end with '"'`, }, } for _, c := range cases { if len(c.comment) > 0 { err := PutValue(make([]byte, 0), c.typ, c.value) qt.Assert(t, err, qt.IsNotNil, qt.Commentf(c.comment)) continue } var buf bytes.Buffer err := binary.Write(&buf, internal.NativeEndian, c.expected) if err != nil { t.Fatal(err) } expected := buf.Bytes() data := make([]byte, len(expected)) err = PutValue(data, c.typ, c.value) qt.Assert(t, err, qt.IsNil) qt.Assert(t, data, qt.DeepEquals, expected) } } golang-github-cilium-ebpf-0.11.0/internal/kconfig/testdata/000077500000000000000000000000001456504511400235645ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/kconfig/testdata/config-6.2.15-300.fc38.x86_64.gz000066400000000000000000001673521456504511400301370ustar00rootroot00000000000000%wodconfig-6.2.15-300.fc38.x86_64^=wUՊfL}?~|>gWgN>l>?]?|>oUp tvp˯YT塚7Zѽ:z[6667({^*7z3W)Fzih d'\UIM:3ݔ4bvjb+a2DֺkީSxhk`:C5\笢:o4QJL$-)9-=4`C_p,%X)VSH$3~~~zk:ch椫3<\rRw?'#rZ7UJ.kd{]юơ.JKHR״{^K"A:M*gj,˿~>=+[І 2%q&d/4U '纶B"kiHx'5+HRQ4k.$"B *a M2^`dTՒ4C naD `j88Qu3s&vȖIIѤ+r_÷'ӭi4Έ\s~H2T:=U}~xt@6Jԉ$K+cd50ɾtQKݵ82٫FИn;ԃFOݍ$z'b46[nO vVtV Ay6ed=_=?^oOcB@R3 *G({|YoDDC;; e"3T)ڪeޝ:$(Ttdt[ɼC-9d$YT'o;l%LsZ"V2"5`D᧦{1^;jJ5uQ4&` !i&s4 :ÁΣ*+ryS#Dm&'Rq@<,W.L'6iGHÚ;5oG2pvz~y<>YЋӴ"uk+-֞]ۂO {W4w&%6uMM`*yr*7'=0ۊ1r8[!yW_u{s}/<Ήl6y :^ X>2$mpF.` tc2?1 !,{ z|2‰o4O)Zmi MHa ;MМ044˔pDޏ2Etm^vr- @}cYݱ@aT3g4LĄ5㑆('Q@):^C.1X] gn[mWe- 8e*X(+p l P_ g`<3:Л5Ȃh@ ؔGI8GS v5L35YS%E5qS@&HoUhKҀ\9v:߫{7ŀa 1 X )5Bw4]ŢSheS6 4sIJ jj 2zݰ#mi@>[ЎHy\un~=toV4rf. \,=U@`vَI:Jڒ;EC\; Q [nMl%J zi& Nh֮ >;Ee^o=te,>=<]_W?{pX)Ov8)#,^jmsDn(ZY&b<.1ڨ:~d >{$2]3Xo5P`Xd󂟆jREJ٠SAQl:7Ƞ,*\6 <QyBo/u"X"0*zi0**YRљ-1rCcWQi )!h+hn˃{PQSHPg01>>\W?a!X]+DuZ{GOHz[M"r&ˈ0@c ts5htuҒyvw]HG`OfB_2RO\׉`8:S60Snu@~( ks+/5:Ȗ5&Nk]?R:{7?tڜIH0e^rWG@@UuEfk)LűۼQan{|i<`H1DklUrTfli y0^0f1vXo?GoXxFk)0ȔF}Zk FSk$fd rۧeAW.apUm$'Fj1/c=9X )~seI-%5Pe' gt`wDemhAhlyc;#e1⛝Z:CЛɚR=W] A~a>@WV a g{31.K^en;/8F/C8KwkS2 =/~. +tes6s@l ȐYh+u!Oꄰ: a.,A_,X"8'`H0,.-!'HTZWQq1 qBpclқ$mـxQYGɔcΓD='w{v.bjiLy mJ'MSkM:zxu箿X13ktb\K.1"!Y(,MXA :AL1/0u@ƞ[/ 30$xi(C0.HFS1[ erob3Q4oC#BѧqU 9~5qP=ǵ|tԓVɖ$!@_ju6ut::[y9F}yU2k_ iXq qXdۉa+Gb1H.Yy?s;uD:\S˶0Hn0?6ڗ(_ 4CEܭƳ˘idNpj1B6LiۤM/ 0-GkS;q}CN?;@h@j"H}]a]Fg)Sj|AW 8ZJ :ٯAʛ/].?Ԭ(Un@ԛ]qRMJ$|,J*cp8 Jf[nPK 拻FHdrJW0ǜBvf*8(,wjsѩ`6 y-:B³/hMEMװ{z+}Zo`3|4#IYKc݊ZQ1m1}F@_bZJ3 m.Pf)~c0dlӟWǯs;J'¾;K8jbjt](j?Ͽ!EF5emgվ{y@~wRj`L[ ZkWqaI0_Y;J/ld"I'lI_=\ѻ۫9LMxy$IX@l}5+I0Wapʛlfddf?ʤƭub(o!u3*B4+E`]yjS:{i%@<[~<cqޘ6S>ix~&Ԉo{x?|*ZtXܫ@2 ܹCD]%lNTy؜ _HeDEڜx YnX;tBUK:I/\=^y|Ɛǿ$iaʢM٪K=Ad𾼪y6$(!m،ɡEkCB}Ȝ* + P5+ϦLjS酏\RGHNq7'2p*|csGW]$6 lMll) 0L7B}&Ϟn 37 gR4gaR/v_SS2wZޤRo@7.}C5ǻeQp-h=S#ȭX e/dg/snh NE\Y_al())sd Ϻz$u+ qJ~ИI~ݵ.z 50Ƞ1/8 D&޴cF"> 4RJe:UQ j 22C!)TfDg!Ȓ4zL s][ݮ28l,JJ~k\]d"ie A p25I)xWDvb:]xDLiS9(lN1kAU`eJIj J}hPtln-8Ifr5in28e Q= KqJH茣<+qM9.]cm_!M*嚉%̋No]*>ijJR^ y31I{el5r>YpJ!dry8]nߟ|x2n?>DQ`҈lQn4`^( (GA^#,KUOD-CxWگ#BYn&{E:hYwn쾥"-9 ⶚+z6{d꠸v4tRĊ|/y-CuisaM}MFo^ <,1y=ɿ u'VK_˷?~b'SLU(GI] G V2xmӉzIf4;i#(,[w'6 67M]w-2Z+x$;UrHja]đ4x aBU}$YG`?^H!cb7:AzJWBGX&I-TM9P+4Y51!`9YHyC%"<)DBoN^J:9bR0!Ηm\1g 0Gx$01g0V0MY DST-D򺜙^w(:ۥ6֕ CPh.4e0B v7{N鐰AEbdJYۙiʵAMTDK -cV5):%3>p$,uʂ i$fL3ݢ)#Auʂx &ܵ̎y$0fTb kk "&Q0@bd@G$EL_"ko[U%Dt*s!_c  )[deEO>&bGAtHQױ$c?I/H6Ϛ[kXLC({!IPQW`/OG֚[{:nq또[{FBU V b0RIrkZ$Yk#LQ}SHtoD 5T ۫^U9q緡TofŌRc]9*,b@6^k{=R* Q_E.` 08཭nA ȞE&OORyE'ECiI1A*5,g9`}}?__},9Sǃ:),v`7y8HP{*6ʻs6Z2eܲnW%r3 R ґQOɜzc${ TbxR\D; "L69jTUX>4_$BdY:7Y[;($,t1(;xY*TκB|V[mn/Lc~ (Cvfv-`fk@(B.k UWz |*P^iCv-Y p]Y"&~g9M4\eh `tFda0sPՆfEeTPR ɛ WB-=tMRepjFs%itthf6)tR篠MV|I2OHcoϯjd.G#(.s-Bwe;UOx\/s;(l nhiifX ^%D̼ev e 5ʔ &n56Ӹi7[a00A7 ]q*8S-υuOR@?/0Qt>^d,T4RZ)K:2ykSLKz/t)/2])4.|9=M=.,ef\GK˥iylO,wywƘ~6K\e:O,|NХ4rzX"xݵm]bIü[{azaXp4,?%-, -1\y\Jxnϣl 4b s' 8g@p<ô`D-]hLE,DX( ?TpN4`Am$!}%討RPX`ʘgi1w8ݘ+S#dzWMx31VEanl 0A_\:1W%ꖜ[sw5y٣ ۮ4X$pN? @ɭ;G2p8p.;+[bl]hK,#ۻQ-EKȹ0s=2|3>̡=^/mJZ""U_}@R@ HSSÙ ޒqQiG#Xgo[kf4i?aP&^=WjZKي~XHE}ffI'^\:\TKQMjJ@l]n(:QݠBD$P~f}*l ź Yp'q2@\-!0T>+e5v6(T2{p ~6ZzJRBM#Z {; !U*ÊJ 1`qV`;@ĎrTD Cx)jbTbpS/88&ԋIR=:)Eƙ UB#;*Ҵwɛwׇ|v1&*࿅D!uܸ]wf 3"9ZO\YN_}UbIR?[ aHOb&R5ۼպ0$٧) rCzjTRTˑLS1$Nq)%*۹q0Jo1tٙkƳgWFXdaZU薨X'l46< O3W,=pyW%a/<_ ŦUKQÍ HB,!)i렴vH3A&NsR6 âΉh곩ʘCfMUFU:6/EFu2[JHQZiJFհbKJ6^VP"[YUǁ`oՃh/o-X$I {x_؃C鏔^uA3Q) 09eL`E<_#iY9.44DJ kgo:ڡAҤezՒ]G[s 1B&8] ZČO߼O'E\фif^36$%bIkaL]ʗ鋞x@dbЍ*ND5c6z'$' \>vP˲/0ؤYjHLMe-ݏ"j:e0_CQORi|k)c 2@F:}4,T&l 8:jq+nx22Ǐ7Lф=4>~Y4P;H J۔p0 /Ce?/vts[9Տ)W'1c{p%\|{ iӈї6y7r ߼Cpz_5(} DbRP"o"m8VԄ% 8|kmב*`:V6sF ={4į<&[E}+cX\Tq_umvN\OU5Cgt)NNnb+ǂi2s8+C(4(4d:G#O8TZS@B`1h@~6WiCBҼ7Jo3ǑI/Mq61HcD0`{@p4]DMڱ{' 6%Y4vavbu3?}dYo lO:5l#m ɜN\ן>SOca&K<9>k~5mfHBMڨ)lrfm{YY_Z =š6 )ׂ5ÝyؠiՐQU`Ny<& s!ϑ~Bi#[P861X;@Dl `U ?%y<؞_/><_?˨?F7dM;V!ζHE:3]Ev_?<dc+æBW'le OpYz'۝=:걊š%XIE`RCe/Yo.h P ʛ5r? }!'83=4R Jq̔fQOх]`l݊6ɏ"֍QF0)JҹcD.j%Q" /!Zb0dg\)9Bypij.&i1$968Ba *(UM4q@P@2)#'>l'*ft#,&_/9D ѺXPP[L=&nb%䳋JH^^OaS'TH ?:#878aWo 谥Jd ZhJqA!VGǬxqkꍼ)oo/tgQX}y{y3-+._ֳy?Q% .).VhॻKD17w\4[.#,źX0XvJV C9TS`XU<:TE#˗o!xO񟚤6NyN]FV.Wꈈ,CYQM_LچEV_tވЕeCPi+)ۦsa!Fbw;5D޽L$mGE1\fKWN3Z2X)B{/=I;}nvWq5DEpmئ=6`Mydq(rAqj_x}~ǘ:K苎r,uMLۗ`ўigMFS.Q E~y8D?_" )z}ˇ_^ CAW6w9;IięYB_™j]UAC'q0/&Rô _%Mv(c΂~K4vZj_1UTgexX妅O7) "w+g n%uu)_!46NTO.seK,sV( 0C~rrٺƑ`Xze.=xAՋl&,r%~mՆz_[*W|{kB?~՗c~1w`{Tj ƀ(u?Ţ4GF4El ϟ>|~{O-raBv;W8Wy8Jq,bD^vp3Ov]H.8ޣG5ڗ jK)H_(j//y?4ՁnjŠ.N ;KHa %荹814wh'_.>W5lpwL~m3qU?)7x~OF&yY Q֫¬Fk`TWV&=i"4\w-}1(D#@:̯:M R`F䪕ox&h_!:u|%4"|NDLPtYGS(8aB=Q^Mk~sO|c w>Ee+j\u`¨Ÿ$DK1jJ$aztgTP)NͅLb(BTnuqwnv33CGY'~Y.>(ru&I]Ѫɥ(Qb}ȑ[² 3YV#KBCW/bːYi]w{W~l.Yo6a~出;X鮒⯴.oe!\xV5NoOm^4(G0*'汨mE (>!'sҶLkX%y1%>CDԦbD,pct.Vta@5 pvnc D![w1,iTLSoZ6FC/y+(P׈֨bKQÖ ?{``27}{'8} ǿ>|/zaECרQ^)CZ;، uG3|Jk}<=1IIz6cC[9S\|AʜA.PL?QX}Hi^^;]#{RЗu 5:}K]GƜZ,/ dV6Y"_M̠,HS"ND{6Yájozy1ޞzq򳘸UNa7і#u7iuhn2xD.$*wvߌ$6ϒ$) YC:eҞ'EjꩄhEl M$)H>jc(&&)QNB'E7Xǁ\'玑ѩbJHp:x?vׇ،WaaLx2xDŽr0{ɠ4mV+lRGdh2:&oENdk`!D:5 |. ,2DgҰTKtʕࢰ5W+2 xR7?SuxAx{u2XQU~?g^b K]5);xҕ)%׸YPm%C&#JCeEjoSsO&b}Psx{_=`݆Gݶ1.9k2:Z=p22zCtk [`(J{c_Qq qfaqp\8sߒw0Džr4vدVdC%h+aUd6?YӍsL7Ih/S ВvU.J@1)̳zy8i,[IH:N,(dzg"-ֹKiR ZX ,%EͦRnfS ϕ<–d%G:FBW@i.i묏)z)Qƈ. &r%7B iŦ0?z9B B4.(f~ Tu_|~v*s/He6 )n */)4*`6`#7 & IPi(+ u h'r04]ʷz &E~x$vuP|U`lPX%ښj aYMB3D"7VFլ@U_>}7g15]aYR}NZpB1WN[<> ikB+#a"9Qz =uHSYO"+ lfZo_hjj5~D1)~T'S6ZWhX&tX`7!+nKGe&6Fş]8_jol|:`z#~'6`zeb[!'#6بi>xaח?ǩ5)wB 0\>B(*9L01vq'_?jKԳ ط#8x '4xQV0˔"N 6էT)a֭~87nTⰯ;hy0gckq,:|T=Vҗ>ҙYBh%az>qIj,G"k|ot>۳DX1- 4fb(C/+୴Y.2Z;zr(`&MT5P M-,23&5 Sx; FZKl &5fNymӒ^._/zqb\-rޥy軦)vݫ4R5SRB2o1tpv&\JXD8֡ Ôhc8HA E&$@4Kŷ^O!{-D F[_]Ɗ0ӬVzmn[i%9f.fNCE4l%!t ^HdƸՉV:ǡbYig7| "rbܞ;g OyBSb2}DtgJ- Pt7q$?ެcxx"t'cǨ(LDI]KV}pXY\ag +KZˬSE: JOt>lGbI *x a9tk<(Z/>؃SWP?<׶2DW 2aLJ8B"HaGc‹2<,&ѝaoWkzҍ6^pm.w,%1nȶ>K/-M͈ՀD;}˵qM(x#4}\.3K*ȯeJHU.<ԯ9j]{ (FAՄ!0dR}7 f`#e"ZckE|b=|DZL!՚%WTs[ Ң|00QO@ `6ZG+w|GLi$v"<-]`W>Um^\mzqE j A==A?Sr}D\,xfzoN~t ;3!Tl8{^ACyBl72`kN6Ot eN4gI^vL y.FBƎ`'ZJo'y/h+BhveDX&RjVN{4g,wxC9W1ANKgd"_^Y4w7j#znJ,])E hրm,scyLWKw FlŘ0lѠ8ф&S 82DoDy1b|к>tF6 &џ4!g-Ԗگc<ڕD/H{ qsJTn@bpJI ml0m\}.Tvh{ JZH6*z QI~sThЈvaSs쮩eRa#LyK h@zVi!!/eY8UPtx[9ۋK(L/II@Vؼzfkn9_y/PZ36UL8(;cѓRj4AHwbe]8@fA'K!XD5]x^zxybz֒p+֫oN?y C‹8 mT~650ZSN@v{30 nʥd+4Z׆5\g40V3ki̎Q0(lcGf^{@ :cOҞ.eBzߟ>Z:N!Tݬ@܎-0m9[[]Ob&i&VZhumpI2#,v]Wa[ %kqU AҐO\)LN1Mp~H/c-J4BZـN!cʷt} n,(G7c5 :{}hS$$O[ PAO@("FP8o;|~({91tD;-r̷)MOdEwoʄ)xtKs ^iأlҰw cQukcbB_:5Jc.4Pu~ gkr#:QLn7]ʉW68[ 1;>Yq6M[oWħOha@l''y%̙=hP:3R=Y-咱O>3#ڽ9է@wqw852Wy# 8=G*m(mdTcX4D8fg&^J'h{fN:8UӁtV#gn9j8BWI(%52, %..< ޸Fgc&x] ]2l 7!F,^]ĖB_&'Tfh z3>}' 89xMF3\HZ>71TY"Z -SU4\,O&7xIpFls 4P+Π!uڱ-?i[Ѐ*K٭O6λ9YHoܪ6&߮F]AQoX Xo0⤡Pӿ(mZݍΈ:LhLrz&8'01~F'ω \9u XzC !h޷|O{bs]y="nkj1,|l/8vb6(v QtB3rZ!s66P(p#N%@w3˃Q9 &:cT V11q2K,cwcq"BMw^СM@ z&N :TLٸ(w_SCiw@jrTolܔ]@S66.幬nI3uƀ;./3ftD#/7Md6@Hյl\=,Q7; 7n7@܆AhCpOLh{_%0?KvXBpub#b胒Rִ^?RaF23@ }-P4(!83% Y_$z!+bIs >QR`D1<%ӓ(Wl8F;~Xh;Znt?J* g%)X`Q~+\O4`ݫ_ yHV.Yݷu'귙Va$N{~[̵zhۀM]"C57}p4R6fԖVg(؄O"JܤP1a? );Q3AsG$)l&fZ5McySl}:p?k 0jǍz2_R^}{=_/{y k0ispxET;=o=b BO?tLLQjb1Ɠv寿ߜa"YY_Q4$ev=O>%as\JJ (ʕ]Ex L)fe~>Ե6ڳ}[oN!-?+' 2g~5[j(%X:Mmnvp_{wz^͍;( {eu`sg22OZSi ˝jV*e%3ٙn-|u)'U΢kF&zP;V3"i7S,t>a^s  #yw]SDT5,{^ ehY.Ҩ ^RF@ř9]h*^RZJQJ?3SVpBZ>]Irɕ1)`)8~ 7 kKww]"O\*ުH!*38;2cWs4+MbeWimn]}AR.L/E4$1Pq'mA6x.sY'-lVQu(£t E¯TM"Ebk K @}H߀KU&̐pV^m |ŵs=s)'$U"ܯ Ŋݶ)\^GBvVW"N į1] os!4Dmm YI}tNx NR$٢voskOie)8.ܘ-_ݩ]:!^X9_+ugṉ /T-u& w~4LEn.eˊt'`D΀4jbTFS/G78eݕܰTDq0Fw4-3  P/~p9atn8q>/Ґ'euX%pgi ]<+87g".Րq2hX1n ~W #u*kC.``5 &n`Y6TlE*'XiwMB ъ>ɥ0mX&\Qu2dC:#epq&Uώ^fJr݇suL]"\&tuL-e運`>g Q/e׉J`G>u#4KڞuTVd{V [\P /On)=8H!J6kZ2We~-[VFg?"LW0Q+*2-*K djU2 ɹLt!)B6SGjU Q&rWϗ禪؁VҫRxOHZaCB)5:(~qImƚtPf9:75Ty-tP ϳS"[HXS j(04Hb`m,ÒqcP 5fW3I(I`^N-/ת5%7*@de;_Rp[OHdf" wv?™g1㼊YuQ <'8G٭=إzm+5g,|J(.:0<&  tSwWǿ(awHRFy:kY*Θߋ-X)ߵ|>7Ieb\fspwo ȓ0$ :cf>y,%yb Y O^>ˇ_őiN9K75sؼ]EANr>Mrw5+"vtE:e@8Mcs([l _ALVf*HOqo=c|aaUKu MUmvy6ck觖O lpZjt^F)@WR@R|V|‹>W{hb .Gԗ4p p)Y'ީ8qEVPKZz5HjӘ)hn io0y/PQp c̈́$C1Iߌ_Mp̟ౌ%̫qÌE x \sb^ټv})Dݧ?)2wy'㱳FGЧ1P;*ۋx9XƮOWtΌ։*ǢqDb~k YAVvၿ~O ?kU`\-0g<ݩ S ׎'}uKW+%L/_޾QH,-,tY#|C1Lk+hT?^>}}EۀyBNË4mjsq:LsufI XbɯmzU\;aS^ k@;:,s+YRݤ(l5@o8l:tK6 TPW;osO}xSRTS4OWlZ$6i$SͶI9SDMաaYV%(N&BnR9adiQGhӎ)ӱ8 v5Ќ:sTH۴r/ex,-;NUT1"O#Vo߈4vL3F,lvLH5D`ș_7s{,;kMwj92X005]@lӵ篯>?8&ryZ NHHW:rtx)5W3>Yih;ZP~q/ِx#nڝ@5pۓT{jO"es3XUemRӦ/&Kwv :_ܡM1`/ڪ ,v~4ڬI}ң6&[:zjq[ՌBCv֣j{b>LAmueepk=P߄i]Xڝāo጑8 \R#yq Cf⍛5`к54H4mHEP%OؤG>T KZ̐zC ȚNt'wυ8\hͧÚ7H7, ׇ*HNnxq^S,n!ڧz/]1Sd8\ts?B݈Fux}M"#,ŪZ<Hw4X8 bi֞1#Yܧ}0@&yI'X1@ϖR\+S+yoNB j4j3aF>9UldtgFp@a.NVPz|P#t b:X,BGU_ːG]n$֏|I_/:<9]#UjK%rLnE8,3Fx ,Uȓ[dqnTs9SRc)m}x~nS@vHNi3=:@qX.,U a+#1M" ;jAKñʓ¨lJ}Vݛ,u|wgص:>Jfm/&aLq9.ۉSȟNo7L:)k}r4] -ޚN|Oᅷ7.E88gۑj׽τvyB"AΕnCz>^cI}bL̆lxTc:gO*$6սyM YXx_"&(6~v֞@T@XD6V!-q+a@ved*ʧ Ee;jȕﺶ?79-}UݑE}7-)L9o'~G w[Sy_goڂԆBM7$W}DnQYVIj~WaD/y}izVe>06w}xUv!#NUUۧ%, QDF>Dnj3i0$o:$o>?ח_p*:WCIot &;CWC^e-H疄-x?Mc hYQL.Lޙ9Y'e:ݝ5)T'!:axhHYlt<ũ8~(TqFmb mUR9gc_sZhvMY/xcfi`V)g;v-,3.dSo*+oaN,FB:`xY8*[Αa8 _rj[9cvin/%zy-Y˒,ս\[ocȕ#bZ\eD9sdK06Ėi3 `_g_ &GZf0nW Yi'}t!'Lu28d0ywͽ^r,`ϵw^x ǝ#L]~n6'ʚ6_an5bHfb$fE]c; @CVYK F)~-~TNgoT2̇&Wz4%tq]k83_m $IjvU궼X%LivN̠5L~PQ0 31=pr*;[?zLbɎf.7m`r&@7my!4XXԿE}Sط*ZiFYlefK;I>)%}q\i]e]%o)U ]ZЂ%H`T0Bm3uu} |ˉy@Q)2[Le6zO38F- @F@B(}=|ï+,mr>uH+sBd>&mr3(Կ M6*h2UyoN~"%]T5t6ӴZf4=lC÷mS}ӷ,O*d{Ы,mJ}_8/[x 8ya 0-?~NQ*Vr7-~zPhZ_DW}rw \jߒ:ZemOk֚ƺQZOE>NH'%D[[-G)jSPӢ+]͘:Bkt<ǘ\"Zڐc Or:0x|w qw~Ik=Hn8zΖ.RCyV}p d=H49r!"% vdcg*#u!9jr0 H I 74ȄL]p!W3f0u/B^hAD& 傕3DT3CR6V> ,Q}rT`B;eOFif-.O-pDs.uE|-67I9u#$lz]UnA%Dy+``Z /&V6@ϫfM6YXx|" JNW9bwڟla6P]|#G$dt,9V 39g3WViR~Y}kꇦНHV="slNaOx8irPUړMSuʠ#W SsV^1h%wJftlxvFƭE09kS!Y0:ϡFn= ZLѿF;:oAw$&kTtꗽ6rl!4_d@k"a\RbQ,V<|.31)>V?1V45l;LA#@bd'N˸aM37).P#fr @E(fOߞtӰD|)#7Cyy[[-Ć",A͉Smr41Y'X45r3{  u| Dۢļ@<7>- ־LM݅J qdKCcC(RLș)X,/ǜu+ rhJg;8 D d7%E (]ŠlɜaBl1BVuȫaonM5*W3K4AtAJjmh!%UðЬ}+_~9|R'M'XGN2VIg4% "FbAd莨?uUy"7R1\@3};l,[ªS1.wE):Nv1`׿~N"(t7x%A)AovTIJ}|}=&iG/㓾ˌ蜵*/wk߾>$ۇ.;^kNKNN/QrZ2c_w]=h&|Xu٩ħ_{7k buO]UtjPy՛ g`4)gXrNYu!Z|r ~jY.^я6J@.<QryC;x=tno"0CMu)Olv-ѺLmhl `:lHlإσ 4x '/{ω!ԝ B0 AzB}0\*|F2E^AuXH Ydn)JJȑ L+|pD~M0G R`pzS`t6~I ʺGp^SFA1B AC7o6v#7C@c'tܭ)JJ}f pʱa??&ot}&:['I/? =Ք񧎑C~{N1ESs'Z+S Ԑ/@e35 F( mc )Mz35A4TUMk{:BbslҞ1⯛d`"‰o 'GIJ+ \%}ű^jJq7GQÕXLܥ7T-SQ .p-_Nf\ܿC,_wO 1hDW ss6^܉g3  ). eퟏLx|ܽ캂%{ikWC*v2`}'n7Tු5h^ !0@RNk뤜!)jE;caE4Ёjދc4k#ps0˜.ơƊ2^%`wxCTNRb~3ɻ4Y~O_<,2=ʗ=:u }"Yb >YT-WUOȯ [o Cza5?BˀjMM狽S+I>Do}D b+* qV >OȹOrqi,q1ݴGb)odA{c gf]n: x&8 Y7P9ڥ<(9xIŒ:zh䕭y^7E~lOvIՉ+%-d|#7fLb՚U ]6Ф&NDT҇UVWrVuƾ֮Ք9wEjI :xOE5_y5z4ӕ:VD0 ~#?>N^pMeUxggǘS"mW{<|<4Gֺ,>0W@o ݸ%6cgUL2`Q%Z6TT(9V.G1%hoslm)s1Q fpv^BP}rnJ<=>ÀȺZqIg"f/i6tMO7ĥ ӧirMaZ랻W/ raj 4SE%1/m'f%fsZM3;]V;, KVTm ոmU/ڍL7|=a*nhtq!BOiKi-DTvJ͉]heF;Y$ ]L!1&peq.T5AbqWo e_?nMݵx>`Ȗ&}J'z6]R|`Ă9'i6Fʪ^wZ+)%J6 /؏y> <>c/lTjxe)QW)c(@ D>S x῭هˠɒ[RQVu)nnǙeG67]$@m+jz97"Gaɓ5n*@2Cr3Dv`b-li5^xk}15E9CnENK%]bvPӍ6 8R.=ma5eZg:-XWE%Zzgz3f27ƀޠf8lw97sڠzpBkt,-UU-cLTT9$Ϗ;m6G3M(yfjdB]Ya^'+u$yKOOTW־dműPv}ꎑ>( yAxX\)b\qEPڬa 2D6+HU'cdmMG*c7{Ҫlj5WoƋ%buG:-)zaɊYڣ#zOYEXan4"g5;?q]iWR?^̅I;! ,[ǥIϑA%Zf"W?w`X:|Ί>˗ՆUpyr.踚e z͓L ,"^um{|(/&P)=ys,j+ESKLdDnh#\m_ZEUf5XujlE[)wb+:Y?vQ,v(%"mmMX*Z+ܹ^|oePG{ͭKjmsk;8?.uZ֦ґ8%Tm7h 4AmWc$$NRt64u+7G6NZo,3@抝x^%Sk4 %r=p:uIDcߕraUWLM=eJLUb؜OU@{E޷xb j4:춱 !H\o*j0U'4EsfEH9IkfuG-K8.Sџ2;SN 8S5.."`%jFTsLp5#,OQ(܄VUumr@pR*zuj*:tzn'Kb*7T5`paj{OU2d[_lzam67XB/?tP٩1kLl3(1KLSw~LO]e*d"F0i/yO˅/[*{btIzpwCmgh=5ḷkp XNdxU _*˨M-8ZN<ցh|HѐΤZ{#"7OTTD~"ͮ!ӸMǙMt-=هi`O)'>̌&B &V(R-nbEviJMϭg;hgl vJ4hHv61 Qj+Mw !§u3xdNm\(JfJ_Jǝe{msVmH5&d_hsNU38=(u+.  )uxv'Ln>'4hC&VKZ}J CMG]/.?ɧ l!:FH739Xa–P'@`.h#9v}`ʚZdP3PLr0 &+U(P&KoU<4w7Eb6/2eʥZbk!m5e&rnlcy״gk-\P+8'DDA)"J`[:vYaQfiЦX@ X2ؖ-)mײ9Fx9Ss;qB#f(qqi绷V Ȗ/08Wq]=}!i%&K;'Г/>jO8?2K9slWg^[E'BG\E4u|rɜ)hNd_00dL/0~/e*^;pVT渻. N!^mkqh=KhG >~}TZ ]ON\]U8Se nSj\=n6fwskTy3v|yxfaIlY qŒV1,?E/&,|(z=bl56䃎l7S4Fe7J>FfR9&cM>cJ7WuL_fzdNU ?V L%\"Ϋ[7gVuA\uj/͛9/pDEU9Lyyǻ?[ߢ|h޽/0g3C沼4UVawDK1%^>PF/²$H^bmZ eS6x٫0^z ԛDLuN*H4eMɑ_XֿuI=?q=*ec`]5lӗ]v.˥jel Lxv,߫Z^-|m򃭋W\^Rfu?:hv#gAb$^}} w`;GVa2BflNb(]9^ q~Y5.k'xe/,%g3-dZS|:vj]{yu1ͻk.q:yeQ H>0H;C8of=xR8qwB'c'Ѽ^#bPl^/|'9qU$ ZMQ1ճ3i@`6v>ĦYbAT 2"6DcHrbіe.z;ΌzC;3vQ} 8q.Mb9Ų@3'sxfrpdl=NalZ֐9Ww.1q=5cL,׽(_5:HRf!tpYN>ظphVX@A?#$Eeסo3j%pdWV2'2cٓBѢ= O-,Tulg !|aX/?VG-lNzLZS>ۏdEHFx'dwQ;o4=A9HǡGLΑXz)dlcRrA.Md]\[K@Y(s}2U(ܡ<fѦ˼[Fxnq[eqLObF[VZc`I)P "VQa"#?3Q:8)wd^ x78ޕ{!6Y(6܁nWshGRG,\&K+Q ](qm;d4 IMmt%w4#8(.kaI(+0gIXof26rM8 7"kݡ- _WԱf+<*u~|ZYg\RŒG뙣V-'kL <z7.r.e?Mm(Ƀďrs{_˵o/t >l?BiY6p:kb%Meʿx+I"N$vNζJ;7: F4FX@CaK'=AY;FcڊJKmC+Ӻ0mp+9ŷb"],n|ף ¹mzL[+h&FjtJNk5GVNT0Xpgkǭ V ~r81<=!ZaD4)MӇ d***;..B#FmR(T\ S3:f;n.N\`6ߨ2A#Nh[(TL9s8Z\H؏Nݕ.k >UEЀZ3KQTP:f*[ <cţTgto*]Riء5kmIa>nYm0fY(Fi)\C{I ^0"6y=ܒ>HD;]Ѱ:mbuEգ李U["J.GP|H{ `)δ b4ShkcH>};Dժz,CZͲh4j24gXN媨ѕY~E#]Q&6H1{Ъ U##@D}W!=n~F=}_Ffl" ЇFF쵏 ik..WD `'N),dwV.F:JjY(D9[ i2QpF2Vƶ9FDTu* C%&@Nv\8r10s h֯MaJϛ;R\?%$rMD'yfad ճ FPpQ._}䳤D$_Я?Vחw{Ѓl zR>`ROQ~Њe%"ċ5[^8 */Jʵӈ|.k 钩jf8(隆MͬK,Ko}?{P{k(p.Y!Qx>d'[K>ֆVLG]ϫWFP+ Uk;V<ʰhZ| qux&qr5j'霡 POAOTXΩ}R'J-(<"ª Tf\DY%u78鵪0aA2zy:~UTr欤 [ RXmǕY 0s>ǃ SV#/a=ÇXf.Dl˧M/q3Msٸ=׸h-iu7^W!C7-|;#6ӎO_{?+ѻ˿KxJ?cL6[x 7ҺZE%:iz4%33R1ɕؓ2G0tN?lj60/4rk}X7bHY3}7`^H~f"! ť0F u9i-yw܉ uWHIsp7Z`{+; .NPE453\>lFNc*>G=aq\XѝQy08q!9жx3Y7v=~^NƯ+Y栚tTHU_OO2^tJ^E^25E6T-V3?=8ɂ16#z!_ƿOYߴqп?qˠd5apȭ^ȳ# Z}Uڂ-kwTvŽȡ<=}y*ORWdKj>b9_j|+:xJ?vo=[zATy#dCTFeDAl9X-%d"ݧS;mj&wW<\t8]:p +=JCSܸPF!Vaa> Z dJjbNBAL}nMi0,L3{ L% NEv:=~18_-5 Y!x*^.D #E1:8H]1[&P DÚ-w^8Vc @Ӂa֒'u;Wzr\I/Q8UF~AH0U ؔ|`IAH1vܬ$E$4^G9M&)2Z߭d:/ 1RR\cѝfE.,SZy79?wqT}ƛуi6bYX𻩏@&OSaEi4s])LȒ%.*cEԢ^ۦl"Jr4l12թ4zghV* œśm͡_TD0lr3^"lf]<2Z(,^Lv KbebMo6u.K8&mixTVfąU |d@rt)}-alUlqAEe8U Zi,YӶ b? Y/A8lH&.wz,oiOբzO{M{)!>҅Ϙ!X #>Fȿ 4L1JhxsÖ6_L4yx5j>c"\KaB$9J|K0T/)Z06$ewIX,G>]0jȃYW*䭃Rl9'Dy02}C{/i] ҵ̨u3ⓙ|>j.RW3FFeĢnwo 1)U, +EY^ME Xй*/fEUp@=/et0 \taŨxZR "8AFmۦ|Ǩ5Gsps=`{t*d(P*VZHbVwL>79.?PcyֽPKGˆ̲$o2-q yŽ=_L)/xʐ?= XÜk0%%-?IuhAzǪDMQ4'[޲9fq@86`a׌JM)\J.f,Y>) wN +\ƔӅ<хZ|noeGXZ%SC4`rvȘ97Iv6O,iUM"<EMsVcۥ:k$:LtnՃcPӃXN@@w%.G0T J"G/p^y\Ɣi:ݣw}slйK!W#1[:̲ܞ qx1`+qg﯆z'W '$Ae0iyDZʒ :i }w ʪ\(G8\$ &rlmujٷƍŚ 5QGs| 3Ep߈vk立 ^P91эVxPˏbS?|Ti8._h0ٖH50c&T1OL6*H`[c隰[ph2'LKA$_2D4o ^r:7);k6d&=p`#|jQ(j_s3Jt(L/r  J7ٮ[b)j+?SV:aLcwp{skU!.CVSm{,\kPvEUVG 9eeQG;zRֲGTjR݂XC>:Y@׈tF.:[Is!+-o.7^ .=>N/_OK8ʫx>n9\uIO/o./7Οϗ'ʑUq],/]ײ0 :zz8QnZJ(ϗ/Uj 5HSޜ/?oүNqųUEdf̹ș$S^?ߨ2\VXΦI@^`+3ɘeӻ&#PDnxn9[T3+^_ꪎlƭ{r/&S,.Zy#O*W!D2QfǍZ"nRrԧB^p{B>Rb ֦- ǀVPCQ*yU۵r d B_5Uk%LzG3&) :86Ppt<"AWjIM-1imCJ/4ɲ(s2fXǛ."ʌ{A9#zK g&GP'X+{F6`04}4 :Tz)*7UN]+I ] #]}]sr>;)9hf]x;AF׫:;8*0ZBarA7ElM})ѲŨz%h\F)u,Ola`ͳwɎ9#"lP | HMnkx 663&Cq\1:Ee eoi\(e]2b:4)5u jI2:-9 *S\"b mhnrJhn=VhhOȇ|U߀FmdT3:#Ur/1s'G{kHv5eEb8escRQVa_9DD6)-3䡡y<8@\2y`%R_k <#īC/ci|t4U¾XT4씹֐8<iےn3_0]8*U:\*_]{o {NO7\^T<< ȳ' mA{W5S(1F΅]I&pfznwwհVz3yP){˶{h1&5_AN@;ʶ"Mu%4 ࢒G,u;:,dZ)Eb6N7; V7r3@eؤ=779O0|6cZkEYKS^䕤,661vӂ+lu-[:Y^&xȜZfnūUI}0nBJe1?@Miww`0,N"EYmZ69w:,nm7O5L@U؀ʣroi 9)9&G&w[G ύjT~;DCϧjtxbOZ\1= )R,7h,bb4yZN#_|"UUr_ܲ86{fMB=tp$3,f?6s-sG?Xdٺ44V*iJ@tlro!7J4Oh>ih8n؀x7瓲wCOt6Fm f[?8\áS&`fH8@QGlrsCº~[CɌZ1U`kkFq&cUuBO,̠V :S#ūVjuiZ}r9}{}y$ bڲ o~=u 'W]ڷoI*筪F@nuKQ÷\z|t8CS6R0MK3QI gh5w"Kתd7ZWxKz*/Xk{"Ȋ~}y՗Gcq2Uϗ<(_їȡ)bv|9i \KdTO~5mgƼd)ͩx~:?EM43ٔ<8חn?\_s>ބ^6ᱚ,NKHx(Oas`Rg0]U*́y>H l28sRj$@dZpE TH >D|}S%a!PoZڎ1۱]2,D6;ZNRBcށ t\{1;CM cP+TDi+ CsUlFQhbǥ{V8ojḀR-.|"؆ov/&Q>eӂ ҒڶlMVB}xm1WFwm1cvA#jywGwq`h*m8}6bC $ƻvCmWp%?pC`rDUIE Ȧw 6'cc_ۍ.VlR.5'0}W,k\lvAtLz66õs+R=gcyh"bzubiZdICE1f D3tq`wٵx\@eY :]/"n"]*> (jx[%xwlr?MfC<\?i^|7`x1,E, 4xN]%h]^}WIhRhxjJTw=DtX3:XF(c!/0C c3c5 3(' z DΏ8`CYX@ l3:/*whEWo\ú(T!21#]\𖶥)AԬ5Aٷ9F ߈ŎQu"Br~h8jƸ Rw?`c<Zh"7d_dS/?oNVd-S^nߞ_~^^/C?t]߮߯>E|Ur]۴r, !o ̀A>Le~26߭܍{mJxy~g>1eMhp$S9f4ϧM;;@-e[AC;0Ƕ,o:JC|vYūZj@k- ]\Y|Y88$_ʃ_߾("D}yd$7}ls3Ihkؖ<d)P-,LJצپ; u#򩫌*2le3DZxP4MGV nFȡ]+|cHE_﹎32x֥fBOoA]h wߛMh- BUD;T&-H+zGwj˅`#| o+>R!ZUAΫgKڄi-qyay;r1dx0׵imv}ZSK_IڌŮi:Y}Ts)xZ׀d^]t2kyeZ?ysֲXxZho9+`GL'gx(TL=Kl&-;¿N|y)6Idz#&w|Dc q$m瓞-n|OS=$8Odm ]H0|6ȡ=qҺ4tTgק* tKZH&Pj?:^LJn=ݿyR:DqH<cz hdW.u#mtpw_OZ0*NjYלKJSI/>?R?\!Z7퉫\ӵ1U?&UOw=%r,ḵ!IBeeO_VC0;iYjyuk^"pgXB5âSɫ5{ݺpnzkwl{ E[˦i]E4 taG鉫]@_ߞ_Kgz"kŷ7. >,-éN䥟iFkmIWVv;j87hod/qPS黥uދMf$g#|r :YE59g(5JzslS+ҖpQl;JGfr2}S7$%@eGZpi@dHzBa|v X2LQ]eo-=Ӣ@[w{_DJ|fG3%nJ ]S_=_kUL`.Q4|*է/+Pc&o:y?+)Ny X/'t௉{ n(+W0nsM rW!Mj9)oyW_Hahۤphjp"qN+ąlDu5Y2sA Ax mgsDʹml SA%iKZ ?+< f=_w6(Ϝsm@.aC'ܒd ]ߓWRlUB)M^~tb)vC1ϼݟOw/?t'Rîs!"/6)+*ɦ<-ZaˤPVzb+Pv[>!AѲ伢 (JdGWS+5)5V7Rמ''脢΋z<]IwuTn%r)@ &gx.>O~'=nV~vb?C3yG&HjŎTv}V1殛>lw($VL`]7hu<=8^utncd\]#nvuv"8흖VQ2lB d{$58ݗ%P3qm ⰴ,>s\Li%RTљ+ܭ}8RQﶳ8hP!.f$3#[G/jərPXYT++q|T]ҚГr䔵j-yJ<0Y WL͇\5v)c 8c쐩9h3qHOu#. (M >r M{V0(r p1+Moc2$@zE`2 EY0p[Wзځ:j#h\vwPFpQRǚTg(Chۤ=?ÿ\!˱{(('~O8&!Rx@$kÃbTَR y$)D>Gԃ31Ƀ-s+i)I3~Yޜ؟ݘ&jMVwWz:: ک2+Zٱj50˧ BWsT?e֩EKb~Q;%Cޡ`OJZ&I"_3?-aZ34,Lz*i]PIĠ£L7({)V2 TRy&3N&N TBF $'P,ֈ3}mYJ9Ld<IEᘰ=n&!\RrI RYP'U;c?_Ղ|d|[u*2_OD.U_sk}6HGV Wjzt#"IjgY].U|h U'MkSU|kJJe^Ph`*E7fV<Ѷ,Zk~o,_68\T=px֚ yK*>qz}j#Qs]Ts qd3[ DVj'68e]_WWPۆ`⻻_o߭/ /p\Okf!qy&qʍ kV N~32Mzo|12N̶Y꼿n-֫*,j"haת 9{EQ,[m=e9)Ѷ%1/6D9!kx_/O_wZ3wUzdoޔ&/ƝP`P('&PpRƤP"Ǭ!N$@YʹѐCFEʮ > #,e L]LbZ;0AS:ޖr ԫ',RhTdc85rݩanz# vtԵZÓSOd8t4RZK`Vjlۮ:?=6*Kv8=RepL]xҦJ OBĕ륖qplӤԲWoAEhSB g*}f*@kj3*CT >byw"m\һMN4 7YQ 9N]M-J[QJ뭵.@Ҡ x3#)HHu!1f*A l#b,AψKzӐ؀ĮآkM S&x ؎1iM(hҷldC}&466 shhӪ$Vf=Ӽ Ǯ!Ǯ(K*].krKǢ7ԍ,Rceߴm\ !lc]E/`$iazYHAQ??fvtP]=x]GӠ-^Ҟq$B43 40,ɶƺPI1vUȒ:gf33+kɪ$M&NUu\KCYF %4HQP\xy[@7i̯jDAL{>(bA^L̋AG>%2~תXV.z$4^k|z*^zE3#<8ġŗ.iio7FܳXS-J6|d,q]\3_0(jLQ)^M3<4,C!,*ִM<8"`hs*P19噕%.i7'"$^ 4OQ0E$+M_7_futUF"$xJQ)Yqln,`ٸa#|s|8 Lq"L8d ,S:| GAasHp ΅A%zfv#m@u,kVm/t,.IuS=cdq*e'ͯK'-::vl36Myy@m:cAVbpTA"b =ݮvuz:-G(Iٮ<4%,뢲A 2: &DѨ⥆/ϯO7O/ۃg KoDq׀mdpѥ3!U޾lo@=ԭN*fEE~H A%N|b.XI9U-B(tI+`,muۓEYp@|hvIa3^w 3Ɋ>:Vx]WKn獡nJ6aTd*t1pP̭OqZ}UOSCĮۅO3tКsn_6owfuж0߿lv{B}߼n5f8>ΚU ̜/10_%Kf07Ӣ1H\d>aй͂I,f<fѩYt%0,6qNR} H<8v'J8ٴ@V@I1sW%4n| V.*r~7 ๹8 sb!aI(Ji[PLk:${GΔ{[f>ݱ[+ C.SkC8Yg^p0Oo߶/2U IUsJ޸b؝&1$ w cL< ?хpqlNf{ʐ?/g<ً:u=+ R7&fYpO4oUqNB1AVV GnQ !EnKrq݃ /?/Ι-3-cBcC+H\O /g?=zǞc%8mgg8 ?2"4"Xq:vӲblrs^؆C$U4dfֆqb'|Z/Y"P:X+?8Xoe,7ȇq%|o+ڐIQI"PW;8AZ8G3T-y뾂xiM/LڴuE]ayN+ uJ$s61HEZ,n&@4_S u,äh,0Z;Rp. xb>ZEL/ d\Lv~2qFbpzW!9l |)<*h̏d8O5x{j2[Dy)m50u 2d8 ޳Mǣ]ucUh #b YBk" %Iغz7K&u n+lElC<`JOJ%ZZIgߴ)K{0 `]e)5f@Qdo9kPRcTؘ*ʯr[P÷݀r5>/wf Bs"m FJ6 iU@7 `.$!t@ҦCL?ةtu;35',l_C10 ?ef4eܫx7QC7/-jGڅ~HO2<zJPB0N' %砋>`  A8%!f(͜N" C:/A8thhkI w(UR`XI۰H-iю2##FE?7o?^1Am^?"qGBD׉ 5l?FUIqS$ѳTc]u3:Y*wv+Q[=,c)>2dtiacwZiմV0ց %U ,dr(J2s`TO!FMl[FpOY\c bZ/R*ajZߍmp6,_jsdbf~b2 *K,cb?VQqii#SP+zAe R݋ AQ\%N#cKXXLf33-(9? 0y%d`Lw>?%=lw VO3.z7@Pe:h6 R"R\5̩ᴿ^u8޺`wlKܚQ-AnFt%SX@jI! w?ȴ]J/#Mn$ŝLYT7.. 8}bKjJyr dSBMu&dCiD̄m.SܱeQI=KǝX F%*kM"lGg$%8W)pAX*f z@r!1WNYBF@f<(O iж./%?xA?{hB;F[Kw/oG=rՕvi {B>\<š\i$ #"?U4 CJ44GkW;/Q6+<`&RlѾdR=89Gp$ۢ^DucKW'&"PY*kFcCi]ZO/ Tz LzYOcCG\|R.%g;vI;ڟxzm!IGh~N2*Jnz,?DR(ĴSHw J>tHP[Aw %3u8)kbWNtkD0-1k$ΒwY[h: HtDMvA@*Gtd_Φi0|&U̝EPT,ay7.pϻ澖/ %}Xb8TJuwnJpǸzȢț'ggZ$8(<ֵR@]ѐ`29궗vښ;j34Y@tP<=>O7=Kiy5'0!^EoɐHN4FV5j"[NHmUEV]2;C!QQ8SFHWXE;o0hA ou2̑'-ٔ'}hA-Cj,$)w̱OE|H~uQ$iK#^?zoi2lV9WY>?~Yz؁.sVbz *6?eu| ǘ ӕΕ;/MEd3^_wS^-"*HԼd]9ʹݙH&w\q.:2 y0Oe]:@-|u61lh[q&$fx!TYnK *Vqx3TUpo9, }W0Ɯ{+@Ɗ+x~u-9Q l|%F9*9s0ўcp]8Eñ柇uQ $gD&&ɴ@BֵD6mrb+鱳*(Ng'VJA9uq;IC.z{!J_T]{VoQ__WItSHo37RW>d5u6 N5ZW\T\,K kՀ'5+rf) Kќ_rK1`Sb.&4rr@n!/|)-re<K63Gaq v]%3VvB;:H5dkgՂy72Z=i;kWܢzu_ӍHC{C20Gc$ gP`Y|- q0=Ȉq՜EFgiZc$xBQd&+ӧ f_z*L+2at{m/i9(+Ou#ɧodh?Y$IBw]rduas%= 1R+-G۪\*⼊4psae~? FӞwRa 2N!$3D n/M*[Ƞ$ޜpp}1#+w T"e3,J:вoE)(ga~y4a;;Y:?Cs&gĩUjP^ۥiKVkYσs@yC-b!`d^I"٣(v<˜ h32>`T{{}jq 2$,81eU8 ^Y~pgolang-github-cilium-ebpf-0.11.0/internal/kconfig/testdata/test.kconfig000066400000000000000000000003111456504511400261000ustar00rootroot00000000000000CONFIG_TRISTATE=m # CONFIG_IS_NOT_SET is not set CONFIG_BOOL=y CONFIG_CHAR=100 CONFIG_USHORT=30000 CONFIG_INT=123456 CONFIG_ULONG=0xDEADBEEFC0DE CONFIG_STR="abracad" CONFIG_FOO="foo" CONFIG_FOO="bar" golang-github-cilium-ebpf-0.11.0/internal/memoize.go000066400000000000000000000007401456504511400223300ustar00rootroot00000000000000package internal import ( "sync" ) type memoizedFunc[T any] struct { once sync.Once fn func() (T, error) result T err error } func (mf *memoizedFunc[T]) do() (T, error) { mf.once.Do(func() { mf.result, mf.err = mf.fn() }) return mf.result, mf.err } // Memoize the result of a function call. // // fn is only ever called once, even if it returns an error. func Memoize[T any](fn func() (T, error)) func() (T, error) { return (&memoizedFunc[T]{fn: fn}).do } golang-github-cilium-ebpf-0.11.0/internal/output.go000066400000000000000000000036721456504511400222320ustar00rootroot00000000000000package internal import ( "bytes" "errors" "go/format" "go/scanner" "io" "reflect" "strings" "unicode" ) // Identifier turns a C style type or field name into an exportable Go equivalent. func Identifier(str string) string { prev := rune(-1) return strings.Map(func(r rune) rune { // See https://golang.org/ref/spec#Identifiers switch { case unicode.IsLetter(r): if prev == -1 { r = unicode.ToUpper(r) } case r == '_': switch { // The previous rune was deleted, or we are at the // beginning of the string. case prev == -1: fallthrough // The previous rune is a lower case letter or a digit. case unicode.IsDigit(prev) || (unicode.IsLetter(prev) && unicode.IsLower(prev)): // delete the current rune, and force the // next character to be uppercased. r = -1 } case unicode.IsDigit(r): default: // Delete the current rune. prev is unchanged. return -1 } prev = r return r }, str) } // WriteFormatted outputs a formatted src into out. // // If formatting fails it returns an informative error message. func WriteFormatted(src []byte, out io.Writer) error { formatted, err := format.Source(src) if err == nil { _, err = out.Write(formatted) return err } var el scanner.ErrorList if !errors.As(err, &el) { return err } var nel scanner.ErrorList for _, err := range el { if !err.Pos.IsValid() { nel = append(nel, err) continue } buf := src[err.Pos.Offset:] nl := bytes.IndexRune(buf, '\n') if nl == -1 { nel = append(nel, err) continue } err.Msg += ": " + string(buf[:nl]) nel = append(nel, err) } return nel } // GoTypeName is like %T, but elides the package name. // // Pointers to a type are peeled off. func GoTypeName(t any) string { rT := reflect.TypeOf(t) for rT.Kind() == reflect.Pointer { rT = rT.Elem() } // Doesn't return the correct Name for generic types due to https://github.com/golang/go/issues/55924 return rT.Name() } golang-github-cilium-ebpf-0.11.0/internal/output_test.go000066400000000000000000000017531456504511400232670ustar00rootroot00000000000000package internal import ( "testing" qt "github.com/frankban/quicktest" ) func TestIdentifier(t *testing.T) { testcases := []struct { in, out string }{ {".rodata", "Rodata"}, {"_foo_bar_", "FooBar"}, {"ipv6_test", "Ipv6Test"}, {"FOO_BAR", "FOO_BAR"}, {"FOO_", "FOO_"}, {"FOO__BAR", "FOO__BAR"}, {"FOO___BAR", "FOO___BAR"}, {"_FOO__BAR", "FOO__BAR"}, {"__FOO__BAR", "FOO__BAR"}, } for _, tc := range testcases { have := Identifier(tc.in) if have != tc.out { t.Errorf("Expected %q as output of %q, got %q", tc.out, tc.in, have) } } } func TestGoTypeName(t *testing.T) { type foo struct{} type bar[T any] struct{} qt.Assert(t, GoTypeName(foo{}), qt.Equals, "foo") qt.Assert(t, GoTypeName(new(foo)), qt.Equals, "foo") qt.Assert(t, GoTypeName(new(*foo)), qt.Equals, "foo") qt.Assert(t, GoTypeName(bar[int]{}), qt.Equals, "bar[int]") // Broken in the stdlib, see GoTypeName for details. // qt.Assert(t, GoTypeName(bar[qt.C]{}), qt.Equals, "bar[quicktest.C]") } golang-github-cilium-ebpf-0.11.0/internal/pinning.go000066400000000000000000000026411456504511400223270ustar00rootroot00000000000000package internal import ( "errors" "fmt" "os" "path/filepath" "runtime" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) func Pin(currentPath, newPath string, fd *sys.FD) error { if newPath == "" { return errors.New("given pinning path cannot be empty") } if currentPath == newPath { return nil } fsType, err := FSType(filepath.Dir(newPath)) if err != nil { return err } if fsType != unix.BPF_FS_MAGIC { return fmt.Errorf("%s is not on a bpf filesystem", newPath) } defer runtime.KeepAlive(fd) if currentPath == "" { return sys.ObjPin(&sys.ObjPinAttr{ Pathname: sys.NewStringPointer(newPath), BpfFd: fd.Uint(), }) } // Renameat2 is used instead of os.Rename to disallow the new path replacing // an existing path. err = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE) if err == nil { // Object is now moved to the new pinning path. return nil } if !os.IsNotExist(err) { return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err) } // Internal state not in sync with the file system so let's fix it. return sys.ObjPin(&sys.ObjPinAttr{ Pathname: sys.NewStringPointer(newPath), BpfFd: fd.Uint(), }) } func Unpin(pinnedPath string) error { if pinnedPath == "" { return nil } err := os.Remove(pinnedPath) if err == nil || os.IsNotExist(err) { return nil } return err } golang-github-cilium-ebpf-0.11.0/internal/platform.go000066400000000000000000000015061456504511400225100ustar00rootroot00000000000000package internal import ( "runtime" ) // PlatformPrefix returns the platform-dependent syscall wrapper prefix used by // the linux kernel. // // Based on https://github.com/golang/go/blob/master/src/go/build/syslist.go // and https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L10047 func PlatformPrefix() string { switch runtime.GOARCH { case "386": return "__ia32_" case "amd64", "amd64p32": return "__x64_" case "arm", "armbe": return "__arm_" case "arm64", "arm64be": return "__arm64_" case "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le": return "__mips_" case "s390": return "__s390_" case "s390x": return "__s390x_" case "riscv", "riscv64": return "__riscv_" case "ppc": return "__powerpc_" case "ppc64", "ppc64le": return "__powerpc64_" default: return "" } } golang-github-cilium-ebpf-0.11.0/internal/prog.go000066400000000000000000000010071456504511400216270ustar00rootroot00000000000000package internal // EmptyBPFContext is the smallest-possible BPF input context to be used for // invoking `Program.{Run,Benchmark,Test}`. // // Programs require a context input buffer of at least 15 bytes. Looking in // net/bpf/test_run.c, bpf_test_init() requires that the input is at least // ETH_HLEN (14) bytes. As of Linux commit fd18942 ("bpf: Don't redirect packets // with invalid pkt_len"), it also requires the skb to be non-empty after // removing the Layer 2 header. var EmptyBPFContext = make([]byte, 15) golang-github-cilium-ebpf-0.11.0/internal/statfs.go000066400000000000000000000010231456504511400221620ustar00rootroot00000000000000package internal import ( "unsafe" "github.com/cilium/ebpf/internal/unix" ) func FSType(path string) (int64, error) { var statfs unix.Statfs_t if err := unix.Statfs(path, &statfs); err != nil { return 0, err } fsType := int64(statfs.Type) if unsafe.Sizeof(statfs.Type) == 4 { // We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a // negative number when interpreted as int32 so we need to cast via // uint32 to avoid sign extension. fsType = int64(uint32(statfs.Type)) } return fsType, nil } golang-github-cilium-ebpf-0.11.0/internal/statfs_test.go000066400000000000000000000006331456504511400232270ustar00rootroot00000000000000package internal import ( "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestFSType(t *testing.T) { for _, fs := range []struct { path string magic int64 }{ {"/sys/kernel/tracing", unix.TRACEFS_MAGIC}, {"/sys/fs/bpf", unix.BPF_FS_MAGIC}, } { fst, err := FSType(fs.path) qt.Assert(t, err, qt.IsNil) qt.Assert(t, fst, qt.Equals, fs.magic) } } golang-github-cilium-ebpf-0.11.0/internal/sys/000077500000000000000000000000001456504511400211515ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/sys/doc.go000066400000000000000000000003611456504511400222450ustar00rootroot00000000000000// Package sys contains bindings for the BPF syscall. package sys // Regenerate types.go by invoking go generate in the current directory. //go:generate go run github.com/cilium/ebpf/internal/cmd/gentypes ../../btf/testdata/vmlinux.btf.gz golang-github-cilium-ebpf-0.11.0/internal/sys/fd.go000066400000000000000000000053601456504511400220750ustar00rootroot00000000000000package sys import ( "fmt" "math" "os" "runtime" "strconv" "github.com/cilium/ebpf/internal/unix" ) var ErrClosedFd = unix.EBADF type FD struct { raw int } func newFD(value int) *FD { if onLeakFD != nil { // Attempt to store the caller's stack for the given fd value. // Panic if fds contains an existing stack for the fd. old, exist := fds.LoadOrStore(value, callersFrames()) if exist { f := old.(*runtime.Frames) panic(fmt.Sprintf("found existing stack for fd %d:\n%s", value, FormatFrames(f))) } } fd := &FD{value} runtime.SetFinalizer(fd, (*FD).finalize) return fd } // finalize is set as the FD's runtime finalizer and // sends a leak trace before calling FD.Close(). func (fd *FD) finalize() { if fd.raw < 0 { return } // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback // is invoked at most once for one sys.FD allocation, runtime.Frames can only // be unwound once. f, ok := fds.LoadAndDelete(fd.Int()) if ok && onLeakFD != nil { onLeakFD(f.(*runtime.Frames)) } _ = fd.Close() } // NewFD wraps a raw fd with a finalizer. // // You must not use the raw fd after calling this function, since the underlying // file descriptor number may change. This is because the BPF UAPI assumes that // zero is not a valid fd value. func NewFD(value int) (*FD, error) { if value < 0 { return nil, fmt.Errorf("invalid fd %d", value) } fd := newFD(value) if value != 0 { return fd, nil } dup, err := fd.Dup() _ = fd.Close() return dup, err } func (fd *FD) String() string { return strconv.FormatInt(int64(fd.raw), 10) } func (fd *FD) Int() int { return fd.raw } func (fd *FD) Uint() uint32 { if fd.raw < 0 || int64(fd.raw) > math.MaxUint32 { // Best effort: this is the number most likely to be an invalid file // descriptor. It is equal to -1 (on two's complement arches). return math.MaxUint32 } return uint32(fd.raw) } func (fd *FD) Close() error { if fd.raw < 0 { return nil } return unix.Close(fd.disown()) } func (fd *FD) disown() int { value := int(fd.raw) fds.Delete(int(value)) fd.raw = -1 runtime.SetFinalizer(fd, nil) return value } func (fd *FD) Dup() (*FD, error) { if fd.raw < 0 { return nil, ErrClosedFd } // Always require the fd to be larger than zero: the BPF API treats the value // as "no argument provided". dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 1) if err != nil { return nil, fmt.Errorf("can't dup fd: %v", err) } return newFD(dup), nil } // File takes ownership of FD and turns it into an [*os.File]. // // You must not use the FD after the call returns. // // Returns nil if the FD is not valid. func (fd *FD) File(name string) *os.File { if fd.raw < 0 { return nil } return os.NewFile(uintptr(fd.disown()), name) } golang-github-cilium-ebpf-0.11.0/internal/sys/fd_test.go000066400000000000000000000024721456504511400231350ustar00rootroot00000000000000package sys import ( "os" "syscall" "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func init() { // Free up fd 0 for TestFD. stdin, err := unix.FcntlInt(os.Stdin.Fd(), unix.F_DUPFD_CLOEXEC, 1) if err != nil { panic(err) } old := os.Stdin os.Stdin = os.NewFile(uintptr(stdin), "stdin") old.Close() reserveFdZero() } func reserveFdZero() { fd, err := unix.Open(os.DevNull, syscall.O_RDONLY, 0) if err != nil { panic(err) } if fd != 0 { panic(err) } } func TestFD(t *testing.T) { _, err := NewFD(-1) qt.Assert(t, err, qt.IsNotNil, qt.Commentf("negative fd should be rejected")) fd, err := NewFD(0) qt.Assert(t, err, qt.IsNil) qt.Assert(t, fd.Int(), qt.Not(qt.Equals), 0, qt.Commentf("fd value should not be zero")) var stat unix.Stat_t err = unix.Fstat(0, &stat) qt.Assert(t, err, qt.ErrorIs, unix.EBADF, qt.Commentf("zero fd should be closed")) reserveFdZero() } func TestFDFile(t *testing.T) { fd := newFD(openFd(t)) file := fd.File("test") qt.Assert(t, file, qt.IsNotNil) qt.Assert(t, file.Close(), qt.IsNil) qt.Assert(t, fd.File("closed"), qt.IsNil) _, err := fd.Dup() qt.Assert(t, err, qt.ErrorIs, ErrClosedFd) } func openFd(tb testing.TB) int { fd, err := unix.Open(os.DevNull, syscall.O_RDONLY, 0) qt.Assert(tb, err, qt.IsNil) return fd } golang-github-cilium-ebpf-0.11.0/internal/sys/fd_trace.go000066400000000000000000000043521456504511400232530ustar00rootroot00000000000000package sys import ( "bytes" "fmt" "runtime" "sync" ) // OnLeakFD controls tracing [FD] lifetime to detect resources that are not // closed by Close(). // // If fn is not nil, tracing is enabled for all FDs created going forward. fn is // invoked for all FDs that are closed by the garbage collector instead of an // explicit Close() by a caller. Calling OnLeakFD twice with a non-nil fn // (without disabling tracing in the meantime) will cause a panic. // // If fn is nil, tracing will be disabled. Any FDs that have not been closed are // considered to be leaked, fn will be invoked for them, and the process will be // terminated. // // fn will be invoked at most once for every unique sys.FD allocation since a // runtime.Frames can only be unwound once. func OnLeakFD(fn func(*runtime.Frames)) { // Enable leak tracing if new fn is provided. if fn != nil { if onLeakFD != nil { panic("OnLeakFD called twice with non-nil fn") } onLeakFD = fn return } // fn is nil past this point. if onLeakFD == nil { return } // Call onLeakFD for all open fds. if fs := flushFrames(); len(fs) != 0 { for _, f := range fs { onLeakFD(f) } } onLeakFD = nil } var onLeakFD func(*runtime.Frames) // fds is a registry of all file descriptors wrapped into sys.fds that were // created while an fd tracer was active. var fds sync.Map // map[int]*runtime.Frames // flushFrames removes all elements from fds and returns them as a slice. This // deals with the fact that a runtime.Frames can only be unwound once using // Next(). func flushFrames() []*runtime.Frames { var frames []*runtime.Frames fds.Range(func(key, value any) bool { frames = append(frames, value.(*runtime.Frames)) fds.Delete(key) return true }) return frames } func callersFrames() *runtime.Frames { c := make([]uintptr, 32) // Skip runtime.Callers and this function. i := runtime.Callers(2, c) if i == 0 { return nil } return runtime.CallersFrames(c) } // FormatFrames formats a runtime.Frames as a human-readable string. func FormatFrames(fs *runtime.Frames) string { var b bytes.Buffer for { f, more := fs.Next() b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line)) if !more { break } } return b.String() } golang-github-cilium-ebpf-0.11.0/internal/sys/mapflags_string.go000066400000000000000000000026751456504511400246720ustar00rootroot00000000000000// Code generated by "stringer -type MapFlags"; DO NOT EDIT. package sys import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[BPF_F_NO_PREALLOC-1] _ = x[BPF_F_NO_COMMON_LRU-2] _ = x[BPF_F_NUMA_NODE-4] _ = x[BPF_F_RDONLY-8] _ = x[BPF_F_WRONLY-16] _ = x[BPF_F_STACK_BUILD_ID-32] _ = x[BPF_F_ZERO_SEED-64] _ = x[BPF_F_RDONLY_PROG-128] _ = x[BPF_F_WRONLY_PROG-256] _ = x[BPF_F_CLONE-512] _ = x[BPF_F_MMAPABLE-1024] _ = x[BPF_F_PRESERVE_ELEMS-2048] _ = x[BPF_F_INNER_MAP-4096] } const _MapFlags_name = "BPF_F_NO_PREALLOCBPF_F_NO_COMMON_LRUBPF_F_NUMA_NODEBPF_F_RDONLYBPF_F_WRONLYBPF_F_STACK_BUILD_IDBPF_F_ZERO_SEEDBPF_F_RDONLY_PROGBPF_F_WRONLY_PROGBPF_F_CLONEBPF_F_MMAPABLEBPF_F_PRESERVE_ELEMSBPF_F_INNER_MAP" var _MapFlags_map = map[MapFlags]string{ 1: _MapFlags_name[0:17], 2: _MapFlags_name[17:36], 4: _MapFlags_name[36:51], 8: _MapFlags_name[51:63], 16: _MapFlags_name[63:75], 32: _MapFlags_name[75:95], 64: _MapFlags_name[95:110], 128: _MapFlags_name[110:127], 256: _MapFlags_name[127:144], 512: _MapFlags_name[144:155], 1024: _MapFlags_name[155:169], 2048: _MapFlags_name[169:189], 4096: _MapFlags_name[189:204], } func (i MapFlags) String() string { if str, ok := _MapFlags_map[i]; ok { return str } return "MapFlags(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.11.0/internal/sys/ptr.go000066400000000000000000000024671456504511400223160ustar00rootroot00000000000000package sys import ( "unsafe" "github.com/cilium/ebpf/internal/unix" ) // NewPointer creates a 64-bit pointer from an unsafe Pointer. func NewPointer(ptr unsafe.Pointer) Pointer { return Pointer{ptr: ptr} } // NewSlicePointer creates a 64-bit pointer from a byte slice. func NewSlicePointer(buf []byte) Pointer { if len(buf) == 0 { return Pointer{} } return Pointer{ptr: unsafe.Pointer(&buf[0])} } // NewSlicePointerLen creates a 64-bit pointer from a byte slice. // // Useful to assign both the pointer and the length in one go. func NewSlicePointerLen(buf []byte) (Pointer, uint32) { return NewSlicePointer(buf), uint32(len(buf)) } // NewStringPointer creates a 64-bit pointer from a string. func NewStringPointer(str string) Pointer { p, err := unix.BytePtrFromString(str) if err != nil { return Pointer{} } return Pointer{ptr: unsafe.Pointer(p)} } // NewStringSlicePointer allocates an array of Pointers to each string in the // given slice of strings and returns a 64-bit pointer to the start of the // resulting array. // // Use this function to pass arrays of strings as syscall arguments. func NewStringSlicePointer(strings []string) Pointer { sp := make([]Pointer, 0, len(strings)) for _, s := range strings { sp = append(sp, NewStringPointer(s)) } return Pointer{ptr: unsafe.Pointer(&sp[0])} } golang-github-cilium-ebpf-0.11.0/internal/sys/ptr_32_be.go000066400000000000000000000003351456504511400232600ustar00rootroot00000000000000//go:build armbe || mips || mips64p32 package sys import ( "unsafe" ) // Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. type Pointer struct { pad uint32 ptr unsafe.Pointer } golang-github-cilium-ebpf-0.11.0/internal/sys/ptr_32_le.go000066400000000000000000000003621456504511400232720ustar00rootroot00000000000000//go:build 386 || amd64p32 || arm || mipsle || mips64p32le package sys import ( "unsafe" ) // Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. type Pointer struct { ptr unsafe.Pointer pad uint32 } golang-github-cilium-ebpf-0.11.0/internal/sys/ptr_64.go000066400000000000000000000004141456504511400226150ustar00rootroot00000000000000//go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32 package sys import ( "unsafe" ) // Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. type Pointer struct { ptr unsafe.Pointer } golang-github-cilium-ebpf-0.11.0/internal/sys/signals.go000066400000000000000000000050271456504511400231440ustar00rootroot00000000000000package sys import ( "fmt" "runtime" "unsafe" "github.com/cilium/ebpf/internal/unix" ) // A sigset containing only SIGPROF. var profSet unix.Sigset_t func init() { // See sigsetAdd for details on the implementation. Open coded here so // that the compiler will check the constant calculations for us. profSet.Val[sigprofBit/wordBits] |= 1 << (sigprofBit % wordBits) } // maskProfilerSignal locks the calling goroutine to its underlying OS thread // and adds SIGPROF to the thread's signal mask. This prevents pprof from // interrupting expensive syscalls like e.g. BPF_PROG_LOAD. // // The caller must defer unmaskProfilerSignal() to reverse the operation. func maskProfilerSignal() { runtime.LockOSThread() if err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil { runtime.UnlockOSThread() panic(fmt.Errorf("masking profiler signal: %w", err)) } } // unmaskProfilerSignal removes SIGPROF from the underlying thread's signal // mask, allowing it to be interrupted for profiling once again. // // It also unlocks the current goroutine from its underlying OS thread. func unmaskProfilerSignal() { defer runtime.UnlockOSThread() if err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil { panic(fmt.Errorf("unmasking profiler signal: %w", err)) } } const ( // Signal is the nth bit in the bitfield. sigprofBit = int(unix.SIGPROF - 1) // The number of bits in one Sigset_t word. wordBits = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0])) * 8 ) // sigsetAdd adds signal to set. // // Note: Sigset_t.Val's value type is uint32 or uint64 depending on the arch. // This function must be able to deal with both and so must avoid any direct // references to u32 or u64 types. func sigsetAdd(set *unix.Sigset_t, signal unix.Signal) error { if signal < 1 { return fmt.Errorf("signal %d must be larger than 0", signal) } // For amd64, runtime.sigaddset() performs the following operation: // set[(signal-1)/32] |= 1 << ((uint32(signal) - 1) & 31) // // This trick depends on sigset being two u32's, causing a signal in the the // bottom 31 bits to be written to the low word if bit 32 is low, or the high // word if bit 32 is high. // Signal is the nth bit in the bitfield. bit := int(signal - 1) // Word within the sigset the bit needs to be written to. word := bit / wordBits if word >= len(set.Val) { return fmt.Errorf("signal %d does not fit within unix.Sigset_t", signal) } // Write the signal bit into its corresponding word at the corrected offset. set.Val[word] |= 1 << (bit % wordBits) return nil } golang-github-cilium-ebpf-0.11.0/internal/sys/signals_test.go000066400000000000000000000036661456504511400242120ustar00rootroot00000000000000package sys import ( "runtime" "testing" "unsafe" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestSigset(t *testing.T) { const maxSignal = unix.Signal(unsafe.Sizeof(unix.Sigset_t{}) * 8) // Type-infer a sigset word. This is a typed uint of 32 or 64 bits depending // on the target architecture, so we can't use an untyped uint. zero := unix.Sigset_t{}.Val[0] words := len(unix.Sigset_t{}.Val) var want, got unix.Sigset_t // Flip the first bit of the first word. if err := sigsetAdd(&got, 1); err != nil { t.Fatal(err) } want.Val[0] = 1 if want != got { t.Fatalf("expected first word to be 0x%x, got: 0x%x", want, got) } // And the last bit of the last word. if err := sigsetAdd(&got, maxSignal); err != nil { t.Fatal(err) } want.Val[words-1] = ^(^zero >> 1) if want != got { t.Fatalf("expected last word to be 0x%x, got: 0x%x", want, got) } if err := sigsetAdd(&got, maxSignal+1); err == nil { t.Fatal("expected out-of-bounds add to be rejected") } if err := sigsetAdd(&got, -1); err == nil { t.Fatal("expected negative signal to be rejected") } } func TestProfilerSignal(t *testing.T) { // Additional goroutine lock to make the PthreadSigmask below execute on the // same OS thread as the functions under test. UnlockOSThread needs to be // called as many times as LockOSThread to unlock the goroutine. runtime.LockOSThread() defer runtime.UnlockOSThread() var old unix.Sigset_t if err := unix.PthreadSigmask(0, nil, &old); err != nil { t.Fatal("get sigmask:", err) } maskProfilerSignal() var have unix.Sigset_t if err := unix.PthreadSigmask(0, nil, &have); err != nil { t.Fatal("get sigmask:", err) } want := have qt.Assert(t, sigsetAdd(&want, unix.SIGPROF), qt.IsNil) qt.Assert(t, have, qt.Equals, want) unmaskProfilerSignal() if err := unix.PthreadSigmask(0, nil, &have); err != nil { t.Fatal("get sigmask:", err) } qt.Assert(t, have, qt.Equals, old) } golang-github-cilium-ebpf-0.11.0/internal/sys/syscall.go000066400000000000000000000074141456504511400231600ustar00rootroot00000000000000package sys import ( "runtime" "syscall" "unsafe" "github.com/cilium/ebpf/internal/unix" ) // ENOTSUPP is a Linux internal error code that has leaked into UAPI. // // It is not the same as ENOTSUP or EOPNOTSUPP. var ENOTSUPP = syscall.Errno(524) // BPF wraps SYS_BPF. // // Any pointers contained in attr must use the Pointer type from this package. func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { // Prevent the Go profiler from repeatedly interrupting the verifier, // which could otherwise lead to a livelock due to receiving EAGAIN. if cmd == BPF_PROG_LOAD || cmd == BPF_PROG_RUN { maskProfilerSignal() defer unmaskProfilerSignal() } for { r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) runtime.KeepAlive(attr) // As of ~4.20 the verifier can be interrupted by a signal, // and returns EAGAIN in that case. if errNo == unix.EAGAIN && cmd == BPF_PROG_LOAD { continue } var err error if errNo != 0 { err = wrappedErrno{errNo} } return r1, err } } // Info is implemented by all structs that can be passed to the ObjInfo syscall. // // MapInfo // ProgInfo // LinkInfo // BtfInfo type Info interface { info() (unsafe.Pointer, uint32) } var _ Info = (*MapInfo)(nil) func (i *MapInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*ProgInfo)(nil) func (i *ProgInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*LinkInfo)(nil) func (i *LinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*BtfInfo)(nil) func (i *BtfInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } // ObjInfo retrieves information about a BPF Fd. // // info may be one of MapInfo, ProgInfo, LinkInfo and BtfInfo. func ObjInfo(fd *FD, info Info) error { ptr, len := info.info() err := ObjGetInfoByFd(&ObjGetInfoByFdAttr{ BpfFd: fd.Uint(), InfoLen: len, Info: NewPointer(ptr), }) runtime.KeepAlive(fd) return err } // BPFObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. type ObjName [unix.BPF_OBJ_NAME_LEN]byte // NewObjName truncates the result if it is too long. func NewObjName(name string) ObjName { var result ObjName copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) return result } // LogLevel controls the verbosity of the kernel's eBPF program verifier. type LogLevel uint32 const ( BPF_LOG_LEVEL1 LogLevel = 1 << iota BPF_LOG_LEVEL2 BPF_LOG_STATS ) // LinkID uniquely identifies a bpf_link. type LinkID uint32 // BTFID uniquely identifies a BTF blob loaded into the kernel. type BTFID uint32 // TypeID identifies a type in a BTF blob. type TypeID uint32 // MapFlags control map behaviour. type MapFlags uint32 //go:generate stringer -type MapFlags const ( BPF_F_NO_PREALLOC MapFlags = 1 << iota BPF_F_NO_COMMON_LRU BPF_F_NUMA_NODE BPF_F_RDONLY BPF_F_WRONLY BPF_F_STACK_BUILD_ID BPF_F_ZERO_SEED BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG BPF_F_CLONE BPF_F_MMAPABLE BPF_F_PRESERVE_ELEMS BPF_F_INNER_MAP ) // wrappedErrno wraps syscall.Errno to prevent direct comparisons with // syscall.E* or unix.E* constants. // // You should never export an error of this type. type wrappedErrno struct { syscall.Errno } func (we wrappedErrno) Unwrap() error { return we.Errno } func (we wrappedErrno) Error() string { if we.Errno == ENOTSUPP { return "operation not supported" } return we.Errno.Error() } type syscallError struct { error errno syscall.Errno } func Error(err error, errno syscall.Errno) error { return &syscallError{err, errno} } func (se *syscallError) Is(target error) bool { return target == se.error } func (se *syscallError) Unwrap() error { return se.errno } golang-github-cilium-ebpf-0.11.0/internal/sys/syscall_test.go000066400000000000000000000024251456504511400242140ustar00rootroot00000000000000package sys import ( "errors" "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestObjName(t *testing.T) { name := NewObjName("more_than_16_characters_long") if name[len(name)-1] != 0 { t.Error("NewBPFObjName doesn't null terminate") } if len(name) != unix.BPF_OBJ_NAME_LEN { t.Errorf("Name is %d instead of %d bytes long", len(name), unix.BPF_OBJ_NAME_LEN) } } func TestWrappedErrno(t *testing.T) { a := error(wrappedErrno{unix.EINVAL}) b := error(unix.EINVAL) if a == b { t.Error("wrappedErrno is comparable to plain errno") } if !errors.Is(a, b) { t.Error("errors.Is(wrappedErrno, errno) returns false") } if errors.Is(a, unix.EAGAIN) { t.Error("errors.Is(wrappedErrno, EAGAIN) returns true") } notsupp := wrappedErrno{ENOTSUPP} qt.Assert(t, notsupp.Error(), qt.Contains, "operation not supported") } func TestSyscallError(t *testing.T) { err := errors.New("foo") foo := Error(err, unix.EINVAL) if !errors.Is(foo, unix.EINVAL) { t.Error("SyscallError is not the wrapped errno") } if !errors.Is(foo, err) { t.Error("SyscallError is not the wrapped error") } if errors.Is(unix.EINVAL, foo) { t.Error("Errno is the SyscallError") } if errors.Is(err, foo) { t.Error("Error is the SyscallError") } } golang-github-cilium-ebpf-0.11.0/internal/sys/types.go000066400000000000000000001040541456504511400226500ustar00rootroot00000000000000// Code generated by internal/cmd/gentypes; DO NOT EDIT. package sys import ( "unsafe" ) type AdjRoomMode uint32 const ( BPF_ADJ_ROOM_NET AdjRoomMode = 0 BPF_ADJ_ROOM_MAC AdjRoomMode = 1 ) type AttachType uint32 const ( BPF_CGROUP_INET_INGRESS AttachType = 0 BPF_CGROUP_INET_EGRESS AttachType = 1 BPF_CGROUP_INET_SOCK_CREATE AttachType = 2 BPF_CGROUP_SOCK_OPS AttachType = 3 BPF_SK_SKB_STREAM_PARSER AttachType = 4 BPF_SK_SKB_STREAM_VERDICT AttachType = 5 BPF_CGROUP_DEVICE AttachType = 6 BPF_SK_MSG_VERDICT AttachType = 7 BPF_CGROUP_INET4_BIND AttachType = 8 BPF_CGROUP_INET6_BIND AttachType = 9 BPF_CGROUP_INET4_CONNECT AttachType = 10 BPF_CGROUP_INET6_CONNECT AttachType = 11 BPF_CGROUP_INET4_POST_BIND AttachType = 12 BPF_CGROUP_INET6_POST_BIND AttachType = 13 BPF_CGROUP_UDP4_SENDMSG AttachType = 14 BPF_CGROUP_UDP6_SENDMSG AttachType = 15 BPF_LIRC_MODE2 AttachType = 16 BPF_FLOW_DISSECTOR AttachType = 17 BPF_CGROUP_SYSCTL AttachType = 18 BPF_CGROUP_UDP4_RECVMSG AttachType = 19 BPF_CGROUP_UDP6_RECVMSG AttachType = 20 BPF_CGROUP_GETSOCKOPT AttachType = 21 BPF_CGROUP_SETSOCKOPT AttachType = 22 BPF_TRACE_RAW_TP AttachType = 23 BPF_TRACE_FENTRY AttachType = 24 BPF_TRACE_FEXIT AttachType = 25 BPF_MODIFY_RETURN AttachType = 26 BPF_LSM_MAC AttachType = 27 BPF_TRACE_ITER AttachType = 28 BPF_CGROUP_INET4_GETPEERNAME AttachType = 29 BPF_CGROUP_INET6_GETPEERNAME AttachType = 30 BPF_CGROUP_INET4_GETSOCKNAME AttachType = 31 BPF_CGROUP_INET6_GETSOCKNAME AttachType = 32 BPF_XDP_DEVMAP AttachType = 33 BPF_CGROUP_INET_SOCK_RELEASE AttachType = 34 BPF_XDP_CPUMAP AttachType = 35 BPF_SK_LOOKUP AttachType = 36 BPF_XDP AttachType = 37 BPF_SK_SKB_VERDICT AttachType = 38 BPF_SK_REUSEPORT_SELECT AttachType = 39 BPF_SK_REUSEPORT_SELECT_OR_MIGRATE AttachType = 40 BPF_PERF_EVENT AttachType = 41 BPF_TRACE_KPROBE_MULTI AttachType = 42 __MAX_BPF_ATTACH_TYPE AttachType = 43 ) type Cmd uint32 const ( BPF_MAP_CREATE Cmd = 0 BPF_MAP_LOOKUP_ELEM Cmd = 1 BPF_MAP_UPDATE_ELEM Cmd = 2 BPF_MAP_DELETE_ELEM Cmd = 3 BPF_MAP_GET_NEXT_KEY Cmd = 4 BPF_PROG_LOAD Cmd = 5 BPF_OBJ_PIN Cmd = 6 BPF_OBJ_GET Cmd = 7 BPF_PROG_ATTACH Cmd = 8 BPF_PROG_DETACH Cmd = 9 BPF_PROG_TEST_RUN Cmd = 10 BPF_PROG_RUN Cmd = 10 BPF_PROG_GET_NEXT_ID Cmd = 11 BPF_MAP_GET_NEXT_ID Cmd = 12 BPF_PROG_GET_FD_BY_ID Cmd = 13 BPF_MAP_GET_FD_BY_ID Cmd = 14 BPF_OBJ_GET_INFO_BY_FD Cmd = 15 BPF_PROG_QUERY Cmd = 16 BPF_RAW_TRACEPOINT_OPEN Cmd = 17 BPF_BTF_LOAD Cmd = 18 BPF_BTF_GET_FD_BY_ID Cmd = 19 BPF_TASK_FD_QUERY Cmd = 20 BPF_MAP_LOOKUP_AND_DELETE_ELEM Cmd = 21 BPF_MAP_FREEZE Cmd = 22 BPF_BTF_GET_NEXT_ID Cmd = 23 BPF_MAP_LOOKUP_BATCH Cmd = 24 BPF_MAP_LOOKUP_AND_DELETE_BATCH Cmd = 25 BPF_MAP_UPDATE_BATCH Cmd = 26 BPF_MAP_DELETE_BATCH Cmd = 27 BPF_LINK_CREATE Cmd = 28 BPF_LINK_UPDATE Cmd = 29 BPF_LINK_GET_FD_BY_ID Cmd = 30 BPF_LINK_GET_NEXT_ID Cmd = 31 BPF_ENABLE_STATS Cmd = 32 BPF_ITER_CREATE Cmd = 33 BPF_LINK_DETACH Cmd = 34 BPF_PROG_BIND_MAP Cmd = 35 ) type FunctionId uint32 const ( BPF_FUNC_unspec FunctionId = 0 BPF_FUNC_map_lookup_elem FunctionId = 1 BPF_FUNC_map_update_elem FunctionId = 2 BPF_FUNC_map_delete_elem FunctionId = 3 BPF_FUNC_probe_read FunctionId = 4 BPF_FUNC_ktime_get_ns FunctionId = 5 BPF_FUNC_trace_printk FunctionId = 6 BPF_FUNC_get_prandom_u32 FunctionId = 7 BPF_FUNC_get_smp_processor_id FunctionId = 8 BPF_FUNC_skb_store_bytes FunctionId = 9 BPF_FUNC_l3_csum_replace FunctionId = 10 BPF_FUNC_l4_csum_replace FunctionId = 11 BPF_FUNC_tail_call FunctionId = 12 BPF_FUNC_clone_redirect FunctionId = 13 BPF_FUNC_get_current_pid_tgid FunctionId = 14 BPF_FUNC_get_current_uid_gid FunctionId = 15 BPF_FUNC_get_current_comm FunctionId = 16 BPF_FUNC_get_cgroup_classid FunctionId = 17 BPF_FUNC_skb_vlan_push FunctionId = 18 BPF_FUNC_skb_vlan_pop FunctionId = 19 BPF_FUNC_skb_get_tunnel_key FunctionId = 20 BPF_FUNC_skb_set_tunnel_key FunctionId = 21 BPF_FUNC_perf_event_read FunctionId = 22 BPF_FUNC_redirect FunctionId = 23 BPF_FUNC_get_route_realm FunctionId = 24 BPF_FUNC_perf_event_output FunctionId = 25 BPF_FUNC_skb_load_bytes FunctionId = 26 BPF_FUNC_get_stackid FunctionId = 27 BPF_FUNC_csum_diff FunctionId = 28 BPF_FUNC_skb_get_tunnel_opt FunctionId = 29 BPF_FUNC_skb_set_tunnel_opt FunctionId = 30 BPF_FUNC_skb_change_proto FunctionId = 31 BPF_FUNC_skb_change_type FunctionId = 32 BPF_FUNC_skb_under_cgroup FunctionId = 33 BPF_FUNC_get_hash_recalc FunctionId = 34 BPF_FUNC_get_current_task FunctionId = 35 BPF_FUNC_probe_write_user FunctionId = 36 BPF_FUNC_current_task_under_cgroup FunctionId = 37 BPF_FUNC_skb_change_tail FunctionId = 38 BPF_FUNC_skb_pull_data FunctionId = 39 BPF_FUNC_csum_update FunctionId = 40 BPF_FUNC_set_hash_invalid FunctionId = 41 BPF_FUNC_get_numa_node_id FunctionId = 42 BPF_FUNC_skb_change_head FunctionId = 43 BPF_FUNC_xdp_adjust_head FunctionId = 44 BPF_FUNC_probe_read_str FunctionId = 45 BPF_FUNC_get_socket_cookie FunctionId = 46 BPF_FUNC_get_socket_uid FunctionId = 47 BPF_FUNC_set_hash FunctionId = 48 BPF_FUNC_setsockopt FunctionId = 49 BPF_FUNC_skb_adjust_room FunctionId = 50 BPF_FUNC_redirect_map FunctionId = 51 BPF_FUNC_sk_redirect_map FunctionId = 52 BPF_FUNC_sock_map_update FunctionId = 53 BPF_FUNC_xdp_adjust_meta FunctionId = 54 BPF_FUNC_perf_event_read_value FunctionId = 55 BPF_FUNC_perf_prog_read_value FunctionId = 56 BPF_FUNC_getsockopt FunctionId = 57 BPF_FUNC_override_return FunctionId = 58 BPF_FUNC_sock_ops_cb_flags_set FunctionId = 59 BPF_FUNC_msg_redirect_map FunctionId = 60 BPF_FUNC_msg_apply_bytes FunctionId = 61 BPF_FUNC_msg_cork_bytes FunctionId = 62 BPF_FUNC_msg_pull_data FunctionId = 63 BPF_FUNC_bind FunctionId = 64 BPF_FUNC_xdp_adjust_tail FunctionId = 65 BPF_FUNC_skb_get_xfrm_state FunctionId = 66 BPF_FUNC_get_stack FunctionId = 67 BPF_FUNC_skb_load_bytes_relative FunctionId = 68 BPF_FUNC_fib_lookup FunctionId = 69 BPF_FUNC_sock_hash_update FunctionId = 70 BPF_FUNC_msg_redirect_hash FunctionId = 71 BPF_FUNC_sk_redirect_hash FunctionId = 72 BPF_FUNC_lwt_push_encap FunctionId = 73 BPF_FUNC_lwt_seg6_store_bytes FunctionId = 74 BPF_FUNC_lwt_seg6_adjust_srh FunctionId = 75 BPF_FUNC_lwt_seg6_action FunctionId = 76 BPF_FUNC_rc_repeat FunctionId = 77 BPF_FUNC_rc_keydown FunctionId = 78 BPF_FUNC_skb_cgroup_id FunctionId = 79 BPF_FUNC_get_current_cgroup_id FunctionId = 80 BPF_FUNC_get_local_storage FunctionId = 81 BPF_FUNC_sk_select_reuseport FunctionId = 82 BPF_FUNC_skb_ancestor_cgroup_id FunctionId = 83 BPF_FUNC_sk_lookup_tcp FunctionId = 84 BPF_FUNC_sk_lookup_udp FunctionId = 85 BPF_FUNC_sk_release FunctionId = 86 BPF_FUNC_map_push_elem FunctionId = 87 BPF_FUNC_map_pop_elem FunctionId = 88 BPF_FUNC_map_peek_elem FunctionId = 89 BPF_FUNC_msg_push_data FunctionId = 90 BPF_FUNC_msg_pop_data FunctionId = 91 BPF_FUNC_rc_pointer_rel FunctionId = 92 BPF_FUNC_spin_lock FunctionId = 93 BPF_FUNC_spin_unlock FunctionId = 94 BPF_FUNC_sk_fullsock FunctionId = 95 BPF_FUNC_tcp_sock FunctionId = 96 BPF_FUNC_skb_ecn_set_ce FunctionId = 97 BPF_FUNC_get_listener_sock FunctionId = 98 BPF_FUNC_skc_lookup_tcp FunctionId = 99 BPF_FUNC_tcp_check_syncookie FunctionId = 100 BPF_FUNC_sysctl_get_name FunctionId = 101 BPF_FUNC_sysctl_get_current_value FunctionId = 102 BPF_FUNC_sysctl_get_new_value FunctionId = 103 BPF_FUNC_sysctl_set_new_value FunctionId = 104 BPF_FUNC_strtol FunctionId = 105 BPF_FUNC_strtoul FunctionId = 106 BPF_FUNC_sk_storage_get FunctionId = 107 BPF_FUNC_sk_storage_delete FunctionId = 108 BPF_FUNC_send_signal FunctionId = 109 BPF_FUNC_tcp_gen_syncookie FunctionId = 110 BPF_FUNC_skb_output FunctionId = 111 BPF_FUNC_probe_read_user FunctionId = 112 BPF_FUNC_probe_read_kernel FunctionId = 113 BPF_FUNC_probe_read_user_str FunctionId = 114 BPF_FUNC_probe_read_kernel_str FunctionId = 115 BPF_FUNC_tcp_send_ack FunctionId = 116 BPF_FUNC_send_signal_thread FunctionId = 117 BPF_FUNC_jiffies64 FunctionId = 118 BPF_FUNC_read_branch_records FunctionId = 119 BPF_FUNC_get_ns_current_pid_tgid FunctionId = 120 BPF_FUNC_xdp_output FunctionId = 121 BPF_FUNC_get_netns_cookie FunctionId = 122 BPF_FUNC_get_current_ancestor_cgroup_id FunctionId = 123 BPF_FUNC_sk_assign FunctionId = 124 BPF_FUNC_ktime_get_boot_ns FunctionId = 125 BPF_FUNC_seq_printf FunctionId = 126 BPF_FUNC_seq_write FunctionId = 127 BPF_FUNC_sk_cgroup_id FunctionId = 128 BPF_FUNC_sk_ancestor_cgroup_id FunctionId = 129 BPF_FUNC_ringbuf_output FunctionId = 130 BPF_FUNC_ringbuf_reserve FunctionId = 131 BPF_FUNC_ringbuf_submit FunctionId = 132 BPF_FUNC_ringbuf_discard FunctionId = 133 BPF_FUNC_ringbuf_query FunctionId = 134 BPF_FUNC_csum_level FunctionId = 135 BPF_FUNC_skc_to_tcp6_sock FunctionId = 136 BPF_FUNC_skc_to_tcp_sock FunctionId = 137 BPF_FUNC_skc_to_tcp_timewait_sock FunctionId = 138 BPF_FUNC_skc_to_tcp_request_sock FunctionId = 139 BPF_FUNC_skc_to_udp6_sock FunctionId = 140 BPF_FUNC_get_task_stack FunctionId = 141 BPF_FUNC_load_hdr_opt FunctionId = 142 BPF_FUNC_store_hdr_opt FunctionId = 143 BPF_FUNC_reserve_hdr_opt FunctionId = 144 BPF_FUNC_inode_storage_get FunctionId = 145 BPF_FUNC_inode_storage_delete FunctionId = 146 BPF_FUNC_d_path FunctionId = 147 BPF_FUNC_copy_from_user FunctionId = 148 BPF_FUNC_snprintf_btf FunctionId = 149 BPF_FUNC_seq_printf_btf FunctionId = 150 BPF_FUNC_skb_cgroup_classid FunctionId = 151 BPF_FUNC_redirect_neigh FunctionId = 152 BPF_FUNC_per_cpu_ptr FunctionId = 153 BPF_FUNC_this_cpu_ptr FunctionId = 154 BPF_FUNC_redirect_peer FunctionId = 155 BPF_FUNC_task_storage_get FunctionId = 156 BPF_FUNC_task_storage_delete FunctionId = 157 BPF_FUNC_get_current_task_btf FunctionId = 158 BPF_FUNC_bprm_opts_set FunctionId = 159 BPF_FUNC_ktime_get_coarse_ns FunctionId = 160 BPF_FUNC_ima_inode_hash FunctionId = 161 BPF_FUNC_sock_from_file FunctionId = 162 BPF_FUNC_check_mtu FunctionId = 163 BPF_FUNC_for_each_map_elem FunctionId = 164 BPF_FUNC_snprintf FunctionId = 165 BPF_FUNC_sys_bpf FunctionId = 166 BPF_FUNC_btf_find_by_name_kind FunctionId = 167 BPF_FUNC_sys_close FunctionId = 168 BPF_FUNC_timer_init FunctionId = 169 BPF_FUNC_timer_set_callback FunctionId = 170 BPF_FUNC_timer_start FunctionId = 171 BPF_FUNC_timer_cancel FunctionId = 172 BPF_FUNC_get_func_ip FunctionId = 173 BPF_FUNC_get_attach_cookie FunctionId = 174 BPF_FUNC_task_pt_regs FunctionId = 175 BPF_FUNC_get_branch_snapshot FunctionId = 176 BPF_FUNC_trace_vprintk FunctionId = 177 BPF_FUNC_skc_to_unix_sock FunctionId = 178 BPF_FUNC_kallsyms_lookup_name FunctionId = 179 BPF_FUNC_find_vma FunctionId = 180 BPF_FUNC_loop FunctionId = 181 BPF_FUNC_strncmp FunctionId = 182 BPF_FUNC_get_func_arg FunctionId = 183 BPF_FUNC_get_func_ret FunctionId = 184 BPF_FUNC_get_func_arg_cnt FunctionId = 185 BPF_FUNC_get_retval FunctionId = 186 BPF_FUNC_set_retval FunctionId = 187 BPF_FUNC_xdp_get_buff_len FunctionId = 188 BPF_FUNC_xdp_load_bytes FunctionId = 189 BPF_FUNC_xdp_store_bytes FunctionId = 190 BPF_FUNC_copy_from_user_task FunctionId = 191 BPF_FUNC_skb_set_tstamp FunctionId = 192 BPF_FUNC_ima_file_hash FunctionId = 193 BPF_FUNC_kptr_xchg FunctionId = 194 BPF_FUNC_map_lookup_percpu_elem FunctionId = 195 BPF_FUNC_skc_to_mptcp_sock FunctionId = 196 BPF_FUNC_dynptr_from_mem FunctionId = 197 BPF_FUNC_ringbuf_reserve_dynptr FunctionId = 198 BPF_FUNC_ringbuf_submit_dynptr FunctionId = 199 BPF_FUNC_ringbuf_discard_dynptr FunctionId = 200 BPF_FUNC_dynptr_read FunctionId = 201 BPF_FUNC_dynptr_write FunctionId = 202 BPF_FUNC_dynptr_data FunctionId = 203 __BPF_FUNC_MAX_ID FunctionId = 204 ) type HdrStartOff uint32 const ( BPF_HDR_START_MAC HdrStartOff = 0 BPF_HDR_START_NET HdrStartOff = 1 ) type LinkType uint32 const ( BPF_LINK_TYPE_UNSPEC LinkType = 0 BPF_LINK_TYPE_RAW_TRACEPOINT LinkType = 1 BPF_LINK_TYPE_TRACING LinkType = 2 BPF_LINK_TYPE_CGROUP LinkType = 3 BPF_LINK_TYPE_ITER LinkType = 4 BPF_LINK_TYPE_NETNS LinkType = 5 BPF_LINK_TYPE_XDP LinkType = 6 BPF_LINK_TYPE_PERF_EVENT LinkType = 7 BPF_LINK_TYPE_KPROBE_MULTI LinkType = 8 BPF_LINK_TYPE_STRUCT_OPS LinkType = 9 MAX_BPF_LINK_TYPE LinkType = 10 ) type MapType uint32 const ( BPF_MAP_TYPE_UNSPEC MapType = 0 BPF_MAP_TYPE_HASH MapType = 1 BPF_MAP_TYPE_ARRAY MapType = 2 BPF_MAP_TYPE_PROG_ARRAY MapType = 3 BPF_MAP_TYPE_PERF_EVENT_ARRAY MapType = 4 BPF_MAP_TYPE_PERCPU_HASH MapType = 5 BPF_MAP_TYPE_PERCPU_ARRAY MapType = 6 BPF_MAP_TYPE_STACK_TRACE MapType = 7 BPF_MAP_TYPE_CGROUP_ARRAY MapType = 8 BPF_MAP_TYPE_LRU_HASH MapType = 9 BPF_MAP_TYPE_LRU_PERCPU_HASH MapType = 10 BPF_MAP_TYPE_LPM_TRIE MapType = 11 BPF_MAP_TYPE_ARRAY_OF_MAPS MapType = 12 BPF_MAP_TYPE_HASH_OF_MAPS MapType = 13 BPF_MAP_TYPE_DEVMAP MapType = 14 BPF_MAP_TYPE_SOCKMAP MapType = 15 BPF_MAP_TYPE_CPUMAP MapType = 16 BPF_MAP_TYPE_XSKMAP MapType = 17 BPF_MAP_TYPE_SOCKHASH MapType = 18 BPF_MAP_TYPE_CGROUP_STORAGE MapType = 19 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY MapType = 20 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE MapType = 21 BPF_MAP_TYPE_QUEUE MapType = 22 BPF_MAP_TYPE_STACK MapType = 23 BPF_MAP_TYPE_SK_STORAGE MapType = 24 BPF_MAP_TYPE_DEVMAP_HASH MapType = 25 BPF_MAP_TYPE_STRUCT_OPS MapType = 26 BPF_MAP_TYPE_RINGBUF MapType = 27 BPF_MAP_TYPE_INODE_STORAGE MapType = 28 BPF_MAP_TYPE_TASK_STORAGE MapType = 29 BPF_MAP_TYPE_BLOOM_FILTER MapType = 30 ) type ProgType uint32 const ( BPF_PROG_TYPE_UNSPEC ProgType = 0 BPF_PROG_TYPE_SOCKET_FILTER ProgType = 1 BPF_PROG_TYPE_KPROBE ProgType = 2 BPF_PROG_TYPE_SCHED_CLS ProgType = 3 BPF_PROG_TYPE_SCHED_ACT ProgType = 4 BPF_PROG_TYPE_TRACEPOINT ProgType = 5 BPF_PROG_TYPE_XDP ProgType = 6 BPF_PROG_TYPE_PERF_EVENT ProgType = 7 BPF_PROG_TYPE_CGROUP_SKB ProgType = 8 BPF_PROG_TYPE_CGROUP_SOCK ProgType = 9 BPF_PROG_TYPE_LWT_IN ProgType = 10 BPF_PROG_TYPE_LWT_OUT ProgType = 11 BPF_PROG_TYPE_LWT_XMIT ProgType = 12 BPF_PROG_TYPE_SOCK_OPS ProgType = 13 BPF_PROG_TYPE_SK_SKB ProgType = 14 BPF_PROG_TYPE_CGROUP_DEVICE ProgType = 15 BPF_PROG_TYPE_SK_MSG ProgType = 16 BPF_PROG_TYPE_RAW_TRACEPOINT ProgType = 17 BPF_PROG_TYPE_CGROUP_SOCK_ADDR ProgType = 18 BPF_PROG_TYPE_LWT_SEG6LOCAL ProgType = 19 BPF_PROG_TYPE_LIRC_MODE2 ProgType = 20 BPF_PROG_TYPE_SK_REUSEPORT ProgType = 21 BPF_PROG_TYPE_FLOW_DISSECTOR ProgType = 22 BPF_PROG_TYPE_CGROUP_SYSCTL ProgType = 23 BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE ProgType = 24 BPF_PROG_TYPE_CGROUP_SOCKOPT ProgType = 25 BPF_PROG_TYPE_TRACING ProgType = 26 BPF_PROG_TYPE_STRUCT_OPS ProgType = 27 BPF_PROG_TYPE_EXT ProgType = 28 BPF_PROG_TYPE_LSM ProgType = 29 BPF_PROG_TYPE_SK_LOOKUP ProgType = 30 BPF_PROG_TYPE_SYSCALL ProgType = 31 ) type RetCode uint32 const ( BPF_OK RetCode = 0 BPF_DROP RetCode = 2 BPF_REDIRECT RetCode = 7 BPF_LWT_REROUTE RetCode = 128 ) type SkAction uint32 const ( SK_DROP SkAction = 0 SK_PASS SkAction = 1 ) type StackBuildIdStatus uint32 const ( BPF_STACK_BUILD_ID_EMPTY StackBuildIdStatus = 0 BPF_STACK_BUILD_ID_VALID StackBuildIdStatus = 1 BPF_STACK_BUILD_ID_IP StackBuildIdStatus = 2 ) type StatsType uint32 const ( BPF_STATS_RUN_TIME StatsType = 0 ) type XdpAction uint32 const ( XDP_ABORTED XdpAction = 0 XDP_DROP XdpAction = 1 XDP_PASS XdpAction = 2 XDP_TX XdpAction = 3 XDP_REDIRECT XdpAction = 4 ) type BtfInfo struct { Btf Pointer BtfSize uint32 Id BTFID Name Pointer NameLen uint32 KernelBtf uint32 } type FuncInfo struct { InsnOff uint32 TypeId uint32 } type LineInfo struct { InsnOff uint32 FileNameOff uint32 LineOff uint32 LineCol uint32 } type LinkInfo struct { Type LinkType Id LinkID ProgId uint32 _ [4]byte Extra [16]uint8 } type MapInfo struct { Type uint32 Id uint32 KeySize uint32 ValueSize uint32 MaxEntries uint32 MapFlags MapFlags Name ObjName Ifindex uint32 BtfVmlinuxValueTypeId TypeID NetnsDev uint64 NetnsIno uint64 BtfId uint32 BtfKeyTypeId TypeID BtfValueTypeId TypeID _ [4]byte MapExtra uint64 } type ProgInfo struct { Type uint32 Id uint32 Tag [8]uint8 JitedProgLen uint32 XlatedProgLen uint32 JitedProgInsns uint64 XlatedProgInsns Pointer LoadTime uint64 CreatedByUid uint32 NrMapIds uint32 MapIds Pointer Name ObjName Ifindex uint32 _ [4]byte /* unsupported bitfield */ NetnsDev uint64 NetnsIno uint64 NrJitedKsyms uint32 NrJitedFuncLens uint32 JitedKsyms uint64 JitedFuncLens uint64 BtfId BTFID FuncInfoRecSize uint32 FuncInfo uint64 NrFuncInfo uint32 NrLineInfo uint32 LineInfo uint64 JitedLineInfo uint64 NrJitedLineInfo uint32 LineInfoRecSize uint32 JitedLineInfoRecSize uint32 NrProgTags uint32 ProgTags uint64 RunTimeNs uint64 RunCnt uint64 RecursionMisses uint64 VerifiedInsns uint32 _ [4]byte } type SkLookup struct { Cookie uint64 Family uint32 Protocol uint32 RemoteIp4 [4]uint8 RemoteIp6 [16]uint8 RemotePort uint16 _ [2]byte LocalIp4 [4]uint8 LocalIp6 [16]uint8 LocalPort uint32 IngressIfindex uint32 _ [4]byte } type XdpMd struct { Data uint32 DataEnd uint32 DataMeta uint32 IngressIfindex uint32 RxQueueIndex uint32 EgressIfindex uint32 } type BtfGetFdByIdAttr struct{ Id uint32 } func BtfGetFdById(attr *BtfGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_BTF_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type BtfGetNextIdAttr struct { Id BTFID NextId BTFID } func BtfGetNextId(attr *BtfGetNextIdAttr) error { _, err := BPF(BPF_BTF_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type BtfLoadAttr struct { Btf Pointer BtfLogBuf Pointer BtfSize uint32 BtfLogSize uint32 BtfLogLevel uint32 _ [4]byte } func BtfLoad(attr *BtfLoadAttr) (*FD, error) { fd, err := BPF(BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type EnableStatsAttr struct{ Type uint32 } func EnableStats(attr *EnableStatsAttr) (*FD, error) { fd, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type IterCreateAttr struct { LinkFd uint32 Flags uint32 } func IterCreate(attr *IterCreateAttr) (*FD, error) { fd, err := BPF(BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateAttr struct { ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 TargetBtfId TypeID _ [28]byte } func LinkCreate(attr *LinkCreateAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateIterAttr struct { ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 IterInfo Pointer IterInfoLen uint32 _ [20]byte } func LinkCreateIter(attr *LinkCreateIterAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateKprobeMultiAttr struct { ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 KprobeMultiFlags uint32 Count uint32 Syms Pointer Addrs Pointer Cookies Pointer } func LinkCreateKprobeMulti(attr *LinkCreateKprobeMultiAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreatePerfEventAttr struct { ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 BpfCookie uint64 _ [24]byte } func LinkCreatePerfEvent(attr *LinkCreatePerfEventAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateTracingAttr struct { ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 TargetBtfId BTFID _ [4]byte Cookie uint64 _ [16]byte } func LinkCreateTracing(attr *LinkCreateTracingAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkUpdateAttr struct { LinkFd uint32 NewProgFd uint32 Flags uint32 OldProgFd uint32 } func LinkUpdate(attr *LinkUpdateAttr) error { _, err := BPF(BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapCreateAttr struct { MapType MapType KeySize uint32 ValueSize uint32 MaxEntries uint32 MapFlags MapFlags InnerMapFd uint32 NumaNode uint32 MapName ObjName MapIfindex uint32 BtfFd uint32 BtfKeyTypeId TypeID BtfValueTypeId TypeID BtfVmlinuxValueTypeId TypeID MapExtra uint64 } func MapCreate(attr *MapCreateAttr) (*FD, error) { fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type MapDeleteBatchAttr struct { InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapDeleteBatch(attr *MapDeleteBatchAttr) error { _, err := BPF(BPF_MAP_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapDeleteElemAttr struct { MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapDeleteElem(attr *MapDeleteElemAttr) error { _, err := BPF(BPF_MAP_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapFreezeAttr struct{ MapFd uint32 } func MapFreeze(attr *MapFreezeAttr) error { _, err := BPF(BPF_MAP_FREEZE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapGetFdByIdAttr struct{ Id uint32 } func MapGetFdById(attr *MapGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_MAP_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type MapGetNextIdAttr struct { Id uint32 NextId uint32 } func MapGetNextId(attr *MapGetNextIdAttr) error { _, err := BPF(BPF_MAP_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapGetNextKeyAttr struct { MapFd uint32 _ [4]byte Key Pointer NextKey Pointer } func MapGetNextKey(attr *MapGetNextKeyAttr) error { _, err := BPF(BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupAndDeleteBatchAttr struct { InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapLookupAndDeleteBatch(attr *MapLookupAndDeleteBatchAttr) error { _, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupAndDeleteElemAttr struct { MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapLookupAndDeleteElem(attr *MapLookupAndDeleteElemAttr) error { _, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupBatchAttr struct { InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapLookupBatch(attr *MapLookupBatchAttr) error { _, err := BPF(BPF_MAP_LOOKUP_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupElemAttr struct { MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapLookupElem(attr *MapLookupElemAttr) error { _, err := BPF(BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapUpdateBatchAttr struct { InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapUpdateBatch(attr *MapUpdateBatchAttr) error { _, err := BPF(BPF_MAP_UPDATE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapUpdateElemAttr struct { MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapUpdateElem(attr *MapUpdateElemAttr) error { _, err := BPF(BPF_MAP_UPDATE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ObjGetAttr struct { Pathname Pointer BpfFd uint32 FileFlags uint32 } func ObjGet(attr *ObjGetAttr) (*FD, error) { fd, err := BPF(BPF_OBJ_GET, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ObjGetInfoByFdAttr struct { BpfFd uint32 InfoLen uint32 Info Pointer } func ObjGetInfoByFd(attr *ObjGetInfoByFdAttr) error { _, err := BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ObjPinAttr struct { Pathname Pointer BpfFd uint32 FileFlags uint32 } func ObjPin(attr *ObjPinAttr) error { _, err := BPF(BPF_OBJ_PIN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgAttachAttr struct { TargetFd uint32 AttachBpfFd uint32 AttachType uint32 AttachFlags uint32 ReplaceBpfFd uint32 } func ProgAttach(attr *ProgAttachAttr) error { _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgBindMapAttr struct { ProgFd uint32 MapFd uint32 Flags uint32 } func ProgBindMap(attr *ProgBindMapAttr) error { _, err := BPF(BPF_PROG_BIND_MAP, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgDetachAttr struct { TargetFd uint32 AttachBpfFd uint32 AttachType uint32 } func ProgDetach(attr *ProgDetachAttr) error { _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgGetFdByIdAttr struct{ Id uint32 } func ProgGetFdById(attr *ProgGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_PROG_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ProgGetNextIdAttr struct { Id uint32 NextId uint32 } func ProgGetNextId(attr *ProgGetNextIdAttr) error { _, err := BPF(BPF_PROG_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgLoadAttr struct { ProgType ProgType InsnCnt uint32 Insns Pointer License Pointer LogLevel LogLevel LogSize uint32 LogBuf Pointer KernVersion uint32 ProgFlags uint32 ProgName ObjName ProgIfindex uint32 ExpectedAttachType AttachType ProgBtfFd uint32 FuncInfoRecSize uint32 FuncInfo Pointer FuncInfoCnt uint32 LineInfoRecSize uint32 LineInfo Pointer LineInfoCnt uint32 AttachBtfId TypeID AttachBtfObjFd uint32 CoreReloCnt uint32 FdArray Pointer CoreRelos Pointer CoreReloRecSize uint32 _ [4]byte } func ProgLoad(attr *ProgLoadAttr) (*FD, error) { fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ProgQueryAttr struct { TargetFd uint32 AttachType AttachType QueryFlags uint32 AttachFlags uint32 ProgIds Pointer ProgCount uint32 _ [4]byte } func ProgQuery(attr *ProgQueryAttr) error { _, err := BPF(BPF_PROG_QUERY, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgRunAttr struct { ProgFd uint32 Retval uint32 DataSizeIn uint32 DataSizeOut uint32 DataIn Pointer DataOut Pointer Repeat uint32 Duration uint32 CtxSizeIn uint32 CtxSizeOut uint32 CtxIn Pointer CtxOut Pointer Flags uint32 Cpu uint32 BatchSize uint32 _ [4]byte } func ProgRun(attr *ProgRunAttr) error { _, err := BPF(BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type RawTracepointOpenAttr struct { Name Pointer ProgFd uint32 _ [4]byte } func RawTracepointOpen(attr *RawTracepointOpenAttr) (*FD, error) { fd, err := BPF(BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type CgroupLinkInfo struct { CgroupId uint64 AttachType AttachType _ [4]byte } type IterLinkInfo struct { TargetName Pointer TargetNameLen uint32 } type NetNsLinkInfo struct { NetnsIno uint32 AttachType AttachType } type RawTracepointLinkInfo struct { TpName Pointer TpNameLen uint32 _ [4]byte } type TracingLinkInfo struct { AttachType AttachType TargetObjId uint32 TargetBtfId TypeID } type XDPLinkInfo struct{ Ifindex uint32 } golang-github-cilium-ebpf-0.11.0/internal/testdata/000077500000000000000000000000001456504511400221445ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/testdata/errno524.log000066400000000000000000000002101456504511400242200ustar00rootroot00000000000000JIT doesn't support bpf-to-bpf calls processed 39 insns (limit 1000000) max_states_per_insn 1 total_states 3 peak_states 3 mark_read 2 golang-github-cilium-ebpf-0.11.0/internal/testdata/invalid-R0.log000066400000000000000000000002371456504511400245560ustar00rootroot000000000000000: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (95) exit R0 !read_ok processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 golang-github-cilium-ebpf-0.11.0/internal/testdata/invalid-ctx-access.log000066400000000000000000000005131456504511400263270ustar00rootroot00000000000000arg#0 type is not a struct Unrecognized arg#0 type PTR ; int BPF_PROG(sys_recvfrom, struct pt_regs *regs) { 0: (79) r6 = *(u64 *)(r1 +0) func '__x64_sys_recvfrom' arg0 type FWD is not a struct invalid bpf_context access off=0 size=8 processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 golang-github-cilium-ebpf-0.11.0/internal/testdata/invalid-member.log000066400000000000000000000016231456504511400255440ustar00rootroot00000000000000CO-RE tracer failed: failed to load ebpf program: field MountEntry: program mount_entry: map .rodata: load BTF: invalid argument: magic: 0xeb9f version: 1 flags: 0x0 hdr_len: 24 type_off: 0 type_len: 16468 str_off: 16468 str_len: 12649 btf_total_size: 29141 [431] FWD rt_mutex_waiter struct [432] FWD sched_class struct [433] FWD seccomp_filter struct [434] FWD sem_undo_list struct [435] FWD sighand_struct struct [436] FWD signal_struct struct [437] FWD task_delay_info struct [438] FWD task_group struct [439] FWD time_namespace struct [440] FWD trace_event_call struct [441] FWD ucounts struct [442] FWD uprobe_task struct [443] FWD user_namespace struct [444] FWD userfaultfd_ctx struct [445] FWD uts_namespace struct [446] FWD vm_operations_struct struct [447] FWD vm_struct struct [52] STRUCT task_struct size=7744 vlen=218 cpus_mask type_id=109 bitfield_size=0 bits_offset=7744 Invalid member golang-github-cilium-ebpf-0.11.0/internal/testdata/issue-43.log000066400000000000000000000014251456504511400242250ustar00rootroot00000000000000magic: 0xeb9f version: 1 flags: 0x0 hdr_len: 24 type_off: 0 type_len: 536 str_off: 536 str_len: 582 btf_total_size: 1142 [1] STRUCT (anon) size=40 vlen=5 type type_id=2 bits_offset=0 key type_id=6 bits_offset=64 value type_id=6 bits_offset=128 max_entries type_id=2 bits_offset=192 map_flags type_id=2 bits_offset=256 [2] PTR (anon) type_id=4 [3] INT int size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [4] ARRAY (anon) type_id=3 index_type_id=5 nr_elems=1 [5] INT __ARRAY_SIZE_TYPE__ size=4 bits_offset=0 nr_bits=32 encoding=(none) [6] PTR (anon) type_id=7 [7] TYPEDEF uint32_t type_id=8 [8] INT unsigned int size=4 bits_offset=0 nr_bits=32 encoding=(none) [9] VAR btf_map type_id=1 linkage=1 [10] FUNC_PROTO (anon) return=3 args=(3 arg) [11] FUNC helper_func2 type_id=10 vlen != 0 golang-github-cilium-ebpf-0.11.0/internal/testutils/000077500000000000000000000000001456504511400223735ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/testutils/bpffs.go000066400000000000000000000006441456504511400240260ustar00rootroot00000000000000package testutils import ( "os" "testing" ) // TempBPFFS creates a temporary directory on a BPF FS. // // The directory is automatically cleaned up at the end of the test run. func TempBPFFS(tb testing.TB) string { tb.Helper() tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test") if err != nil { tb.Fatal("Create temporary directory on BPFFS:", err) } tb.Cleanup(func() { os.RemoveAll(tmp) }) return tmp } golang-github-cilium-ebpf-0.11.0/internal/testutils/cgroup.go000066400000000000000000000021601456504511400242200ustar00rootroot00000000000000package testutils import ( "errors" "os" "strings" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var cgroup2Path = internal.Memoize(func() (string, error) { mounts, err := os.ReadFile("/proc/mounts") if err != nil { return "", err } for _, line := range strings.Split(string(mounts), "\n") { mount := strings.SplitN(line, " ", 3) if mount[0] == "cgroup2" { return mount[1], nil } continue } return "", errors.New("cgroup2 not mounted") }) func CreateCgroup(tb testing.TB) *os.File { tb.Helper() cg2, err := cgroup2Path() if err != nil { tb.Fatal("Can't locate cgroup2 mount:", err) } cgdir, err := os.MkdirTemp(cg2, "ebpf-link") if err != nil { tb.Fatal("Can't create cgroupv2:", err) } cgroup, err := os.Open(cgdir) if err != nil { os.Remove(cgdir) tb.Fatal(err) } tb.Cleanup(func() { cgroup.Close() os.Remove(cgdir) }) return cgroup } func GetCgroupIno(t *testing.T, cgroup *os.File) uint64 { cgroupStat := unix.Stat_t{} err := unix.Fstat(int(cgroup.Fd()), &cgroupStat) if err != nil { t.Fatal(err) } return cgroupStat.Ino } golang-github-cilium-ebpf-0.11.0/internal/testutils/fdtrace/000077500000000000000000000000001456504511400240035ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/testutils/fdtrace/fd.go000066400000000000000000000013361456504511400247260ustar00rootroot00000000000000package fdtrace import ( "fmt" "os" "runtime" "testing" "github.com/cilium/ebpf/internal/sys" ) // TestMain runs m with sys.FD leak tracing enabled. func TestMain(m *testing.M) { // fn can either be invoked asynchronously by the gc or during disabling of // the leak tracer below. Don't terminate the program immediately, instead // capture a boolean that will be used to set the exit code. This avoids races // and gives all events the chance to be written to stderr. var leak bool sys.OnLeakFD(func(fs *runtime.Frames) { fmt.Fprintln(os.Stderr, "leaked fd created at:") fmt.Fprintln(os.Stderr, sys.FormatFrames(fs)) leak = true }) ret := m.Run() sys.OnLeakFD(nil) if leak { ret = 99 } os.Exit(ret) } golang-github-cilium-ebpf-0.11.0/internal/testutils/feature.go000066400000000000000000000062051456504511400243600ustar00rootroot00000000000000package testutils import ( "errors" "os" "strings" "testing" "github.com/cilium/ebpf/internal" ) const ( ignoreKernelVersionEnvVar = "EBPF_TEST_IGNORE_KERNEL_VERSION" ) func CheckFeatureTest(t *testing.T, fn func() error) { checkFeatureTestError(t, fn()) } func checkFeatureTestError(t *testing.T, err error) { if err == nil { return } var ufe *internal.UnsupportedFeatureError if errors.As(err, &ufe) { if ignoreKernelVersionCheck(t.Name()) { t.Skipf("Ignoring error due to %s: %s", ignoreKernelVersionEnvVar, ufe.Error()) } else { checkKernelVersion(t, ufe) } } else { t.Error("Feature test failed:", err) } } func CheckFeatureMatrix[K comparable](t *testing.T, fm internal.FeatureMatrix[K]) { t.Helper() for key, ft := range fm { t.Run(ft.Name, func(t *testing.T) { checkFeatureTestError(t, fm.Result(key)) }) } } func SkipIfNotSupported(tb testing.TB, err error) { tb.Helper() if err == internal.ErrNotSupported { tb.Fatal("Unwrapped ErrNotSupported") } var ufe *internal.UnsupportedFeatureError if errors.As(err, &ufe) { checkKernelVersion(tb, ufe) tb.Skip(ufe.Error()) } if errors.Is(err, internal.ErrNotSupported) { tb.Skip(err.Error()) } } func checkKernelVersion(tb testing.TB, ufe *internal.UnsupportedFeatureError) { if ufe.MinimumVersion.Unspecified() { return } if !isKernelLessThan(tb, ufe.MinimumVersion) { tb.Helper() tb.Fatalf("Feature '%s' isn't supported even though kernel is newer than %s", ufe.Name, ufe.MinimumVersion) } } func SkipOnOldKernel(tb testing.TB, minVersion, feature string) { tb.Helper() if IsKernelLessThan(tb, minVersion) { tb.Skipf("Test requires at least kernel %s (due to missing %s)", minVersion, feature) } } func IsKernelLessThan(tb testing.TB, minVersion string) bool { tb.Helper() minv, err := internal.NewVersion(minVersion) if err != nil { tb.Fatalf("Invalid version %s: %s", minVersion, err) } return isKernelLessThan(tb, minv) } func isKernelLessThan(tb testing.TB, minv internal.Version) bool { tb.Helper() if max := os.Getenv("CI_MAX_KERNEL_VERSION"); max != "" { maxv, err := internal.NewVersion(max) if err != nil { tb.Fatalf("Invalid version %q in CI_MAX_KERNEL_VERSION: %s", max, err) } if maxv.Less(minv) { tb.Fatalf("Test for %s will never execute on CI since %s is the most recent kernel", minv, maxv) } } return kernelVersion(tb).Less(minv) } func kernelVersion(tb testing.TB) internal.Version { tb.Helper() v, err := internal.KernelVersion() if err != nil { tb.Fatal(err) } return v } // ignoreKernelVersionCheck checks if test name should be ignored for kernel version check by checking against environment var EBPF_TEST_IGNORE_KERNEL_VERSION. // EBPF_TEST_IGNORE_KERNEL_VERSION is a comma (,) separated list of test names for which kernel version check should be ignored. // // eg: EBPF_TEST_IGNORE_KERNEL_VERSION=TestABC,TestXYZ func ignoreKernelVersionCheck(tName string) bool { tNames := os.Getenv(ignoreKernelVersionEnvVar) if tNames == "" { return false } ignored := strings.Split(tNames, ",") for _, n := range ignored { if strings.TrimSpace(n) == tName { return true } } return false } golang-github-cilium-ebpf-0.11.0/internal/testutils/feature_test.go000066400000000000000000000034471456504511400254240ustar00rootroot00000000000000package testutils import ( "testing" ) func TestIgnoreKernelVersionCheckWhenEnvVarIsSet(t *testing.T) { tests := []struct { name string toIgnoreNamesEnvValue string testName string ignoreKernelVersionCheck bool }{ { name: "should NOT ignore kernel version check if environment var set to empty string", toIgnoreNamesEnvValue: "", testName: "TestABC", ignoreKernelVersionCheck: false, }, { name: "should ignore kernel version check if environment var set to skip test name with single value", toIgnoreNamesEnvValue: "TestABC", testName: "TestABC", ignoreKernelVersionCheck: true, }, { name: "should match test name when multiple comma separated names list is provided", toIgnoreNamesEnvValue: "TestABC,TestXYZ", testName: "TestXYZ", ignoreKernelVersionCheck: true, }, { name: "should NOT match test name when multiple comma separated names list is provided but name is not present in list", toIgnoreNamesEnvValue: "TestABC,TestXYZ", testName: "TestPQR", ignoreKernelVersionCheck: false, }, { name: "should match test name if names list has leading/trailing spaces", toIgnoreNamesEnvValue: "TestABC, TestXYZ , TestPQR", testName: "TestXYZ", ignoreKernelVersionCheck: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Setenv(ignoreKernelVersionEnvVar, tt.toIgnoreNamesEnvValue) if got := ignoreKernelVersionCheck(tt.testName); got != tt.ignoreKernelVersionCheck { t.Errorf("ignoreKernelVersionCheck() = %v, want %v", got, tt.ignoreKernelVersionCheck) } }) } } golang-github-cilium-ebpf-0.11.0/internal/testutils/glob.go000066400000000000000000000022511456504511400236450ustar00rootroot00000000000000package testutils import ( "path/filepath" "testing" ) // Files calls fn for each given file. // // The function errors out if the pattern matches no files. func Files(t *testing.T, files []string, fn func(*testing.T, string)) { t.Helper() if len(files) == 0 { t.Fatalf("No files given") } for _, f := range files { file := f // force copy name := filepath.Base(file) t.Run(name, func(t *testing.T) { fn(t, file) }) } } // Glob finds files matching a pattern. // // The pattern should may include full path. Excludes use the same syntax as // pattern, but are only applied to the basename instead of the full path. func Glob(tb testing.TB, pattern string, excludes ...string) []string { tb.Helper() files, err := filepath.Glob(pattern) if err != nil { tb.Fatal("Can't glob files:", err) } if len(excludes) == 0 { return files } var filtered []string nextFile: for _, file := range files { base := filepath.Base(file) for _, exclude := range excludes { if matched, err := filepath.Match(exclude, base); err != nil { tb.Fatal(err) } else if matched { continue nextFile } } filtered = append(filtered, file) } return filtered } golang-github-cilium-ebpf-0.11.0/internal/testutils/rlimit.go000066400000000000000000000005651456504511400242300ustar00rootroot00000000000000package testutils import ( "fmt" "os" "github.com/cilium/ebpf/rlimit" ) func init() { // Increase the memlock for all tests unconditionally. It's a great source of // weird bugs, since different distros have different default limits. if err := rlimit.RemoveMemlock(); err != nil { fmt.Fprintln(os.Stderr, "WARNING: Failed to adjust rlimit, tests may fail") } } golang-github-cilium-ebpf-0.11.0/internal/testutils/seed.go000066400000000000000000000005031456504511400236400ustar00rootroot00000000000000package testutils import ( "fmt" "math/rand" "sync" "time" ) var randSeed struct { value int64 once sync.Once } func Rand() *rand.Rand { randSeed.once.Do(func() { randSeed.value = time.Now().UnixMicro() fmt.Printf("Random seed is %d\n", randSeed.value) }) return rand.New(rand.NewSource(randSeed.value)) } golang-github-cilium-ebpf-0.11.0/internal/tracefs/000077500000000000000000000000001456504511400217625ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/tracefs/kprobe.go000066400000000000000000000250171456504511400236000ustar00rootroot00000000000000package tracefs import ( "crypto/rand" "errors" "fmt" "os" "path/filepath" "runtime" "strings" "syscall" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( ErrInvalidInput = errors.New("invalid input") ErrInvalidMaxActive = errors.New("can only set maxactive on kretprobes") ) //go:generate stringer -type=ProbeType -linecomment type ProbeType uint8 const ( Kprobe ProbeType = iota // kprobe Uprobe // uprobe ) func (pt ProbeType) eventsFile() (*os.File, error) { path, err := sanitizeTracefsPath(fmt.Sprintf("%s_events", pt.String())) if err != nil { return nil, err } return os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0666) } type ProbeArgs struct { Type ProbeType Symbol, Group, Path string Offset, RefCtrOffset, Cookie uint64 Pid, RetprobeMaxActive int Ret bool } // RandomGroup generates a pseudorandom string for use as a tracefs group name. // Returns an error when the output string would exceed 63 characters (kernel // limitation), when rand.Read() fails or when prefix contains characters not // allowed by IsValidTraceID. func RandomGroup(prefix string) (string, error) { if !validIdentifier(prefix) { return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, ErrInvalidInput) } b := make([]byte, 8) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("reading random bytes: %w", err) } group := fmt.Sprintf("%s_%x", prefix, b) if len(group) > 63 { return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, ErrInvalidInput) } return group, nil } // validIdentifier implements the equivalent of a regex match // against "^[a-zA-Z_][0-9a-zA-Z_]*$". // // Trace event groups, names and kernel symbols must adhere to this set // of characters. Non-empty, first character must not be a number, all // characters must be alphanumeric or underscore. func validIdentifier(s string) bool { if len(s) < 1 { return false } for i, c := range []byte(s) { switch { case c >= 'a' && c <= 'z': case c >= 'A' && c <= 'Z': case c == '_': case i > 0 && c >= '0' && c <= '9': default: return false } } return true } func sanitizeTracefsPath(path ...string) (string, error) { base, err := getTracefsPath() if err != nil { return "", err } l := filepath.Join(path...) p := filepath.Join(base, l) if !strings.HasPrefix(p, base) { return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, ErrInvalidInput) } return p, nil } // getTracefsPath will return a correct path to the tracefs mount point. // Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing, // but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted. // The available tracefs paths will depends on distribution choices. var getTracefsPath = internal.Memoize(func() (string, error) { for _, p := range []struct { path string fsType int64 }{ {"/sys/kernel/tracing", unix.TRACEFS_MAGIC}, {"/sys/kernel/debug/tracing", unix.TRACEFS_MAGIC}, // RHEL/CentOS {"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC}, } { if fsType, err := internal.FSType(p.path); err == nil && fsType == p.fsType { return p.path, nil } } return "", errors.New("neither debugfs nor tracefs are mounted") }) // sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore. // // It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_"). func sanitizeIdentifier(s string) string { var skip bool return strings.Map(func(c rune) rune { switch { case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9': skip = false return c case skip: return -1 default: skip = true return '_' } }, s) } // EventID reads a trace event's ID from tracefs given its group and name. // The kernel requires group and name to be alphanumeric or underscore. func EventID(group, name string) (uint64, error) { if !validIdentifier(group) { return 0, fmt.Errorf("invalid tracefs group: %q", group) } if !validIdentifier(name) { return 0, fmt.Errorf("invalid tracefs name: %q", name) } path, err := sanitizeTracefsPath("events", group, name, "id") if err != nil { return 0, err } tid, err := internal.ReadUint64FromFile("%d\n", path) if errors.Is(err, os.ErrNotExist) { return 0, err } if err != nil { return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err) } return tid, nil } func probePrefix(ret bool, maxActive int) string { if ret { if maxActive > 0 { return fmt.Sprintf("r%d", maxActive) } return "r" } return "p" } // Event represents an entry in a tracefs probe events file. type Event struct { typ ProbeType group, name string // event id allocated by the kernel. 0 if the event has already been removed. id uint64 } // NewEvent creates a new ephemeral trace event. // // Returns os.ErrNotExist if symbol is not a valid // kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist // if a probe with the same group and symbol already exists. Returns an error if // args.RetprobeMaxActive is used on non kprobe types. Returns ErrNotSupported if // the kernel is too old to support kretprobe maxactive. func NewEvent(args ProbeArgs) (*Event, error) { // Before attempting to create a trace event through tracefs, // check if an event with the same group and name already exists. // Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate // entry, so we need to rely on reads for detecting uniqueness. eventName := sanitizeIdentifier(args.Symbol) _, err := EventID(args.Group, eventName) if err == nil { return nil, fmt.Errorf("trace event %s/%s: %w", args.Group, eventName, os.ErrExist) } if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("checking trace event %s/%s: %w", args.Group, eventName, err) } // Open the kprobe_events file in tracefs. f, err := args.Type.eventsFile() if err != nil { return nil, err } defer f.Close() var pe, token string switch args.Type { case Kprobe: // The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt): // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe // -:[GRP/]EVENT : Clear a probe // // Some examples: // r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy // p:ebpf_5678/p_my_kprobe __x64_sys_execve // // Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the // kernel default to NR_CPUS. This is desired in most eBPF cases since // subsampling or rate limiting logic can be more accurately implemented in // the eBPF program itself. // See Documentation/kprobes.txt for more details. if args.RetprobeMaxActive != 0 && !args.Ret { return nil, ErrInvalidMaxActive } token = KprobeToken(args) pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, args.RetprobeMaxActive), args.Group, eventName, token) case Uprobe: // The uprobe_events syntax is as follows: // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe // r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe // -:[GRP/]EVENT : Clear a probe // // Some examples: // r:ebpf_1234/readline /bin/bash:0x12345 // p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123) // // See Documentation/trace/uprobetracer.txt for more details. if args.RetprobeMaxActive != 0 { return nil, ErrInvalidMaxActive } token = UprobeToken(args) pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, 0), args.Group, eventName, token) } _, err = f.WriteString(pe) // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a retprobe for a missing symbol. if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("token %s: not found: %w", token, err) } // Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved // to an invalid insn boundary. The exact conditions that trigger this error are // arch specific however. if errors.Is(err, syscall.EILSEQ) { return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist) } // ERANGE is returned when the `SYM[+offs]` token is too big and cannot // be resolved. if errors.Is(err, syscall.ERANGE) { return nil, fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist) } if err != nil { return nil, fmt.Errorf("token %s: writing '%s': %w", token, pe, err) } // Get the newly-created trace event's id. tid, err := EventID(args.Group, eventName) if args.RetprobeMaxActive != 0 && errors.Is(err, os.ErrNotExist) { // Kernels < 4.12 don't support maxactive and therefore auto generate // group and event names from the symbol and offset. The symbol is used // without any sanitization. // See https://elixir.bootlin.com/linux/v4.10/source/kernel/trace/trace_kprobe.c#L712 event := fmt.Sprintf("kprobes/r_%s_%d", args.Symbol, args.Offset) if err := removeEvent(args.Type, event); err != nil { return nil, fmt.Errorf("failed to remove spurious maxactive event: %s", err) } return nil, fmt.Errorf("create trace event with non-default maxactive: %w", internal.ErrNotSupported) } if err != nil { return nil, fmt.Errorf("get trace event id: %w", err) } evt := &Event{args.Type, args.Group, eventName, tid} runtime.SetFinalizer(evt, (*Event).Close) return evt, nil } // Close removes the event from tracefs. // // Returns os.ErrClosed if the event has already been closed before. func (evt *Event) Close() error { if evt.id == 0 { return os.ErrClosed } evt.id = 0 runtime.SetFinalizer(evt, nil) pe := fmt.Sprintf("%s/%s", evt.group, evt.name) return removeEvent(evt.typ, pe) } func removeEvent(typ ProbeType, pe string) error { f, err := typ.eventsFile() if err != nil { return err } defer f.Close() // See [k,u]probe_events syntax above. The probe type does not need to be specified // for removals. if _, err = f.WriteString("-:" + pe); err != nil { return fmt.Errorf("remove event %q from %s: %w", pe, f.Name(), err) } return nil } // ID returns the tracefs ID associated with the event. func (evt *Event) ID() uint64 { return evt.id } // Group returns the tracefs group used by the event. func (evt *Event) Group() string { return evt.group } // KprobeToken creates the SYM[+offs] token for the tracefs api. func KprobeToken(args ProbeArgs) string { po := args.Symbol if args.Offset != 0 { po += fmt.Sprintf("+%#x", args.Offset) } return po } golang-github-cilium-ebpf-0.11.0/internal/tracefs/kprobe_test.go000066400000000000000000000037701456504511400246410ustar00rootroot00000000000000package tracefs import ( "fmt" "os" "testing" qt "github.com/frankban/quicktest" ) // Global symbol, present on all tested kernels. const ksym = "vprintk" func TestKprobeTraceFSGroup(t *testing.T) { c := qt.New(t) // Expect _<16 random hex chars>. g, err := RandomGroup("ebpftest") c.Assert(err, qt.IsNil) c.Assert(g, qt.Matches, `ebpftest_[a-f0-9]{16}`) // Expect error when the generator's output exceeds 63 characters. p := make([]byte, 47) // 63 - 17 (length of the random suffix and underscore) + 1 for i := range p { p[i] = byte('a') } _, err = RandomGroup(string(p)) c.Assert(err, qt.Not(qt.IsNil)) // Reject non-alphanumeric characters. _, err = RandomGroup("/") c.Assert(err, qt.Not(qt.IsNil)) } func TestKprobeToken(t *testing.T) { tests := []struct { args ProbeArgs expected string }{ {ProbeArgs{Symbol: "symbol"}, "symbol"}, {ProbeArgs{Symbol: "symbol", Offset: 1}, "symbol+0x1"}, {ProbeArgs{Symbol: "symbol", Offset: 65535}, "symbol+0xffff"}, {ProbeArgs{Symbol: "symbol", Offset: 65536}, "symbol+0x10000"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { po := KprobeToken(tt.args) if tt.expected != po { t.Errorf("Expected symbol+offset to be '%s', got '%s'", tt.expected, po) } }) } } func TestNewEvent(t *testing.T) { for _, args := range []ProbeArgs{ {Type: Kprobe, Symbol: ksym}, {Type: Kprobe, Symbol: ksym, Ret: true}, {Type: Uprobe, Path: "/bin/bash", Symbol: "main"}, {Type: Uprobe, Path: "/bin/bash", Symbol: "main", Ret: true}, } { name := fmt.Sprintf("%s ret=%v", args.Type, args.Ret) t.Run(name, func(t *testing.T) { args.Group, _ = RandomGroup("ebpftest") evt, err := NewEvent(args) qt.Assert(t, err, qt.IsNil) defer evt.Close() _, err = NewEvent(args) qt.Assert(t, err, qt.ErrorIs, os.ErrExist, qt.Commentf("expected consecutive event creation to contain os.ErrExist")) qt.Assert(t, evt.Close(), qt.IsNil) qt.Assert(t, evt.Close(), qt.ErrorIs, os.ErrClosed) }) } } golang-github-cilium-ebpf-0.11.0/internal/tracefs/perf_event_test.go000066400000000000000000000034711456504511400255120ustar00rootroot00000000000000package tracefs import ( "errors" "fmt" "testing" qt "github.com/frankban/quicktest" ) func TestEventID(t *testing.T) { c := qt.New(t) eid, err := EventID("syscalls", "sys_enter_mmap") c.Assert(err, qt.IsNil) c.Assert(eid, qt.Not(qt.Equals), 0) } func TestSanitizePath(t *testing.T) { _, err := sanitizeTracefsPath("../escaped") if !errors.Is(err, ErrInvalidInput) { t.Errorf("expected error %s, got: %s", ErrInvalidInput, err) } _, err = sanitizeTracefsPath("./not/escaped") if err != nil { t.Errorf("expected no error, got: %s", err) } } func TestValidIdentifier(t *testing.T) { tests := []struct { name string in string fail bool }{ {"empty string", "", true}, {"leading number", "1test", true}, {"underscore first", "__x64_syscall", false}, {"contains number", "bpf_trace_run1", false}, {"underscore", "_", false}, {"contains dash", "-EINVAL", true}, {"contains number", "all0wed", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { exp := "pass" if tt.fail { exp = "fail" } if validIdentifier(tt.in) == tt.fail { t.Errorf("expected string '%s' to %s valid ID check", tt.in, exp) } }) } } func TestSanitizeIdentifier(t *testing.T) { tests := []struct { symbol string expected string }{ {"readline", "readline"}, {"main.Func123", "main_Func123"}, {"a.....a", "a_a"}, {"./;'{}[]a", "_a"}, {"***xx**xx###", "_xx_xx_"}, {`@P#r$i%v^3*+t)i&k++--`, "_P_r_i_v_3_t_i_k_"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { sanitized := sanitizeIdentifier(tt.symbol) if tt.expected != sanitized { t.Errorf("Expected sanitized symbol to be '%s', got '%s'", tt.expected, sanitized) } }) } } func TestGetTracefsPath(t *testing.T) { _, err := getTracefsPath() qt.Assert(t, err, qt.IsNil) } golang-github-cilium-ebpf-0.11.0/internal/tracefs/probetype_string.go000066400000000000000000000011651456504511400257130ustar00rootroot00000000000000// Code generated by "stringer -type=ProbeType -linecomment"; DO NOT EDIT. package tracefs import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Kprobe-0] _ = x[Uprobe-1] } const _ProbeType_name = "kprobeuprobe" var _ProbeType_index = [...]uint8{0, 6, 12} func (i ProbeType) String() string { if i >= ProbeType(len(_ProbeType_index)-1) { return "ProbeType(" + strconv.FormatInt(int64(i), 10) + ")" } return _ProbeType_name[_ProbeType_index[i]:_ProbeType_index[i+1]] } golang-github-cilium-ebpf-0.11.0/internal/tracefs/uprobe.go000066400000000000000000000006761456504511400236160ustar00rootroot00000000000000package tracefs import "fmt" // UprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api. func UprobeToken(args ProbeArgs) string { po := fmt.Sprintf("%s:%#x", args.Path, args.Offset) if args.RefCtrOffset != 0 { // This is not documented in Documentation/trace/uprobetracer.txt. // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/trace/trace.c#L5564 po += fmt.Sprintf("(%#x)", args.RefCtrOffset) } return po } golang-github-cilium-ebpf-0.11.0/internal/tracefs/uprobe_test.go000066400000000000000000000014351456504511400246470ustar00rootroot00000000000000package tracefs import ( "fmt" "testing" ) func TestUprobeToken(t *testing.T) { tests := []struct { args ProbeArgs expected string }{ {ProbeArgs{Path: "/bin/bash"}, "/bin/bash:0x0"}, {ProbeArgs{Path: "/bin/bash", Offset: 1}, "/bin/bash:0x1"}, {ProbeArgs{Path: "/bin/bash", Offset: 65535}, "/bin/bash:0xffff"}, {ProbeArgs{Path: "/bin/bash", Offset: 65536}, "/bin/bash:0x10000"}, {ProbeArgs{Path: "/bin/bash", Offset: 1, RefCtrOffset: 1}, "/bin/bash:0x1(0x1)"}, {ProbeArgs{Path: "/bin/bash", Offset: 1, RefCtrOffset: 65535}, "/bin/bash:0x1(0xffff)"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { po := UprobeToken(tt.args) if tt.expected != po { t.Errorf("Expected path:offset to be '%s', got '%s'", tt.expected, po) } }) } } golang-github-cilium-ebpf-0.11.0/internal/unix/000077500000000000000000000000001456504511400213165ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/internal/unix/doc.go000066400000000000000000000007651456504511400224220ustar00rootroot00000000000000// Package unix re-exports Linux specific parts of golang.org/x/sys/unix. // // It avoids breaking compilation on other OS by providing stubs as follows: // - Invoking a function always returns an error. // - Errnos have distinct, non-zero values. // - Constants have distinct but meaningless values. // - Types use the same names for members, but may or may not follow the // Linux layout. package unix // Note: please don't add any custom API to this package. Use internal/sys instead. golang-github-cilium-ebpf-0.11.0/internal/unix/types_linux.go000066400000000000000000000141751456504511400242400ustar00rootroot00000000000000//go:build linux package unix import ( "syscall" linux "golang.org/x/sys/unix" ) const ( ENOENT = linux.ENOENT EEXIST = linux.EEXIST EAGAIN = linux.EAGAIN ENOSPC = linux.ENOSPC EINVAL = linux.EINVAL EPOLLIN = linux.EPOLLIN EINTR = linux.EINTR EPERM = linux.EPERM ESRCH = linux.ESRCH ENODEV = linux.ENODEV EBADF = linux.EBADF E2BIG = linux.E2BIG EFAULT = linux.EFAULT EACCES = linux.EACCES EILSEQ = linux.EILSEQ EOPNOTSUPP = linux.EOPNOTSUPP ) const ( BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE BPF_F_RDONLY = linux.BPF_F_RDONLY BPF_F_WRONLY = linux.BPF_F_WRONLY BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE BPF_F_XDP_HAS_FRAGS = linux.BPF_F_XDP_HAS_FRAGS BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN BPF_TAG_SIZE = linux.BPF_TAG_SIZE BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ SYS_BPF = linux.SYS_BPF F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC O_CLOEXEC = linux.O_CLOEXEC O_NONBLOCK = linux.O_NONBLOCK PROT_NONE = linux.PROT_NONE PROT_READ = linux.PROT_READ PROT_WRITE = linux.PROT_WRITE MAP_ANON = linux.MAP_ANON MAP_SHARED = linux.MAP_SHARED MAP_PRIVATE = linux.MAP_PRIVATE PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1 PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF PerfBitWatermark = linux.PerfBitWatermark PerfBitWriteBackward = linux.PerfBitWriteBackward PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC RLIM_INFINITY = linux.RLIM_INFINITY RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME PERF_RECORD_LOST = linux.PERF_RECORD_LOST PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE AT_FDCWD = linux.AT_FDCWD RENAME_NOREPLACE = linux.RENAME_NOREPLACE SO_ATTACH_BPF = linux.SO_ATTACH_BPF SO_DETACH_BPF = linux.SO_DETACH_BPF SOL_SOCKET = linux.SOL_SOCKET SIGPROF = linux.SIGPROF SIG_BLOCK = linux.SIG_BLOCK SIG_UNBLOCK = linux.SIG_UNBLOCK EM_NONE = linux.EM_NONE EM_BPF = linux.EM_BPF BPF_FS_MAGIC = linux.BPF_FS_MAGIC TRACEFS_MAGIC = linux.TRACEFS_MAGIC DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC ) type Statfs_t = linux.Statfs_t type Stat_t = linux.Stat_t type Rlimit = linux.Rlimit type Signal = linux.Signal type Sigset_t = linux.Sigset_t type PerfEventMmapPage = linux.PerfEventMmapPage type EpollEvent = linux.EpollEvent type PerfEventAttr = linux.PerfEventAttr type Utsname = linux.Utsname func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) } func PthreadSigmask(how int, set, oldset *Sigset_t) error { return linux.PthreadSigmask(how, set, oldset) } func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return linux.FcntlInt(fd, cmd, arg) } func IoctlSetInt(fd int, req uint, value int) error { return linux.IoctlSetInt(fd, req, value) } func Statfs(path string, buf *Statfs_t) (err error) { return linux.Statfs(path, buf) } func Close(fd int) (err error) { return linux.Close(fd) } func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return linux.EpollWait(epfd, events, msec) } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return linux.EpollCtl(epfd, op, fd, event) } func Eventfd(initval uint, flags int) (fd int, err error) { return linux.Eventfd(initval, flags) } func Write(fd int, p []byte) (n int, err error) { return linux.Write(fd, p) } func EpollCreate1(flag int) (fd int, err error) { return linux.EpollCreate1(flag) } func SetNonblock(fd int, nonblocking bool) (err error) { return linux.SetNonblock(fd, nonblocking) } func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return linux.Mmap(fd, offset, length, prot, flags) } func Munmap(b []byte) (err error) { return linux.Munmap(b) } func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags) } func Uname(buf *Utsname) (err error) { return linux.Uname(buf) } func Getpid() int { return linux.Getpid() } func Gettid() int { return linux.Gettid() } func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return linux.Tgkill(tgid, tid, sig) } func BytePtrFromString(s string) (*byte, error) { return linux.BytePtrFromString(s) } func ByteSliceToString(s []byte) string { return linux.ByteSliceToString(s) } func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags) } func Prlimit(pid, resource int, new, old *Rlimit) error { return linux.Prlimit(pid, resource, new, old) } func Open(path string, mode int, perm uint32) (int, error) { return linux.Open(path, mode, perm) } func Fstat(fd int, stat *Stat_t) error { return linux.Fstat(fd, stat) } func SetsockoptInt(fd, level, opt, value int) error { return linux.SetsockoptInt(fd, level, opt, value) } golang-github-cilium-ebpf-0.11.0/internal/unix/types_other.go000066400000000000000000000120461456504511400242150ustar00rootroot00000000000000//go:build !linux package unix import ( "fmt" "runtime" "syscall" ) var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) // Errnos are distinct and non-zero. const ( ENOENT syscall.Errno = iota + 1 EEXIST EAGAIN ENOSPC EINVAL EINTR EPERM ESRCH ENODEV EBADF E2BIG EFAULT EACCES EILSEQ EOPNOTSUPP ) // Constants are distinct to avoid breaking switch statements. const ( BPF_F_NO_PREALLOC = iota BPF_F_NUMA_NODE BPF_F_RDONLY BPF_F_WRONLY BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG BPF_F_SLEEPABLE BPF_F_MMAPABLE BPF_F_INNER_MAP BPF_F_KPROBE_MULTI_RETURN BPF_F_XDP_HAS_FRAGS BPF_OBJ_NAME_LEN BPF_TAG_SIZE BPF_RINGBUF_BUSY_BIT BPF_RINGBUF_DISCARD_BIT BPF_RINGBUF_HDR_SZ SYS_BPF F_DUPFD_CLOEXEC EPOLLIN EPOLL_CTL_ADD EPOLL_CLOEXEC O_CLOEXEC O_NONBLOCK PROT_NONE PROT_READ PROT_WRITE MAP_ANON MAP_SHARED MAP_PRIVATE PERF_ATTR_SIZE_VER1 PERF_TYPE_SOFTWARE PERF_TYPE_TRACEPOINT PERF_COUNT_SW_BPF_OUTPUT PERF_EVENT_IOC_DISABLE PERF_EVENT_IOC_ENABLE PERF_EVENT_IOC_SET_BPF PerfBitWatermark PerfBitWriteBackward PERF_SAMPLE_RAW PERF_FLAG_FD_CLOEXEC RLIM_INFINITY RLIMIT_MEMLOCK BPF_STATS_RUN_TIME PERF_RECORD_LOST PERF_RECORD_SAMPLE AT_FDCWD RENAME_NOREPLACE SO_ATTACH_BPF SO_DETACH_BPF SOL_SOCKET SIGPROF SIG_BLOCK SIG_UNBLOCK EM_NONE EM_BPF BPF_FS_MAGIC TRACEFS_MAGIC DEBUGFS_MAGIC ) type Statfs_t struct { Type int64 Bsize int64 Blocks uint64 Bfree uint64 Bavail uint64 Files uint64 Ffree uint64 Fsid [2]int32 Namelen int64 Frsize int64 Flags int64 Spare [4]int64 } type Stat_t struct { Dev uint64 Ino uint64 Nlink uint64 Mode uint32 Uid uint32 Gid uint32 _ int32 Rdev uint64 Size int64 Blksize int64 Blocks int64 } type Rlimit struct { Cur uint64 Max uint64 } type Signal int type Sigset_t struct { Val [4]uint64 } func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, syscall.ENOTSUP } func PthreadSigmask(how int, set, oldset *Sigset_t) error { return errNonLinux } func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return -1, errNonLinux } func IoctlSetInt(fd int, req uint, value int) error { return errNonLinux } func Statfs(path string, buf *Statfs_t) error { return errNonLinux } func Close(fd int) (err error) { return errNonLinux } type EpollEvent struct { Events uint32 Fd int32 Pad int32 } func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return 0, errNonLinux } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return errNonLinux } func Eventfd(initval uint, flags int) (fd int, err error) { return 0, errNonLinux } func Write(fd int, p []byte) (n int, err error) { return 0, errNonLinux } func EpollCreate1(flag int) (fd int, err error) { return 0, errNonLinux } type PerfEventMmapPage struct { Version uint32 Compat_version uint32 Lock uint32 Index uint32 Offset int64 Time_enabled uint64 Time_running uint64 Capabilities uint64 Pmc_width uint16 Time_shift uint16 Time_mult uint32 Time_offset uint64 Time_zero uint64 Size uint32 Data_head uint64 Data_tail uint64 Data_offset uint64 Data_size uint64 Aux_head uint64 Aux_tail uint64 Aux_offset uint64 Aux_size uint64 } func SetNonblock(fd int, nonblocking bool) (err error) { return errNonLinux } func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return []byte{}, errNonLinux } func Munmap(b []byte) (err error) { return errNonLinux } type PerfEventAttr struct { Type uint32 Size uint32 Config uint64 Sample uint64 Sample_type uint64 Read_format uint64 Bits uint64 Wakeup uint32 Bp_type uint32 Ext1 uint64 Ext2 uint64 Branch_sample_type uint64 Sample_regs_user uint64 Sample_stack_user uint32 Clockid int32 Sample_regs_intr uint64 Aux_watermark uint32 Sample_max_stack uint16 } func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return 0, errNonLinux } type Utsname struct { Release [65]byte Version [65]byte } func Uname(buf *Utsname) (err error) { return errNonLinux } func Getpid() int { return -1 } func Gettid() int { return -1 } func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return errNonLinux } func BytePtrFromString(s string) (*byte, error) { return nil, errNonLinux } func ByteSliceToString(s []byte) string { return "" } func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return errNonLinux } func Prlimit(pid, resource int, new, old *Rlimit) error { return errNonLinux } func Open(path string, mode int, perm uint32) (int, error) { return -1, errNonLinux } func Fstat(fd int, stat *Stat_t) error { return errNonLinux } func SetsockoptInt(fd, level, opt, value int) error { return errNonLinux } golang-github-cilium-ebpf-0.11.0/internal/vdso.go000066400000000000000000000105341456504511400216400ustar00rootroot00000000000000package internal import ( "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "github.com/cilium/ebpf/internal/unix" ) var ( errAuxvNoVDSO = errors.New("no vdso address found in auxv") ) // vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library // linked into the current process image. func vdsoVersion() (uint32, error) { // Read data from the auxiliary vector, which is normally passed directly // to the process. Go does not expose that data, so we must read it from procfs. // https://man7.org/linux/man-pages/man3/getauxval.3.html av, err := os.Open("/proc/self/auxv") if errors.Is(err, unix.EACCES) { return 0, fmt.Errorf("opening auxv: %w (process may not be dumpable due to file capabilities)", err) } if err != nil { return 0, fmt.Errorf("opening auxv: %w", err) } defer av.Close() vdsoAddr, err := vdsoMemoryAddress(av) if err != nil { return 0, fmt.Errorf("finding vDSO memory address: %w", err) } // Use /proc/self/mem rather than unsafe.Pointer tricks. mem, err := os.Open("/proc/self/mem") if err != nil { return 0, fmt.Errorf("opening mem: %w", err) } defer mem.Close() // Open ELF at provided memory address, as offset into /proc/self/mem. c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64)) if err != nil { return 0, fmt.Errorf("reading linux version code: %w", err) } return c, nil } // vdsoMemoryAddress returns the memory address of the vDSO library // linked into the current process image. r is an io.Reader into an auxv blob. func vdsoMemoryAddress(r io.Reader) (uint64, error) { const ( _AT_NULL = 0 // End of vector _AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image ) // Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`, // the address of a page containing the virtual Dynamic Shared Object (vDSO). aux := struct{ Tag, Val uint64 }{} for { if err := binary.Read(r, NativeEndian, &aux); err != nil { return 0, fmt.Errorf("reading auxv entry: %w", err) } switch aux.Tag { case _AT_SYSINFO_EHDR: if aux.Val != 0 { return aux.Val, nil } return 0, fmt.Errorf("invalid vDSO address in auxv") // _AT_NULL is always the last tag/val pair in the aux vector // and can be treated like EOF. case _AT_NULL: return 0, errAuxvNoVDSO } } } // format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)' type elfNoteHeader struct { NameSize int32 DescSize int32 Type int32 } // vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in // the ELF notes section of the binary provided by the reader. func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { hdr, err := NewSafeELFFile(r) if err != nil { return 0, fmt.Errorf("reading vDSO ELF: %w", err) } sections := hdr.SectionsByType(elf.SHT_NOTE) if len(sections) == 0 { return 0, fmt.Errorf("no note section found in vDSO ELF") } for _, sec := range sections { sr := sec.Open() var n elfNoteHeader // Read notes until we find one named 'Linux'. for { if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil { if errors.Is(err, io.EOF) { // We looked at all the notes in this section break } return 0, fmt.Errorf("reading note header: %w", err) } // If a note name is defined, it follows the note header. var name string if n.NameSize > 0 { // Read the note name, aligned to 4 bytes. buf := make([]byte, Align(n.NameSize, 4)) if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil { return 0, fmt.Errorf("reading note name: %w", err) } // Read nul-terminated string. name = unix.ByteSliceToString(buf[:n.NameSize]) } // If a note descriptor is defined, it follows the name. // It is possible for a note to have a descriptor but not a name. if n.DescSize > 0 { // LINUX_VERSION_CODE is a uint32 value. if name == "Linux" && n.DescSize == 4 && n.Type == 0 { var version uint32 if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil { return 0, fmt.Errorf("reading note descriptor: %w", err) } return version, nil } // Discard the note descriptor if it exists but we're not interested in it. if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil { return 0, err } } } } return 0, fmt.Errorf("no Linux note in ELF") } golang-github-cilium-ebpf-0.11.0/internal/vdso_test.go000066400000000000000000000026501456504511400226770ustar00rootroot00000000000000package internal import ( "errors" "os" "testing" ) func TestAuxvVDSOMemoryAddress(t *testing.T) { av, err := os.Open("../testdata/auxv.bin") if err != nil { t.Fatal(err) } t.Cleanup(func() { av.Close() }) addr, err := vdsoMemoryAddress(av) if err != nil { t.Fatal(err) } expected := uint64(0x7ffd377e5000) if addr != expected { t.Errorf("Expected vDSO memory address %x, got %x", expected, addr) } } func TestAuxvNoVDSO(t *testing.T) { // Copy of auxv.bin with the vDSO pointer removed. av, err := os.Open("../testdata/auxv_no_vdso.bin") if err != nil { t.Fatal(err) } t.Cleanup(func() { av.Close() }) _, err = vdsoMemoryAddress(av) if want, got := errAuxvNoVDSO, err; !errors.Is(got, want) { t.Fatalf("expected error '%v', got: %v", want, got) } } func TestLinuxVersionCodeEmbedded(t *testing.T) { tests := []struct { file string version uint32 }{ { "../testdata/vdso.bin", uint32(328828), // 5.4.124 }, { "../testdata/vdso_multiple_notes.bin", uint32(328875), // Container Optimized OS v85 with a 5.4.x kernel }, } for _, test := range tests { t.Run(test.file, func(t *testing.T) { vdso, err := os.Open(test.file) if err != nil { t.Fatal(err) } defer vdso.Close() vc, err := vdsoLinuxVersionCode(vdso) if err != nil { t.Fatal(err) } if vc != test.version { t.Errorf("Expected version code %d, got %d", test.version, vc) } }) } } golang-github-cilium-ebpf-0.11.0/internal/version.go000066400000000000000000000055631456504511400223600ustar00rootroot00000000000000package internal import ( "fmt" "github.com/cilium/ebpf/internal/unix" ) const ( // Version constant used in ELF binaries indicating that the loader needs to // substitute the eBPF program's version with the value of the kernel's // KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf // and RedSift. MagicKernelVersion = 0xFFFFFFFE ) // A Version in the form Major.Minor.Patch. type Version [3]uint16 // NewVersion creates a version from a string like "Major.Minor.Patch". // // Patch is optional. func NewVersion(ver string) (Version, error) { var major, minor, patch uint16 n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) if n < 2 { return Version{}, fmt.Errorf("invalid version: %s", ver) } return Version{major, minor, patch}, nil } // NewVersionFromCode creates a version from a LINUX_VERSION_CODE. func NewVersionFromCode(code uint32) Version { return Version{ uint16(uint8(code >> 16)), uint16(uint8(code >> 8)), uint16(uint8(code)), } } func (v Version) String() string { if v[2] == 0 { return fmt.Sprintf("v%d.%d", v[0], v[1]) } return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2]) } // Less returns true if the version is less than another version. func (v Version) Less(other Version) bool { for i, a := range v { if a == other[i] { continue } return a < other[i] } return false } // Unspecified returns true if the version is all zero. func (v Version) Unspecified() bool { return v[0] == 0 && v[1] == 0 && v[2] == 0 } // Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h. // It represents the kernel version and patch level as a single value. func (v Version) Kernel() uint32 { // Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid // overflowing into PATCHLEVEL. // See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255"). s := v[2] if s > 255 { s = 255 } // Truncate members to uint8 to prevent them from spilling over into // each other when overflowing 8 bits. return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) } // KernelVersion returns the version of the currently running kernel. var KernelVersion = Memoize(func() (Version, error) { return detectKernelVersion() }) // detectKernelVersion returns the version of the running kernel. func detectKernelVersion() (Version, error) { vc, err := vdsoVersion() if err != nil { return Version{}, err } return NewVersionFromCode(vc), nil } // KernelRelease returns the release string of the running kernel. // Its format depends on the Linux distribution and corresponds to directory // names in /lib/modules by convention. Some examples are 5.15.17-1-lts and // 4.19.0-16-amd64. func KernelRelease() (string, error) { var uname unix.Utsname if err := unix.Uname(&uname); err != nil { return "", fmt.Errorf("uname failed: %w", err) } return unix.ByteSliceToString(uname.Release[:]), nil } golang-github-cilium-ebpf-0.11.0/internal/version_test.go000066400000000000000000000040451456504511400234110ustar00rootroot00000000000000package internal import ( "os" "testing" ) func TestVersion(t *testing.T) { a, err := NewVersion("1.2") if err != nil { t.Fatal(err) } b, err := NewVersion("2.2.1") if err != nil { t.Fatal(err) } if !a.Less(b) { t.Error("A should be less than B") } if b.Less(a) { t.Error("B shouldn't be less than A") } v200 := Version{2, 0, 0} if !a.Less(v200) { t.Error("1.2.1 should not be less than 2.0.0") } if v200.Less(a) { t.Error("2.0.0 should not be less than 1.2.1") } } func TestKernelVersion(t *testing.T) { // Kernels 4.4 and 4.9 have a SUBLEVEL of over 255 and clamp it to 255. // In our implementation, the other version segments are truncated. if v, want := (Version{256, 256, 256}), uint32(255); v.Kernel() != want { t.Errorf("256.256.256 should result in a kernel version of %d, got: %d", want, v.Kernel()) } // Known good version. if v, want := (Version{4, 9, 128}), uint32(264576); v.Kernel() != want { t.Errorf("4.9.1 should result in a kernel version of %d, got: %d", want, v.Kernel()) } } func TestCurrentKernelVersion(t *testing.T) { v, err := KernelVersion() if err != nil { t.Fatal(err) } if evStr := os.Getenv("KERNEL_VERSION"); evStr != "" { ev, err := NewVersion(evStr) if err != nil { t.Fatal(err) } if ev[0] != v[0] || ev[1] != v[1] { t.Errorf("expected kernel version %d.%d, got %d.%d", ev[0], ev[1], v[0], v[1]) } } } func TestVersionFromCode(t *testing.T) { var tests = []struct { name string code uint32 v Version }{ {"0.0.0", 0, Version{0, 0, 0}}, {"1.0.0", 0x10000, Version{1, 0, 0}}, {"4.4.255", 0x404ff, Version{4, 4, 255}}, {"255.255.255", 0xffffff, Version{255, 255, 255}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := NewVersionFromCode(tt.code) if v != tt.v { t.Errorf("unexpected version for code '%d'. got: %v, want: %v", tt.code, v, tt.v) } }) } } func TestKernelRelease(t *testing.T) { r, err := KernelRelease() if err != nil { t.Fatal(err) } if r == "" { t.Fatal("unexpected empty kernel release") } } golang-github-cilium-ebpf-0.11.0/link/000077500000000000000000000000001456504511400174545ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/link/cgroup.go000066400000000000000000000113761456504511400213120ustar00rootroot00000000000000package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" ) type cgroupAttachFlags uint32 const ( // Allow programs attached to sub-cgroups to override the verdict of this // program. flagAllowOverride cgroupAttachFlags = 1 << iota // Allow attaching multiple programs to the cgroup. Only works if the cgroup // has zero or more programs attached using the Multi flag. Implies override. flagAllowMulti // Set automatically by progAttachCgroup.Update(). Used for updating a // specific given program attached in multi-mode. flagReplace ) type CgroupOptions struct { // Path to a cgroupv2 folder. Path string // One of the AttachCgroup* constants Attach ebpf.AttachType // Program must be of type CGroup*, and the attach type must match Attach. Program *ebpf.Program } // AttachCgroup links a BPF program to a cgroup. // // If the running kernel doesn't support bpf_link, attempts to emulate its // semantics using the legacy PROG_ATTACH mechanism. If bpf_link is not // available, the returned [Link] will not support pinning to bpffs. // // If you need more control over attachment flags or the attachment mechanism // used, look at [RawAttachProgram] and [AttachRawLink] instead. func AttachCgroup(opts CgroupOptions) (cg Link, err error) { cgroup, err := os.Open(opts.Path) if err != nil { return nil, fmt.Errorf("can't open cgroup: %s", err) } defer func() { if _, ok := cg.(*progAttachCgroup); ok { // Skip closing the cgroup handle if we return a valid progAttachCgroup, // where the handle is retained to implement Update(). return } cgroup.Close() }() cg, err = newLinkCgroup(cgroup, opts.Attach, opts.Program) if err == nil { return cg, nil } if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowMulti) } if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowOverride) } if err != nil { return nil, err } return cg, nil } type progAttachCgroup struct { cgroup *os.File current *ebpf.Program attachType ebpf.AttachType flags cgroupAttachFlags } var _ Link = (*progAttachCgroup)(nil) func (cg *progAttachCgroup) isLink() {} // newProgAttachCgroup attaches prog to cgroup using BPF_PROG_ATTACH. // cgroup and prog are retained by [progAttachCgroup]. func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) { if flags&flagAllowMulti > 0 { if err := haveProgAttachReplace(); err != nil { return nil, fmt.Errorf("can't support multiple programs: %w", err) } } // Use a program handle that cannot be closed by the caller. clone, err := prog.Clone() if err != nil { return nil, err } err = RawAttachProgram(RawAttachProgramOptions{ Target: int(cgroup.Fd()), Program: clone, Flags: uint32(flags), Attach: attach, }) if err != nil { clone.Close() return nil, fmt.Errorf("cgroup: %w", err) } return &progAttachCgroup{cgroup, clone, attach, flags}, nil } func (cg *progAttachCgroup) Close() error { defer cg.cgroup.Close() defer cg.current.Close() err := RawDetachProgram(RawDetachProgramOptions{ Target: int(cg.cgroup.Fd()), Program: cg.current, Attach: cg.attachType, }) if err != nil { return fmt.Errorf("close cgroup: %s", err) } return nil } func (cg *progAttachCgroup) Update(prog *ebpf.Program) error { new, err := prog.Clone() if err != nil { return err } args := RawAttachProgramOptions{ Target: int(cg.cgroup.Fd()), Program: prog, Attach: cg.attachType, Flags: uint32(cg.flags), } if cg.flags&flagAllowMulti > 0 { // Atomically replacing multiple programs requires at least // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf // program in MULTI mode") args.Flags |= uint32(flagReplace) args.Replace = cg.current } if err := RawAttachProgram(args); err != nil { new.Close() return fmt.Errorf("can't update cgroup: %s", err) } cg.current.Close() cg.current = new return nil } func (cg *progAttachCgroup) Pin(string) error { return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) } func (cg *progAttachCgroup) Unpin() error { return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported) } func (cg *progAttachCgroup) Info() (*Info, error) { return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported) } type linkCgroup struct { RawLink } var _ Link = (*linkCgroup)(nil) // newLinkCgroup attaches prog to cgroup using BPF_LINK_CREATE. func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) { link, err := AttachRawLink(RawLinkOptions{ Target: int(cgroup.Fd()), Program: prog, Attach: attach, }) if err != nil { return nil, err } return &linkCgroup{*link}, err } golang-github-cilium-ebpf-0.11.0/link/cgroup_test.go000066400000000000000000000032021456504511400223360ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestAttachCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := AttachCgroup(CgroupOptions{ Path: cgroup.Name(), Attach: ebpf.AttachCGroupInetEgress, Program: prog, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer link.Close() if haveBPFLink() == nil { if _, ok := link.(*linkCgroup); !ok { t.Fatalf("Have support for bpf_link, but got %T instead of linkCgroup", link) } } else { if _, ok := link.(*progAttachCgroup); !ok { t.Fatalf("Expected progAttachCgroup, got %T instead", link) } } } func TestProgAttachCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, 0) if err != nil { t.Fatal("Can't create link:", err) } testLink(t, link, prog) } func TestProgAttachCgroupAllowMulti(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowMulti) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create link:", err) } // It's currently not possible for a program to replace // itself. prog2 := mustLoadProgram(t, ebpf.CGroupSKB, ebpf.AttachCGroupInetEgress, "") testLink(t, link, prog2) } func TestLinkCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create link:", err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.11.0/link/doc.go000066400000000000000000000001251456504511400205460ustar00rootroot00000000000000// Package link allows attaching eBPF programs to various kernel hooks. package link golang-github-cilium-ebpf-0.11.0/link/iter.go000066400000000000000000000036031456504511400207500ustar00rootroot00000000000000package link import ( "fmt" "io" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type IterOptions struct { // Program must be of type Tracing with attach type // AttachTraceIter. The kind of iterator to attach to is // determined at load time via the AttachTo field. // // AttachTo requires the kernel to include BTF of itself, // and it to be compiled with a recent pahole (>= 1.16). Program *ebpf.Program // Map specifies the target map for bpf_map_elem and sockmap iterators. // It may be nil. Map *ebpf.Map } // AttachIter attaches a BPF seq_file iterator. func AttachIter(opts IterOptions) (*Iter, error) { if err := haveBPFLink(); err != nil { return nil, err } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } var info bpfIterLinkInfoMap if opts.Map != nil { mapFd := opts.Map.FD() if mapFd < 0 { return nil, fmt.Errorf("invalid map: %w", sys.ErrClosedFd) } info.map_fd = uint32(mapFd) } attr := sys.LinkCreateIterAttr{ ProgFd: uint32(progFd), AttachType: sys.AttachType(ebpf.AttachTraceIter), IterInfo: sys.NewPointer(unsafe.Pointer(&info)), IterInfoLen: uint32(unsafe.Sizeof(info)), } fd, err := sys.LinkCreateIter(&attr) if err != nil { return nil, fmt.Errorf("can't link iterator: %w", err) } return &Iter{RawLink{fd, ""}}, err } // Iter represents an attached bpf_iter. type Iter struct { RawLink } // Open creates a new instance of the iterator. // // Reading from the returned reader triggers the BPF program. func (it *Iter) Open() (io.ReadCloser, error) { attr := &sys.IterCreateAttr{ LinkFd: it.fd.Uint(), } fd, err := sys.IterCreate(attr) if err != nil { return nil, fmt.Errorf("can't create iterator: %w", err) } return fd.File("bpf_iter"), nil } // union bpf_iter_link_info.map type bpfIterLinkInfoMap struct { map_fd uint32 } golang-github-cilium-ebpf-0.11.0/link/iter_test.go000066400000000000000000000041211456504511400220030ustar00rootroot00000000000000package link import ( "io" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestIter(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "bpf_map iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "bpf_map") it, err := AttachIter(IterOptions{ Program: prog, }) if err != nil { t.Fatal("Can't create iter:", err) } file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } testLink(t, it, prog) } func TestIterMapElements(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "bpf_map_elem iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "bpf_map_elem") arr, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Array, KeySize: 4, ValueSize: 4, MaxEntries: 3, }) if err != nil { t.Fatal(err) } defer arr.Close() it, err := AttachIter(IterOptions{ Program: prog, Map: arr, }) if err != nil { t.Fatal("Can't create iter:", err) } defer it.Close() file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } } func TestUDPIter(t *testing.T) { // Introduced by 5788b3a07fc5 ("net: bpf: Implement bpf iterator for udp") testutils.SkipOnOldKernel(t, "5.9", "udp iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "udp") it, err := AttachIter(IterOptions{ Program: prog, }) if err != nil { t.Fatal("Can't create iter:", err) } file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } testLink(t, it, prog) } golang-github-cilium-ebpf-0.11.0/link/kprobe.go000066400000000000000000000271271456504511400212760ustar00rootroot00000000000000package link import ( "errors" "fmt" "os" "runtime" "strings" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // KprobeOptions defines additional parameters that will be used // when loading Kprobes. type KprobeOptions struct { // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 // Offset of the kprobe relative to the traced symbol. // Can be used to insert kprobes at arbitrary offsets in kernel functions, // e.g. in places where functions have been inlined. Offset uint64 // Increase the maximum number of concurrent invocations of a kretprobe. // Required when tracing some long running functions in the kernel. // // Deprecated: this setting forces the use of an outdated kernel API and is not portable // across kernel versions. RetprobeMaxActive int // Prefix used for the event name if the kprobe must be attached using tracefs. // The group name will be formatted as `_`. // The default empty string is equivalent to "ebpf" as the prefix. TraceFSPrefix string } func (ko *KprobeOptions) cookie() uint64 { if ko == nil { return 0 } return ko.Cookie } // Kprobe attaches the given eBPF program to a perf event that fires when the // given kernel symbol starts executing. See /proc/kallsyms for available // symbols. For example, printk(): // // kp, err := Kprobe("printk", prog, nil) // // Losing the reference to the resulting Link (kp) will close the Kprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // If attaching to symbol fails, automatically retries with the running // platform's syscall prefix (e.g. __x64_) to support attaching to syscalls // in a portable fashion. func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { k, err := kprobe(symbol, prog, opts, false) if err != nil { return nil, err } lnk, err := attachPerfEvent(k, prog, opts.cookie()) if err != nil { k.Close() return nil, err } return lnk, nil } // Kretprobe attaches the given eBPF program to a perf event that fires right // before the given kernel symbol exits, with the function stack left intact. // See /proc/kallsyms for available symbols. For example, printk(): // // kp, err := Kretprobe("printk", prog, nil) // // Losing the reference to the resulting Link (kp) will close the Kretprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // If attaching to symbol fails, automatically retries with the running // platform's syscall prefix (e.g. __x64_) to support attaching to syscalls // in a portable fashion. // // On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol // incorrectly returns unix.EINVAL instead of os.ErrNotExist. func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { k, err := kprobe(symbol, prog, opts, true) if err != nil { return nil, err } lnk, err := attachPerfEvent(k, prog, opts.cookie()) if err != nil { k.Close() return nil, err } return lnk, nil } // isValidKprobeSymbol implements the equivalent of a regex match // against "^[a-zA-Z_][0-9a-zA-Z_.]*$". func isValidKprobeSymbol(s string) bool { if len(s) < 1 { return false } for i, c := range []byte(s) { switch { case c >= 'a' && c <= 'z': case c >= 'A' && c <= 'Z': case c == '_': case i > 0 && c >= '0' && c <= '9': // Allow `.` in symbol name. GCC-compiled kernel may change symbol name // to have a `.isra.$n` suffix, like `udp_send_skb.isra.52`. // See: https://gcc.gnu.org/gcc-10/changes.html case i > 0 && c == '.': default: return false } } return true } // kprobe opens a perf event on the given symbol and attaches prog to it. // If ret is true, create a kretprobe. func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*perfEvent, error) { if symbol == "" { return nil, fmt.Errorf("symbol name cannot be empty: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if !isValidKprobeSymbol(symbol) { return nil, fmt.Errorf("symbol '%s' must be a valid symbol in /proc/kallsyms: %w", symbol, errInvalidInput) } if prog.Type() != ebpf.Kprobe { return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput) } args := tracefs.ProbeArgs{ Type: tracefs.Kprobe, Pid: perfAllThreads, Symbol: symbol, Ret: ret, } if opts != nil { args.RetprobeMaxActive = opts.RetprobeMaxActive args.Cookie = opts.Cookie args.Offset = opts.Offset args.Group = opts.TraceFSPrefix } // Use kprobe PMU if the kernel has it available. tp, err := pmuProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { if prefix := internal.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = pmuProbe(args) } } if err == nil { return tp, nil } if err != nil && !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_kprobe PMU (arch-specific fallback for %q): %w", symbol, err) } // Use tracefs if kprobe PMU is missing. args.Symbol = symbol tp, err = tracefsProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { if prefix := internal.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = tracefsProbe(args) } } if err != nil { return nil, fmt.Errorf("creating tracefs event (arch-specific fallback for %q): %w", symbol, err) } return tp, nil } // pmuProbe opens a perf event based on a Performance Monitoring Unit. // // Requires at least a 4.17 kernel. // e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU" // 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU" // // Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU func pmuProbe(args tracefs.ProbeArgs) (*perfEvent, error) { // Getting the PMU type will fail if the kernel doesn't support // the perf_[k,u]probe PMU. eventType, err := internal.ReadUint64FromFileOnce("%d\n", "/sys/bus/event_source/devices", args.Type.String(), "type") if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("%s: %w", args.Type, ErrNotSupported) } if err != nil { return nil, err } // Use tracefs if we want to set kretprobe's retprobeMaxActive. if args.RetprobeMaxActive != 0 { return nil, fmt.Errorf("pmu probe: non-zero retprobeMaxActive: %w", ErrNotSupported) } var config uint64 if args.Ret { bit, err := internal.ReadUint64FromFileOnce("config:%d\n", "/sys/bus/event_source/devices", args.Type.String(), "/format/retprobe") if err != nil { return nil, err } config |= 1 << bit } var ( attr unix.PerfEventAttr sp unsafe.Pointer token string ) switch args.Type { case tracefs.Kprobe: // Create a pointer to a NUL-terminated string for the kernel. sp, err = unsafeStringPtr(args.Symbol) if err != nil { return nil, err } token = tracefs.KprobeToken(args) attr = unix.PerfEventAttr{ // The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1, // since it added the config2 (Ext2) field. Use Ext2 as probe_offset. Size: unix.PERF_ATTR_SIZE_VER1, Type: uint32(eventType), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Kernel symbol to trace Ext2: args.Offset, // Kernel symbol offset Config: config, // Retprobe flag } case tracefs.Uprobe: sp, err = unsafeStringPtr(args.Path) if err != nil { return nil, err } if args.RefCtrOffset != 0 { config |= args.RefCtrOffset << uprobeRefCtrOffsetShift } token = tracefs.UprobeToken(args) attr = unix.PerfEventAttr{ // The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1, // since it added the config2 (Ext2) field. The Size field controls the // size of the internal buffer the kernel allocates for reading the // perf_event_attr argument from userspace. Size: unix.PERF_ATTR_SIZE_VER1, Type: uint32(eventType), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Uprobe path Ext2: args.Offset, // Uprobe offset Config: config, // RefCtrOffset, Retprobe flag } } rawFd, err := unix.PerfEventOpen(&attr, args.Pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) // On some old kernels, kprobe PMU doesn't allow `.` in symbol names and // return -EINVAL. Return ErrNotSupported to allow falling back to tracefs. // https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343 if errors.Is(err, unix.EINVAL) && strings.Contains(args.Symbol, ".") { return nil, fmt.Errorf("token %s: older kernels don't accept dots: %w", token, ErrNotSupported) } // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a retprobe for a missing symbol. if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("token %s: not found: %w", token, err) } // Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved // to an invalid insn boundary. The exact conditions that trigger this error are // arch specific however. if errors.Is(err, unix.EILSEQ) { return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist) } // Since at least commit cb9a19fe4aa51, ENOTSUPP is returned // when attempting to set a uprobe on a trap instruction. if errors.Is(err, sys.ENOTSUPP) { return nil, fmt.Errorf("token %s: failed setting uprobe on offset %#x (possible trap insn): %w", token, args.Offset, err) } if err != nil { return nil, fmt.Errorf("token %s: opening perf event: %w", token, err) } // Ensure the string pointer is not collected before PerfEventOpen returns. runtime.KeepAlive(sp) fd, err := sys.NewFD(rawFd) if err != nil { return nil, err } // Kernel has perf_[k,u]probe PMU available, initialize perf event. return newPerfEvent(fd, nil), nil } // tracefsProbe creates a trace event by writing an entry to /[k,u]probe_events. // A new trace event group name is generated on every call to support creating // multiple trace events for the same kernel or userspace symbol. // Path and offset are only set in the case of uprobe(s) and are used to set // the executable/library path on the filesystem and the offset where the probe is inserted. // A perf event is then opened on the newly-created trace event and returned to the caller. func tracefsProbe(args tracefs.ProbeArgs) (*perfEvent, error) { groupPrefix := "ebpf" if args.Group != "" { groupPrefix = args.Group } // Generate a random string for each trace event we attempt to create. // This value is used as the 'group' token in tracefs to allow creating // multiple kprobe trace events with the same name. group, err := tracefs.RandomGroup(groupPrefix) if err != nil { return nil, fmt.Errorf("randomizing group name: %w", err) } args.Group = group // Create the [k,u]probe trace event using tracefs. evt, err := tracefs.NewEvent(args) if err != nil { return nil, fmt.Errorf("creating probe entry on tracefs: %w", err) } // Kprobes are ephemeral tracepoints and share the same perf event type. fd, err := openTracepointPerfEvent(evt.ID(), args.Pid) if err != nil { // Make sure we clean up the created tracefs event when we return error. // If a livepatch handler is already active on the symbol, the write to // tracefs will succeed, a trace event will show up, but creating the // perf event will fail with EBUSY. _ = evt.Close() return nil, err } return newPerfEvent(fd, evt), nil } golang-github-cilium-ebpf-0.11.0/link/kprobe_multi.go000066400000000000000000000117341456504511400225050ustar00rootroot00000000000000package link import ( "errors" "fmt" "os" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // KprobeMultiOptions defines additional parameters that will be used // when opening a KprobeMulti Link. type KprobeMultiOptions struct { // Symbols takes a list of kernel symbol names to attach an ebpf program to. // // Mutually exclusive with Addresses. Symbols []string // Addresses takes a list of kernel symbol addresses in case they can not // be referred to by name. // // Note that only start addresses can be specified, since the fprobe API // limits the attach point to the function entry or return. // // Mutually exclusive with Symbols. Addresses []uintptr // Cookies specifies arbitrary values that can be fetched from an eBPF // program via `bpf_get_attach_cookie()`. // // If set, its length should be equal to the length of Symbols or Addresses. // Each Cookie is assigned to the Symbol or Address specified at the // corresponding slice index. Cookies []uint64 } // KprobeMulti attaches the given eBPF program to the entry point of a given set // of kernel symbols. // // The difference with Kprobe() is that multi-kprobe accomplishes this in a // single system call, making it significantly faster than attaching many // probes one at a time. // // Requires at least Linux 5.18. func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { return kprobeMulti(prog, opts, 0) } // KretprobeMulti attaches the given eBPF program to the return point of a given // set of kernel symbols. // // The difference with Kretprobe() is that multi-kprobe accomplishes this in a // single system call, making it significantly faster than attaching many // probes one at a time. // // Requires at least Linux 5.18. func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN) } func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) { if prog == nil { return nil, errors.New("cannot attach a nil program") } syms := uint32(len(opts.Symbols)) addrs := uint32(len(opts.Addresses)) cookies := uint32(len(opts.Cookies)) if syms == 0 && addrs == 0 { return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput) } if syms != 0 && addrs != 0 { return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput) } if cookies > 0 && cookies != syms && cookies != addrs { return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput) } if err := haveBPFLinkKprobeMulti(); err != nil { return nil, err } attr := &sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_KPROBE_MULTI, KprobeMultiFlags: flags, } switch { case syms != 0: attr.Count = syms attr.Syms = sys.NewStringSlicePointer(opts.Symbols) case addrs != 0: attr.Count = addrs attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0])) } if cookies != 0 { attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0])) } fd, err := sys.LinkCreateKprobeMulti(attr) if errors.Is(err, unix.ESRCH) { return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist) } if errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err) } if err != nil { return nil, err } return &kprobeMultiLink{RawLink{fd, ""}}, nil } type kprobeMultiLink struct { RawLink } var _ Link = (*kprobeMultiLink)(nil) func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error { return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported) } func (kml *kprobeMultiLink) Pin(string) error { return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported) } func (kml *kprobeMultiLink) Unpin() error { return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported) } var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_kpm_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, AttachType: ebpf.AttachTraceKprobeMulti, License: "MIT", }) if errors.Is(err, unix.E2BIG) { // Kernel doesn't support AttachType field. return internal.ErrNotSupported } if err != nil { return err } defer prog.Close() fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_KPROBE_MULTI, Count: 1, Syms: sys.NewStringSlicePointer([]string{"vprintk"}), }) switch { case errors.Is(err, unix.EINVAL): return internal.ErrNotSupported // If CONFIG_FPROBE isn't set. case errors.Is(err, unix.EOPNOTSUPP): return internal.ErrNotSupported case err != nil: return err } fd.Close() return nil }) golang-github-cilium-ebpf-0.11.0/link/kprobe_multi_test.go000066400000000000000000000065541456504511400235500ustar00rootroot00000000000000package link import ( "errors" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) var kprobeMultiSyms = []string{"vprintk", "inet6_release"} func TestKprobeMulti(t *testing.T) { testutils.SkipIfNotSupported(t, haveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") km, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms}) if err != nil { t.Fatal(err) } defer km.Close() testLink(t, km, prog) } func TestKprobeMultiInput(t *testing.T) { // Program type that loads on all kernels. Not expected to link successfully. prog := mustLoadProgram(t, ebpf.SocketFilter, 0, "") // One of Symbols or Addresses must be given. _, err := KprobeMulti(prog, KprobeMultiOptions{}) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } // Symbols and Addresses are mutually exclusive. _, err = KprobeMulti(prog, KprobeMultiOptions{ Symbols: []string{"foo"}, Addresses: []uintptr{1}, }) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } // One Symbol, two cookies.. _, err = KprobeMulti(prog, KprobeMultiOptions{ Symbols: []string{"one"}, Cookies: []uint64{2, 3}, }) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } } func TestKprobeMultiErrors(t *testing.T) { testutils.SkipIfNotSupported(t, haveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") // Nonexistent kernel symbol. _, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: []string{"bogus"}}) if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) { t.Fatalf("expected ErrNotExist or EINVAL, got: %s", err) } // Only have a negative test for addresses as it would be hard to maintain a // proper one. if _, err := KprobeMulti(prog, KprobeMultiOptions{ Addresses: []uintptr{^uintptr(0)}, }); !errors.Is(err, unix.EINVAL) { t.Fatalf("expected EINVAL, got: %s", err) } } func TestKprobeMultiCookie(t *testing.T) { testutils.SkipIfNotSupported(t, haveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") km, err := KprobeMulti(prog, KprobeMultiOptions{ Symbols: kprobeMultiSyms, Cookies: []uint64{0, 1}, }) if err != nil { t.Fatal(err) } _ = km.Close() } func TestKprobeMultiProgramCall(t *testing.T) { testutils.SkipIfNotSupported(t, haveBPFLinkKprobeMulti()) m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti) // For simplicity, just assert the increment happens with any symbol in the array. opts := KprobeMultiOptions{ Symbols: []string{"__do_sys_getpid"}, } km, err := KprobeMulti(p, opts) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() // Assert that the value at index 0 has been updated to 1. assertMapValue(t, m, 0, 1) // Close the link. if err := km.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } func TestHaveBPFLinkKprobeMulti(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFLinkKprobeMulti) } golang-github-cilium-ebpf-0.11.0/link/kprobe_test.go000066400000000000000000000240371456504511400223320ustar00rootroot00000000000000package link import ( "errors" "os" "testing" qt "github.com/frankban/quicktest" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // Global symbol, present on all tested kernels. var ksym = "vprintk" // Collection of various symbols present in all tested kernels. // Compiler optimizations result in different names for these symbols. var symTests = []string{ "echo_char.isra.0", // function optimized by -fipa-sra "proc_get_long.constprop.0", // optimized function with constant operands "unregister_kprobes.part.0", // function body that was split and partially inlined } func TestKprobe(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") for _, tt := range symTests { t.Run(tt, func(t *testing.T) { k, err := Kprobe(tt, prog, nil) if err != nil { t.Fatal(err) } defer k.Close() }) } c := qt.New(t) k, err := Kprobe("bogus", prog, nil) c.Assert(err, qt.ErrorIs, os.ErrNotExist, qt.Commentf("got error: %s", err)) if k != nil { k.Close() } k, err = Kprobe(ksym, prog, nil) c.Assert(err, qt.IsNil) defer k.Close() testLink(t, k, prog) } func TestKprobeOffset(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // The layout of a function is compiler and arch dependent, so we try to // find a valid attach target in the first few bytes of the function. for i := uint64(1); i < 16; i++ { k, err := Kprobe("inet6_release", prog, &KprobeOptions{Offset: i}) if err != nil { continue } k.Close() return } t.Fatal("Can't attach with non-zero offset") } func TestKretprobeMaxActive(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") defer prog.Close() _, err := Kprobe("do_sys_open", prog, &KprobeOptions{RetprobeMaxActive: 4096}) if !errors.Is(err, tracefs.ErrInvalidMaxActive) { t.Fatal("Expected ErrInvalidMaxActive, got", err) } k, err := Kretprobe("__put_task_struct", prog, &KprobeOptions{RetprobeMaxActive: 4096}) if testutils.IsKernelLessThan(t, "4.12") && errors.Is(err, ErrNotSupported) { t.Skip("Kernel doesn't support maxactive") } if err != nil { t.Fatal("Kretprobe with maxactive returned an error:", err) } if err := k.Close(); err != nil { t.Fatal("Closing kretprobe:", err) } } func TestKretprobe(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") for _, tt := range symTests { t.Run(tt, func(t *testing.T) { k, err := Kretprobe(tt, prog, nil) if err != nil { t.Fatal(err) } defer k.Close() }) } c := qt.New(t) k, err := Kretprobe("bogus", prog, nil) if !(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)) { t.Fatal(err) } if k != nil { k.Close() } k, err = Kretprobe(ksym, prog, nil) c.Assert(err, qt.IsNil) defer k.Close() testLink(t, k, prog) } func TestKprobeErrors(t *testing.T) { c := qt.New(t) // Invalid Kprobe incantations. Kretprobe uses the same code paths // with a different ret flag. _, err := Kprobe("", nil, nil) // empty symbol c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe("_", nil, nil) // empty prog c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe(".", &ebpf.Program{}, nil) // illegal chars in symbol c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe("foo", &ebpf.Program{}, nil) // wrong prog type c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) } // Test k(ret)probe creation using perf_kprobe PMU. func TestKprobeCreatePMU(t *testing.T) { // Requires at least 4.17 (e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU") testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU") c := qt.New(t) // kprobe happy path. printk is always present. pk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) c.Assert(err, qt.IsNil) defer pk.Close() // kretprobe happy path. pr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true}) c.Assert(err, qt.IsNil) defer pr.Close() // Expect os.ErrNotExist when specifying a non-existent kernel symbol // on kernels 4.17 and up. _, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: "bogus"}) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err)) // A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead // of ENOENT, but only for kretprobes. _, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: "bogus", Ret: true}) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err)) } // Test fallback behaviour on kernels without perf_kprobe PMU available. func TestKprobePMUUnavailable(t *testing.T) { c := qt.New(t) pk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) if err == nil { pk.Close() t.Skipf("Kernel supports perf_kprobe PMU, not asserting error.") } // Only allow a PMU creation with a valid kernel symbol to fail with ErrNotSupported. c.Assert(errors.Is(err, ErrNotSupported), qt.IsTrue, qt.Commentf("got error: %s", err)) } func BenchmarkKprobeCreatePMU(b *testing.B) { for n := 0; n < b.N; n++ { pr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) if err != nil { b.Error("error creating perf_kprobe PMU:", err) } if err := pr.Close(); err != nil { b.Error("error closing perf_kprobe PMU:", err) } } } // Test tracefs k(ret)probe creation on all kernel versions. func TestKprobeTraceFS(t *testing.T) { c := qt.New(t) // Open and close tracefs k(ret)probes, checking all errors. kp, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) c.Assert(err, qt.IsNil) c.Assert(kp.Close(), qt.IsNil) kp, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true}) c.Assert(err, qt.IsNil) c.Assert(kp.Close(), qt.IsNil) // Create two identical trace events, ensure their IDs differ. k1, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) c.Assert(err, qt.IsNil) defer k1.Close() c.Assert(k1.tracefsEvent, qt.IsNotNil) k2, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) c.Assert(err, qt.IsNil) defer k2.Close() c.Assert(k2.tracefsEvent, qt.IsNotNil) // Compare the kprobes' tracefs IDs. c.Assert(k1.tracefsEvent.ID(), qt.Not(qt.Equals), k2.tracefsEvent.ID()) // Expect an error when supplying an invalid custom group name _, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: "/"}) c.Assert(err, qt.Not(qt.IsNil)) cg := "customgroup" k3, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: cg}) c.Assert(err, qt.IsNil) defer k3.Close() c.Assert(k3.tracefsEvent.Group(), qt.Matches, `customgroup_[a-f0-9]{16}`) // Prepare probe args. args := tracefs.ProbeArgs{Type: tracefs.Kprobe, Group: "testgroup", Symbol: "symbol"} // Write a k(ret)probe event for a non-existing symbol. _, err = tracefs.NewEvent(args) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err)) // A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead // of ENOENT, but only for kretprobes. args.Ret = true _, err = tracefs.NewEvent(args) if !(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)) { t.Fatal(err) } } func BenchmarkKprobeCreateTraceFS(b *testing.B) { for n := 0; n < b.N; n++ { // Include /kprobe_events operations in the benchmark loop // because we create one per perf event. pr, err := tracefsProbe(tracefs.ProbeArgs{Symbol: ksym}) if err != nil { b.Error("error creating tracefs perf event:", err) } if err := pr.Close(); err != nil { b.Error("error closing tracefs perf event:", err) } } } func TestKprobeProgramCall(t *testing.T) { m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Open Kprobe on `sys_getpid` and attach it // to the ebpf program created above. k, err := Kprobe("sys_getpid", p, nil) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() // Assert that the value at index 0 has been updated to 1. assertMapValue(t, m, 0, 1) // Detach the Kprobe. if err := k.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } func newUpdaterMapProg(t *testing.T, typ ebpf.ProgramType, attach ebpf.AttachType) (*ebpf.Map, *ebpf.Program) { // Create ebpf map. Will contain only one key with initial value 0. m, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Fatal(err) } // Create ebpf program. When called, will set the value of key 0 in // the map created above to 1. p, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: typ, Instructions: asm.Instructions{ // u32 key = 0 asm.Mov.Imm(asm.R1, 0), asm.StoreMem(asm.RFP, -4, asm.R1, asm.Word), // u32 val = 1 asm.Mov.Imm(asm.R1, 1), asm.StoreMem(asm.RFP, -8, asm.R1, asm.Word), // bpf_map_update_elem(...) asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), asm.Mov.Reg(asm.R3, asm.RFP), asm.Add.Imm(asm.R3, -8), asm.LoadMapPtr(asm.R1, m.FD()), asm.Mov.Imm(asm.R4, 0), asm.FnMapUpdateElem.Call(), // exit 0 asm.Mov.Imm(asm.R0, 0), asm.Return(), }, AttachType: attach, License: "Dual MIT/GPL", }) if err != nil { t.Fatal(err) } // Close the program and map on test teardown. t.Cleanup(func() { m.Close() p.Close() }) return m, p } func assertMapValue(t *testing.T, m *ebpf.Map, k, v uint32) { var val uint32 if err := m.Lookup(k, &val); err != nil { t.Fatal(err) } if val != v { t.Fatalf("unexpected value: want '%d', got '%d'", v, val) } } func TestKprobeCookie(t *testing.T) { testutils.SkipOnOldKernel(t, "5.15", "bpf_perf_link") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") k, err := Kprobe(ksym, prog, &KprobeOptions{Cookie: 1000}) if err != nil { t.Fatal(err) } k.Close() } golang-github-cilium-ebpf-0.11.0/link/link.go000066400000000000000000000170621456504511400207460ustar00rootroot00000000000000package link import ( "bytes" "encoding/binary" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) var ErrNotSupported = internal.ErrNotSupported // Link represents a Program attached to a BPF hook. type Link interface { // Replace the current program with a new program. // // Passing a nil program is an error. May return an error wrapping ErrNotSupported. Update(*ebpf.Program) error // Persist a link by pinning it into a bpffs. // // May return an error wrapping ErrNotSupported. Pin(string) error // Undo a previous call to Pin. // // May return an error wrapping ErrNotSupported. Unpin() error // Close frees resources. // // The link will be broken unless it has been successfully pinned. // A link may continue past the lifetime of the process if Close is // not called. Close() error // Info returns metadata on a link. // // May return an error wrapping ErrNotSupported. Info() (*Info, error) // Prevent external users from implementing this interface. isLink() } // NewLinkFromFD creates a link from a raw fd. // // You should not use fd after calling this function. func NewLinkFromFD(fd int) (Link, error) { sysFD, err := sys.NewFD(fd) if err != nil { return nil, err } return wrapRawLink(&RawLink{fd: sysFD}) } // LoadPinnedLink loads a link that was persisted into a bpffs. func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { raw, err := loadPinnedRawLink(fileName, opts) if err != nil { return nil, err } return wrapRawLink(raw) } // wrap a RawLink in a more specific type if possible. // // The function takes ownership of raw and closes it on error. func wrapRawLink(raw *RawLink) (_ Link, err error) { defer func() { if err != nil { raw.Close() } }() info, err := raw.Info() if err != nil { return nil, err } switch info.Type { case RawTracepointType: return &rawTracepoint{*raw}, nil case TracingType: return &tracing{*raw}, nil case CgroupType: return &linkCgroup{*raw}, nil case IterType: return &Iter{*raw}, nil case NetNsType: return &NetNsLink{*raw}, nil case KprobeMultiType: return &kprobeMultiLink{*raw}, nil case PerfEventType: return nil, fmt.Errorf("recovering perf event fd: %w", ErrNotSupported) default: return raw, nil } } // ID uniquely identifies a BPF link. type ID = sys.LinkID // RawLinkOptions control the creation of a raw link. type RawLinkOptions struct { // File descriptor to attach to. This differs for each attach type. Target int // Program to attach. Program *ebpf.Program // Attach must match the attach type of Program. Attach ebpf.AttachType // BTF is the BTF of the attachment target. BTF btf.TypeID // Flags control the attach behaviour. Flags uint32 } // Info contains metadata on a link. type Info struct { Type Type ID ID Program ebpf.ProgramID extra interface{} } type TracingInfo sys.TracingLinkInfo type CgroupInfo sys.CgroupLinkInfo type NetNsInfo sys.NetNsLinkInfo type XDPInfo sys.XDPLinkInfo // Tracing returns tracing type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Tracing() *TracingInfo { e, _ := r.extra.(*TracingInfo) return e } // Cgroup returns cgroup type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Cgroup() *CgroupInfo { e, _ := r.extra.(*CgroupInfo) return e } // NetNs returns netns type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) NetNs() *NetNsInfo { e, _ := r.extra.(*NetNsInfo) return e } // ExtraNetNs returns XDP type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) XDP() *XDPInfo { e, _ := r.extra.(*XDPInfo) return e } // RawLink is the low-level API to bpf_link. // // You should consider using the higher level interfaces in this // package instead. type RawLink struct { fd *sys.FD pinnedPath string } // AttachRawLink creates a raw link. func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { if err := haveBPFLink(); err != nil { return nil, err } if opts.Target < 0 { return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } attr := sys.LinkCreateAttr{ TargetFd: uint32(opts.Target), ProgFd: uint32(progFd), AttachType: sys.AttachType(opts.Attach), TargetBtfId: opts.BTF, Flags: opts.Flags, } fd, err := sys.LinkCreate(&attr) if err != nil { return nil, fmt.Errorf("create link: %w", err) } return &RawLink{fd, ""}, nil } func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { fd, err := sys.ObjGet(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, fmt.Errorf("load pinned link: %w", err) } return &RawLink{fd, fileName}, nil } func (l *RawLink) isLink() {} // FD returns the raw file descriptor. func (l *RawLink) FD() int { return l.fd.Int() } // Close breaks the link. // // Use Pin if you want to make the link persistent. func (l *RawLink) Close() error { return l.fd.Close() } // Pin persists a link past the lifetime of the process. // // Calling Close on a pinned Link will not break the link // until the pin is removed. func (l *RawLink) Pin(fileName string) error { if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { return err } l.pinnedPath = fileName return nil } // Unpin implements the Link interface. func (l *RawLink) Unpin() error { if err := internal.Unpin(l.pinnedPath); err != nil { return err } l.pinnedPath = "" return nil } // IsPinned returns true if the Link has a non-empty pinned path. func (l *RawLink) IsPinned() bool { return l.pinnedPath != "" } // Update implements the Link interface. func (l *RawLink) Update(new *ebpf.Program) error { return l.UpdateArgs(RawLinkUpdateOptions{ New: new, }) } // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. type RawLinkUpdateOptions struct { New *ebpf.Program Old *ebpf.Program Flags uint32 } // UpdateArgs updates a link based on args. func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { newFd := opts.New.FD() if newFd < 0 { return fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } var oldFd int if opts.Old != nil { oldFd = opts.Old.FD() if oldFd < 0 { return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd) } } attr := sys.LinkUpdateAttr{ LinkFd: l.fd.Uint(), NewProgFd: uint32(newFd), OldProgFd: uint32(oldFd), Flags: opts.Flags, } return sys.LinkUpdate(&attr) } // Info returns metadata about the link. func (l *RawLink) Info() (*Info, error) { var info sys.LinkInfo if err := sys.ObjInfo(l.fd, &info); err != nil { return nil, fmt.Errorf("link info: %s", err) } var extra interface{} switch info.Type { case CgroupType: extra = &CgroupInfo{} case NetNsType: extra = &NetNsInfo{} case TracingType: extra = &TracingInfo{} case XDPType: extra = &XDPInfo{} case RawTracepointType, IterType, PerfEventType, KprobeMultiType: // Extra metadata not supported. default: return nil, fmt.Errorf("unknown link info type: %d", info.Type) } if extra != nil { buf := bytes.NewReader(info.Extra[:]) err := binary.Read(buf, internal.NativeEndian, extra) if err != nil { return nil, fmt.Errorf("cannot read extra link info: %w", err) } } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.11.0/link/link_test.go000066400000000000000000000135341456504511400220050ustar00rootroot00000000000000package link import ( "errors" "math" "os" "path/filepath" "reflect" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/fdtrace" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestRawLink(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := AttachRawLink(RawLinkOptions{ Target: int(cgroup.Fd()), Program: prog, Attach: ebpf.AttachCGroupInetEgress, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create raw link:", err) } info, err := link.Info() if err != nil { t.Fatal("Can't get link info:", err) } pi, err := prog.Info() if err != nil { t.Fatal("Can't get program info:", err) } progID, ok := pi.ID() if !ok { t.Fatal("Program ID not available in program info") } if info.Program != progID { t.Error("Link program ID doesn't match program ID") } testLink(t, &linkCgroup{*link}, prog) } func TestUnpinRawLink(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, _ := newPinnedRawLink(t, cgroup, prog) defer link.Close() qt.Assert(t, link.IsPinned(), qt.IsTrue) if err := link.Unpin(); err != nil { t.Fatal(err) } qt.Assert(t, link.IsPinned(), qt.IsFalse) } func TestRawLinkLoadPinnedWithOptions(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, path := newPinnedRawLink(t, cgroup, prog) defer link.Close() qt.Assert(t, link.IsPinned(), qt.IsTrue) // It seems like the kernel ignores BPF_F_RDONLY when updating a link, // so we can't test this. _, err := loadPinnedRawLink(path, &ebpf.LoadPinOptions{ Flags: math.MaxUint32, }) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags don't trigger an error:", err) } } func newPinnedRawLink(t *testing.T, cgroup *os.File, prog *ebpf.Program) (*RawLink, string) { t.Helper() link, err := AttachRawLink(RawLinkOptions{ Target: int(cgroup.Fd()), Program: prog, Attach: ebpf.AttachCGroupInetEgress, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create raw link:", err) } path := filepath.Join(testutils.TempBPFFS(t), "link") err = link.Pin(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } return link, path } func mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program) { t.Helper() testutils.SkipIfNotSupported(t, haveProgAttach()) return testutils.CreateCgroup(t), mustLoadProgram(t, ebpf.CGroupSKB, 0, "") } func testLink(t *testing.T, link Link, prog *ebpf.Program) { t.Helper() tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) t.Run("link/pinning", func(t *testing.T) { path := filepath.Join(tmp, "link") err = link.Pin(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("Can't pin %T: %s", link, err) } link2, err := LoadPinnedLink(path, nil) if err != nil { t.Fatalf("Can't load pinned %T: %s", link, err) } link2.Close() if reflect.TypeOf(link) != reflect.TypeOf(link2) { t.Errorf("Loading a pinned %T returns a %T", link, link2) } _, err = LoadPinnedLink(path, &ebpf.LoadPinOptions{ Flags: math.MaxUint32, }) if !errors.Is(err, unix.EINVAL) { t.Errorf("Loading a pinned %T doesn't respect flags", link) } }) t.Run("link/update", func(t *testing.T) { err := link.Update(prog) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Update returns an error:", err) } func() { // Panicking is OK defer func() { _ = recover() }() if err := link.Update(nil); err == nil { t.Fatalf("%T.Update accepts nil program", link) } }() }) t.Run("link/info", func(t *testing.T) { info, err := link.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Link info returns an error:", err) } if info.Type == 0 { t.Fatal("Failed to get link info type") } switch info.Type { case sys.BPF_LINK_TYPE_TRACING: if info.Tracing() == nil { t.Fatalf("Failed to get link tracing extra info") } case sys.BPF_LINK_TYPE_CGROUP: cg := info.Cgroup() if cg.CgroupId == 0 { t.Fatalf("Failed to get link Cgroup extra info") } case sys.BPF_LINK_TYPE_NETNS: netns := info.NetNs() if netns.AttachType == 0 { t.Fatalf("Failed to get link NetNs extra info") } case sys.BPF_LINK_TYPE_XDP: xdp := info.XDP() if xdp.Ifindex == 0 { t.Fatalf("Failed to get link XDP extra info") } } }) type FDer interface { FD() int } t.Run("from fd", func(t *testing.T) { fder, ok := link.(FDer) if !ok { t.Skip("Link doesn't allow retrieving FD") } // We need to dup the FD since NewLinkFromFD takes // ownership. dupFD, err := unix.FcntlInt(uintptr(fder.FD()), unix.F_DUPFD_CLOEXEC, 1) if err != nil { t.Fatal("Can't dup link FD:", err) } defer unix.Close(dupFD) newLink, err := NewLinkFromFD(dupFD) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create new link from dup link FD:", err) } defer newLink.Close() if reflect.TypeOf(newLink) != reflect.TypeOf(link) { t.Fatalf("Expected type %T, got %T", link, newLink) } }) if err := link.Close(); err != nil { t.Fatalf("%T.Close returns an error: %s", link, err) } } func mustLoadProgram(tb testing.TB, typ ebpf.ProgramType, attachType ebpf.AttachType, attachTo string) *ebpf.Program { tb.Helper() license := "MIT" switch typ { case ebpf.RawTracepoint, ebpf.LSM: license = "GPL" } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: typ, AttachType: attachType, AttachTo: attachTo, License: license, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return prog } golang-github-cilium-ebpf-0.11.0/link/netns.go000066400000000000000000000013011456504511400211250ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" ) // NetNsLink is a program attached to a network namespace. type NetNsLink struct { RawLink } // AttachNetNs attaches a program to a network namespace. func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) { var attach ebpf.AttachType switch t := prog.Type(); t { case ebpf.FlowDissector: attach = ebpf.AttachFlowDissector case ebpf.SkLookup: attach = ebpf.AttachSkLookup default: return nil, fmt.Errorf("can't attach %v to network namespace", t) } link, err := AttachRawLink(RawLinkOptions{ Target: ns, Program: prog, Attach: attach, }) if err != nil { return nil, err } return &NetNsLink{*link}, nil } golang-github-cilium-ebpf-0.11.0/link/netns_test.go000066400000000000000000000024271456504511400221760ustar00rootroot00000000000000package link import ( "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestSkLookup(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "sk_lookup program") prog := mustLoadProgram(t, ebpf.SkLookup, ebpf.AttachSkLookup, "") netns, err := os.Open("/proc/self/ns/net") if err != nil { t.Fatal(err) } defer netns.Close() link, err := AttachNetNs(int(netns.Fd()), prog) if err != nil { t.Fatal("Can't attach link:", err) } testLink(t, link, prog) } func createSkLookupProgram() (*ebpf.Program, error) { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SkLookup, AttachType: ebpf.AttachSkLookup, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return nil, err } return prog, nil } func ExampleAttachNetNs() { prog, err := createSkLookupProgram() if err != nil { panic(err) } defer prog.Close() // This can be a path to another netns as well. netns, err := os.Open("/proc/self/ns/net") if err != nil { panic(err) } defer netns.Close() link, err := AttachNetNs(int(netns.Fd()), prog) if err != nil { panic(err) } // The socket lookup program is now active until Close(). link.Close() } golang-github-cilium-ebpf-0.11.0/link/perf_event.go000066400000000000000000000205501456504511400221420ustar00rootroot00000000000000package link import ( "errors" "fmt" "runtime" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // Getting the terminology right is usually the hardest part. For posterity and // for staying sane during implementation: // // - trace event: Representation of a kernel runtime hook. Filesystem entries // under /events. Can be tracepoints (static), kprobes or uprobes. // Can be instantiated into perf events (see below). // - tracepoint: A predetermined hook point in the kernel. Exposed as trace // events in (sub)directories under /events. Cannot be closed or // removed, they are static. // - k(ret)probe: Ephemeral trace events based on entry or exit points of // exported kernel symbols. kprobe-based (tracefs) trace events can be // created system-wide by writing to the /kprobe_events file, or // they can be scoped to the current process by creating PMU perf events. // - u(ret)probe: Ephemeral trace events based on user provides ELF binaries // and offsets. uprobe-based (tracefs) trace events can be // created system-wide by writing to the /uprobe_events file, or // they can be scoped to the current process by creating PMU perf events. // - perf event: An object instantiated based on an existing trace event or // kernel symbol. Referred to by fd in userspace. // Exactly one eBPF program can be attached to a perf event. Multiple perf // events can be created from a single trace event. Closing a perf event // stops any further invocations of the attached eBPF program. var ( errInvalidInput = tracefs.ErrInvalidInput ) const ( perfAllThreads = -1 ) // A perfEvent represents a perf event kernel object. Exactly one eBPF program // can be attached to it. It is created based on a tracefs trace event or a // Performance Monitoring Unit (PMU). type perfEvent struct { // Trace event backing this perfEvent. May be nil. tracefsEvent *tracefs.Event // This is the perf event FD. fd *sys.FD } func newPerfEvent(fd *sys.FD, event *tracefs.Event) *perfEvent { pe := &perfEvent{event, fd} // Both event and fd have their own finalizer, but we want to // guarantee that they are closed in a certain order. runtime.SetFinalizer(pe, (*perfEvent).Close) return pe } func (pe *perfEvent) Close() error { runtime.SetFinalizer(pe, nil) if err := pe.fd.Close(); err != nil { return fmt.Errorf("closing perf event fd: %w", err) } if pe.tracefsEvent != nil { return pe.tracefsEvent.Close() } return nil } // perfEventLink represents a bpf perf link. type perfEventLink struct { RawLink pe *perfEvent } func (pl *perfEventLink) isLink() {} // Pinning requires the underlying perf event FD to stay open. // // | PerfEvent FD | BpfLink FD | Works | // |--------------|------------|-------| // | Open | Open | Yes | // | Closed | Open | No | // | Open | Closed | No (Pin() -> EINVAL) | // | Closed | Closed | No (Pin() -> EINVAL) | // // There is currently no pretty way to recover the perf event FD // when loading a pinned link, so leave as not supported for now. func (pl *perfEventLink) Pin(string) error { return fmt.Errorf("perf event link pin: %w", ErrNotSupported) } func (pl *perfEventLink) Unpin() error { return fmt.Errorf("perf event link unpin: %w", ErrNotSupported) } func (pl *perfEventLink) Close() error { if err := pl.fd.Close(); err != nil { return fmt.Errorf("perf link close: %w", err) } if err := pl.pe.Close(); err != nil { return fmt.Errorf("perf event close: %w", err) } return nil } func (pl *perfEventLink) Update(prog *ebpf.Program) error { return fmt.Errorf("perf event link update: %w", ErrNotSupported) } // perfEventIoctl implements Link and handles the perf event lifecycle // via ioctl(). type perfEventIoctl struct { *perfEvent } func (pi *perfEventIoctl) isLink() {} // Since 4.15 (e87c6bc3852b "bpf: permit multiple bpf attachments for a single perf event"), // calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array // owned by the perf event, which means multiple programs can be attached // simultaneously. // // Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event // returns EEXIST. // // Detaching a program from a perf event is currently not possible, so a // program replacement mechanism cannot be implemented for perf events. func (pi *perfEventIoctl) Update(prog *ebpf.Program) error { return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported) } func (pi *perfEventIoctl) Pin(string) error { return fmt.Errorf("perf event ioctl pin: %w", ErrNotSupported) } func (pi *perfEventIoctl) Unpin() error { return fmt.Errorf("perf event ioctl unpin: %w", ErrNotSupported) } func (pi *perfEventIoctl) Info() (*Info, error) { return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported) } // attach the given eBPF prog to the perf event stored in pe. // pe must contain a valid perf event fd. // prog's type must match the program type stored in pe. func attachPerfEvent(pe *perfEvent, prog *ebpf.Program, cookie uint64) (Link, error) { if prog == nil { return nil, errors.New("cannot attach a nil program") } if prog.FD() < 0 { return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd) } if err := haveBPFLinkPerfEvent(); err == nil { return attachPerfEventLink(pe, prog, cookie) } if cookie != 0 { return nil, fmt.Errorf("cookies are not supported: %w", ErrNotSupported) } return attachPerfEventIoctl(pe, prog) } func attachPerfEventIoctl(pe *perfEvent, prog *ebpf.Program) (*perfEventIoctl, error) { // Assign the eBPF program to the perf event. err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_SET_BPF, prog.FD()) if err != nil { return nil, fmt.Errorf("setting perf event bpf program: %w", err) } // PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values. if err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil { return nil, fmt.Errorf("enable perf event: %s", err) } return &perfEventIoctl{pe}, nil } // Use the bpf api to attach the perf event (BPF_LINK_TYPE_PERF_EVENT, 5.15+). // // https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e func attachPerfEventLink(pe *perfEvent, prog *ebpf.Program, cookie uint64) (*perfEventLink, error) { fd, err := sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{ ProgFd: uint32(prog.FD()), TargetFd: pe.fd.Uint(), AttachType: sys.BPF_PERF_EVENT, BpfCookie: cookie, }) if err != nil { return nil, fmt.Errorf("cannot create bpf perf link: %v", err) } return &perfEventLink{RawLink{fd: fd}, pe}, nil } // unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str. func unsafeStringPtr(str string) (unsafe.Pointer, error) { p, err := unix.BytePtrFromString(str) if err != nil { return nil, err } return unsafe.Pointer(p), nil } // openTracepointPerfEvent opens a tracepoint-type perf event. System-wide // [k,u]probes created by writing to /[k,u]probe_events are tracepoints // behind the scenes, and can be attached to using these perf events. func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) { attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_TRACEPOINT, Config: tid, Sample_type: unix.PERF_SAMPLE_RAW, Sample: 1, Wakeup: 1, } fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { return nil, fmt.Errorf("opening tracepoint perf event: %w", err) } return sys.NewFD(fd) } // Probe BPF perf link. // // https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307 // https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_bpf_perf_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) if err != nil { return err } defer prog.Close() _, err = sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_PERF_EVENT, }) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) golang-github-cilium-ebpf-0.11.0/link/perf_event_test.go000066400000000000000000000002701456504511400231760ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveBPFLinkPerfEvent(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFLinkPerfEvent) } golang-github-cilium-ebpf-0.11.0/link/program.go000066400000000000000000000035061456504511400214560ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type RawAttachProgramOptions struct { // File descriptor to attach to. This differs for each attach type. Target int // Program to attach. Program *ebpf.Program // Program to replace (cgroups). Replace *ebpf.Program // Attach must match the attach type of Program (and Replace). Attach ebpf.AttachType // Flags control the attach behaviour. This differs for each attach type. Flags uint32 } // RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH. // // You should use one of the higher level abstractions available in this // package if possible. func RawAttachProgram(opts RawAttachProgramOptions) error { if err := haveProgAttach(); err != nil { return err } var replaceFd uint32 if opts.Replace != nil { replaceFd = uint32(opts.Replace.FD()) } attr := sys.ProgAttachAttr{ TargetFd: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), ReplaceBpfFd: replaceFd, AttachType: uint32(opts.Attach), AttachFlags: uint32(opts.Flags), } if err := sys.ProgAttach(&attr); err != nil { return fmt.Errorf("can't attach program: %w", err) } return nil } type RawDetachProgramOptions struct { Target int Program *ebpf.Program Attach ebpf.AttachType } // RawDetachProgram is a low level wrapper around BPF_PROG_DETACH. // // You should use one of the higher level abstractions available in this // package if possible. func RawDetachProgram(opts RawDetachProgramOptions) error { if err := haveProgAttach(); err != nil { return err } attr := sys.ProgDetachAttr{ TargetFd: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), AttachType: uint32(opts.Attach), } if err := sys.ProgDetach(&attr); err != nil { return fmt.Errorf("can't detach program: %w", err) } return nil } golang-github-cilium-ebpf-0.11.0/link/program_test.go000066400000000000000000000014761456504511400225210ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestProgramAlter(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "SkSKB type") prog := mustLoadProgram(t, ebpf.SkSKB, 0, "") var sockMap *ebpf.Map sockMap, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.MapType(15), // BPF_MAP_TYPE_SOCKMAP KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } defer sockMap.Close() err = RawAttachProgram(RawAttachProgramOptions{ Target: sockMap.FD(), Program: prog, Attach: ebpf.AttachSkSKBStreamParser, }) if err != nil { t.Fatal(err) } err = RawDetachProgram(RawDetachProgramOptions{ Target: sockMap.FD(), Program: prog, Attach: ebpf.AttachSkSKBStreamParser, }) if err != nil { t.Fatal(err) } } golang-github-cilium-ebpf-0.11.0/link/query.go000066400000000000000000000033241456504511400211520ustar00rootroot00000000000000package link import ( "fmt" "os" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) // QueryOptions defines additional parameters when querying for programs. type QueryOptions struct { // Path can be a path to a cgroup, netns or LIRC2 device Path string // Attach specifies the AttachType of the programs queried for Attach ebpf.AttachType // QueryFlags are flags for BPF_PROG_QUERY, e.g. BPF_F_QUERY_EFFECTIVE QueryFlags uint32 } // QueryPrograms retrieves ProgramIDs associated with the AttachType. // // Returns (nil, nil) if there are no programs attached to the queried kernel // resource. Calling QueryPrograms on a kernel missing PROG_QUERY will result in // ErrNotSupported. func QueryPrograms(opts QueryOptions) ([]ebpf.ProgramID, error) { if haveProgQuery() != nil { return nil, fmt.Errorf("can't query program IDs: %w", ErrNotSupported) } f, err := os.Open(opts.Path) if err != nil { return nil, fmt.Errorf("can't open file: %s", err) } defer f.Close() // query the number of programs to allocate correct slice size attr := sys.ProgQueryAttr{ TargetFd: uint32(f.Fd()), AttachType: sys.AttachType(opts.Attach), QueryFlags: opts.QueryFlags, } if err := sys.ProgQuery(&attr); err != nil { return nil, fmt.Errorf("can't query program count: %w", err) } // return nil if no progs are attached if attr.ProgCount == 0 { return nil, nil } // we have at least one prog, so we query again progIds := make([]ebpf.ProgramID, attr.ProgCount) attr.ProgIds = sys.NewPointer(unsafe.Pointer(&progIds[0])) attr.ProgCount = uint32(len(progIds)) if err := sys.ProgQuery(&attr); err != nil { return nil, fmt.Errorf("can't query program IDs: %w", err) } return progIds, nil } golang-github-cilium-ebpf-0.11.0/link/query_test.go000066400000000000000000000036701456504511400222150ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestQueryPrograms(t *testing.T) { for name, fn := range map[string]func(*testing.T) (*ebpf.Program, QueryOptions){ "cgroup": queryCgroupFixtures, "netns": queryNetNSFixtures, } { t.Run(name, func(t *testing.T) { prog, opts := fn(t) ids, err := QueryPrograms(opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't query programs:", err) } progInfo, err := prog.Info() if err != nil { t.Fatal("Can't get program info:", err) } progId, _ := progInfo.ID() for _, id := range ids { if id == progId { return } } t.Fatalf("Can't find program ID %d in query result: %v", progId, ids) }) } } func queryCgroupFixtures(t *testing.T) (*ebpf.Program, QueryOptions) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, 0) if err != nil { t.Fatal("Can't create link:", err) } t.Cleanup(func() { link.Close() }) return prog, QueryOptions{Path: cgroup.Name(), Attach: ebpf.AttachCGroupInetEgress} } func queryNetNSFixtures(t *testing.T) (*ebpf.Program, QueryOptions) { testutils.SkipOnOldKernel(t, "4.20", "flow_dissector program") prog := mustLoadProgram(t, ebpf.FlowDissector, ebpf.AttachFlowDissector, "") // RawAttachProgramOptions.Target needs to be 0, as PROG_ATTACH with namespaces // only works with the threads current netns. Any other fd will be rejected. if err := RawAttachProgram(RawAttachProgramOptions{ Target: 0, Program: prog, Attach: ebpf.AttachFlowDissector, }); err != nil { t.Fatal(err) } t.Cleanup(func() { err := RawDetachProgram(RawDetachProgramOptions{ Target: 0, Program: prog, Attach: ebpf.AttachFlowDissector, }) if err != nil { t.Fatal(err) } }) return prog, QueryOptions{Path: "/proc/self/ns/net", Attach: ebpf.AttachFlowDissector} } golang-github-cilium-ebpf-0.11.0/link/raw_tracepoint.go000066400000000000000000000040711456504511400230260ustar00rootroot00000000000000package link import ( "errors" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type RawTracepointOptions struct { // Tracepoint name. Name string // Program must be of type RawTracepoint* Program *ebpf.Program } // AttachRawTracepoint links a BPF program to a raw_tracepoint. // // Requires at least Linux 4.17. func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable { return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t) } if opts.Program.FD() < 0 { return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd) } fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{ Name: sys.NewStringPointer(opts.Name), ProgFd: uint32(opts.Program.FD()), }) if err != nil { return nil, err } err = haveBPFLink() if errors.Is(err, ErrNotSupported) { // Prior to commit 70ed506c3bbc ("bpf: Introduce pinnable bpf_link abstraction") // raw_tracepoints are just a plain fd. return &simpleRawTracepoint{fd}, nil } if err != nil { return nil, err } return &rawTracepoint{RawLink{fd: fd}}, nil } type simpleRawTracepoint struct { fd *sys.FD } var _ Link = (*simpleRawTracepoint)(nil) func (frt *simpleRawTracepoint) isLink() {} func (frt *simpleRawTracepoint) Close() error { return frt.fd.Close() } func (frt *simpleRawTracepoint) Update(_ *ebpf.Program) error { return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Pin(string) error { return fmt.Errorf("pin raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Unpin() error { return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Info() (*Info, error) { return nil, fmt.Errorf("can't get raw_tracepoint info: %w", ErrNotSupported) } type rawTracepoint struct { RawLink } var _ Link = (*rawTracepoint)(nil) func (rt *rawTracepoint) Update(_ *ebpf.Program) error { return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported) } golang-github-cilium-ebpf-0.11.0/link/raw_tracepoint_test.go000066400000000000000000000014421456504511400240640ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API") prog := mustLoadProgram(t, ebpf.RawTracepoint, 0, "") link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_mkdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, prog) } func TestRawTracepoint_writable(t *testing.T) { testutils.SkipOnOldKernel(t, "5.2", "BPF_RAW_TRACEPOINT_WRITABLE API") prog := mustLoadProgram(t, ebpf.RawTracepoint, 0, "") defer prog.Close() link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_rmdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.11.0/link/socket_filter.go000066400000000000000000000016231456504511400226420ustar00rootroot00000000000000package link import ( "syscall" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/unix" ) // AttachSocketFilter attaches a SocketFilter BPF program to a socket. func AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error { rawConn, err := conn.SyscallConn() if err != nil { return err } var ssoErr error err = rawConn.Control(func(fd uintptr) { ssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD()) }) if ssoErr != nil { return ssoErr } return err } // DetachSocketFilter detaches a SocketFilter BPF program from a socket. func DetachSocketFilter(conn syscall.Conn) error { rawConn, err := conn.SyscallConn() if err != nil { return err } var ssoErr error err = rawConn.Control(func(fd uintptr) { ssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0) }) if ssoErr != nil { return ssoErr } return err } golang-github-cilium-ebpf-0.11.0/link/socket_filter_test.go000066400000000000000000000007261456504511400237040ustar00rootroot00000000000000package link import ( "net" "testing" "github.com/cilium/ebpf" ) func TestSocketFilterAttach(t *testing.T) { prog := mustLoadProgram(t, ebpf.SocketFilter, 0, "") defer prog.Close() conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) if err != nil { t.Fatal(err) } defer conn.Close() if err := AttachSocketFilter(conn, prog); err != nil { t.Fatal(err) } if err := DetachSocketFilter(conn); err != nil { t.Fatal(err) } } golang-github-cilium-ebpf-0.11.0/link/syscalls.go000066400000000000000000000063241456504511400216450ustar00rootroot00000000000000package link import ( "errors" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // Type is the kind of link. type Type = sys.LinkType // Valid link types. const ( UnspecifiedType = sys.BPF_LINK_TYPE_UNSPEC RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT TracingType = sys.BPF_LINK_TYPE_TRACING CgroupType = sys.BPF_LINK_TYPE_CGROUP IterType = sys.BPF_LINK_TYPE_ITER NetNsType = sys.BPF_LINK_TYPE_NETNS XDPType = sys.BPF_LINK_TYPE_XDP PerfEventType = sys.BPF_LINK_TYPE_PERF_EVENT KprobeMultiType = sys.BPF_LINK_TYPE_KPROBE_MULTI ) var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } // BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB, // so being able to load the program is enough to infer that we // have the syscall. prog.Close() return nil }) var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", "5.5", func() error { if err := haveProgAttach(); err != nil { return err } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, AttachType: ebpf.AttachCGroupInetIngress, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } defer prog.Close() // We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs. // If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't // present. attr := sys.ProgAttachAttr{ // We rely on this being checked after attachFlags. TargetFd: ^uint32(0), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(ebpf.AttachCGroupInetIngress), AttachFlags: uint32(flagReplace), } err = sys.ProgAttach(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error { attr := sys.LinkCreateAttr{ // This is a hopefully invalid file descriptor, which triggers EBADF. TargetFd: ^uint32(0), ProgFd: ^uint32(0), AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress), } _, err := sys.LinkCreate(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() error { attr := sys.ProgQueryAttr{ // We rely on this being checked during the syscall. // With an otherwise correct payload we expect EBADF here // as an indication that the feature is present. TargetFd: ^uint32(0), AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress), } err := sys.ProgQuery(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) golang-github-cilium-ebpf-0.11.0/link/syscalls_test.go000066400000000000000000000005501456504511400226770ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveProgAttach(t *testing.T) { testutils.CheckFeatureTest(t, haveProgAttach) } func TestHaveProgAttachReplace(t *testing.T) { testutils.CheckFeatureTest(t, haveProgAttachReplace) } func TestHaveBPFLink(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFLink) } golang-github-cilium-ebpf-0.11.0/link/tracepoint.go000066400000000000000000000035161456504511400221600ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/tracefs" ) // TracepointOptions defines additional parameters that will be used // when loading Tracepoints. type TracepointOptions struct { // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 } // Tracepoint attaches the given eBPF program to the tracepoint with the given // group and name. See /sys/kernel/tracing/events to find available // tracepoints. The top-level directory is the group, the event's subdirectory // is the name. Example: // // tp, err := Tracepoint("syscalls", "sys_enter_fork", prog, nil) // // Losing the reference to the resulting Link (tp) will close the Tracepoint // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is // only possible as of kernel 4.14 (commit cf5f5ce). func Tracepoint(group, name string, prog *ebpf.Program, opts *TracepointOptions) (Link, error) { if group == "" || name == "" { return nil, fmt.Errorf("group and name cannot be empty: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.TracePoint { return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput) } tid, err := tracefs.EventID(group, name) if err != nil { return nil, err } fd, err := openTracepointPerfEvent(tid, perfAllThreads) if err != nil { return nil, err } var cookie uint64 if opts != nil { cookie = opts.Cookie } pe := newPerfEvent(fd, nil) lnk, err := attachPerfEvent(pe, prog, cookie) if err != nil { pe.Close() return nil, err } return lnk, nil } golang-github-cilium-ebpf-0.11.0/link/tracepoint_test.go000066400000000000000000000051611456504511400232150ustar00rootroot00000000000000package link import ( "errors" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestTracepoint(t *testing.T) { // Requires at least 4.7 (98b5c2c65c29 "perf, bpf: allow bpf programs attach to tracepoints") testutils.SkipOnOldKernel(t, "4.7", "tracepoint support") prog := mustLoadProgram(t, ebpf.TracePoint, 0, "") // printk is guaranteed to be present. // Kernels before 4.14 don't support attaching to syscall tracepoints. tp, err := Tracepoint("printk", "console", prog, nil) if err != nil { t.Fatal(err) } if err := tp.Close(); err != nil { t.Error("closing tracepoint:", err) } } func TestTracepointMissing(t *testing.T) { // Requires at least 4.7 (98b5c2c65c29 "perf, bpf: allow bpf programs attach to tracepoints") testutils.SkipOnOldKernel(t, "4.7", "tracepoint support") prog := mustLoadProgram(t, ebpf.TracePoint, 0, "") _, err := Tracepoint("missing", "foobazbar", prog, nil) if !errors.Is(err, os.ErrNotExist) { t.Error("Expected os.ErrNotExist, got", err) } } func TestTracepointErrors(t *testing.T) { c := qt.New(t) // Invalid Tracepoint incantations. _, err := Tracepoint("", "", nil, nil) // empty names c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint("_", "_", nil, nil) // empty prog c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint(".", "+", &ebpf.Program{}, nil) // illegal chars in group/name c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint("foo", "bar", &ebpf.Program{}, nil) // wrong prog type c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) } func TestTracepointProgramCall(t *testing.T) { // Kernels before 4.14 don't support attaching to syscall tracepoints. testutils.SkipOnOldKernel(t, "4.14", "syscalls tracepoint support") m, p := newUpdaterMapProg(t, ebpf.TracePoint, 0) // Open Tracepoint at /sys/kernel/tracing/events/syscalls/sys_enter_getpid // and attach it to the ebpf program created above. tp, err := Tracepoint("syscalls", "sys_enter_getpid", p, nil) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() // Assert that the value at index 0 has been updated to 1. assertMapValue(t, m, 0, 1) // Detach the Tracepoint. if err := tp.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } golang-github-cilium-ebpf-0.11.0/link/tracing.go000066400000000000000000000126711456504511400214410ustar00rootroot00000000000000package link import ( "errors" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) type tracing struct { RawLink } func (f *tracing) Update(new *ebpf.Program) error { return fmt.Errorf("tracing update: %w", ErrNotSupported) } // AttachFreplace attaches the given eBPF program to the function it replaces. // // The program and name can either be provided at link time, or can be provided // at program load time. If they were provided at load time, they should be nil // and empty respectively here, as they will be ignored by the kernel. // Examples: // // AttachFreplace(dispatcher, "function", replacement) // AttachFreplace(nil, "", replacement) func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) { if (name == "") != (targetProg == nil) { return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.Extension { return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput) } var ( target int typeID btf.TypeID ) if targetProg != nil { btfHandle, err := targetProg.Handle() if err != nil { return nil, err } defer btfHandle.Close() spec, err := btfHandle.Spec(nil) if err != nil { return nil, err } var function *btf.Func if err := spec.TypeByName(name, &function); err != nil { return nil, err } target = targetProg.FD() typeID, err = spec.TypeID(function) if err != nil { return nil, err } } link, err := AttachRawLink(RawLinkOptions{ Target: target, Program: prog, Attach: ebpf.AttachNone, BTF: typeID, }) if errors.Is(err, sys.ENOTSUPP) { // This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke. return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported) } if err != nil { return nil, err } return &tracing{*link}, nil } type TracingOptions struct { // Program must be of type Tracing with attach type // AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or // AttachTraceRawTp. Program *ebpf.Program // Program attach type. Can be one of: // - AttachTraceFEntry // - AttachTraceFExit // - AttachModifyReturn // - AttachTraceRawTp // This field is optional. AttachType ebpf.AttachType // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. Cookie uint64 } type LSMOptions struct { // Program must be of type LSM with attach type // AttachLSMMac. Program *ebpf.Program // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. Cookie uint64 } // attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id. func attachBTFID(program *ebpf.Program, at ebpf.AttachType, cookie uint64) (Link, error) { if program.FD() < 0 { return nil, fmt.Errorf("invalid program %w", sys.ErrClosedFd) } var ( fd *sys.FD err error ) switch at { case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachTraceRawTp, ebpf.AttachModifyReturn, ebpf.AttachLSMMac: // Attach via BPF link fd, err = sys.LinkCreateTracing(&sys.LinkCreateTracingAttr{ ProgFd: uint32(program.FD()), AttachType: sys.AttachType(at), Cookie: cookie, }) if err == nil { break } if !errors.Is(err, unix.EINVAL) && !errors.Is(err, sys.ENOTSUPP) { return nil, fmt.Errorf("create tracing link: %w", err) } fallthrough case ebpf.AttachNone: // Attach via RawTracepointOpen if cookie > 0 { return nil, fmt.Errorf("create raw tracepoint with cookie: %w", ErrNotSupported) } fd, err = sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{ ProgFd: uint32(program.FD()), }) if errors.Is(err, sys.ENOTSUPP) { // This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke. return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported) } if err != nil { return nil, fmt.Errorf("create raw tracepoint: %w", err) } default: return nil, fmt.Errorf("invalid attach type: %s", at.String()) } raw := RawLink{fd: fd} info, err := raw.Info() if err != nil { raw.Close() return nil, err } if info.Type == RawTracepointType { // Sadness upon sadness: a Tracing program with AttachRawTp returns // a raw_tracepoint link. Other types return a tracing link. return &rawTracepoint{raw}, nil } return &tracing{raw}, nil } // AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or // a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined // in kernel modules. func AttachTracing(opts TracingOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.Tracing { return nil, fmt.Errorf("invalid program type %s, expected Tracing", t) } switch opts.AttachType { case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachModifyReturn, ebpf.AttachTraceRawTp, ebpf.AttachNone: default: return nil, fmt.Errorf("invalid attach type: %s", opts.AttachType.String()) } return attachBTFID(opts.Program, opts.AttachType, opts.Cookie) } // AttachLSM links a Linux security module (LSM) BPF Program to a BPF // hook defined in kernel modules. func AttachLSM(opts LSMOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.LSM { return nil, fmt.Errorf("invalid program type %s, expected LSM", t) } return attachBTFID(opts.Program, ebpf.AttachLSMMac, opts.Cookie) } golang-github-cilium-ebpf-0.11.0/link/tracing_test.go000066400000000000000000000066221456504511400224770ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestFreplace(t *testing.T) { testutils.SkipOnOldKernel(t, "5.10", "freplace") testutils.Files(t, testutils.Glob(t, "../testdata/freplace-*.elf"), func(t *testing.T, file string) { spec, err := ebpf.LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } if spec.ByteOrder != internal.NativeEndian { return } target, err := ebpf.NewProgram(spec.Programs["sched_process_exec"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create target program:", err) } defer target.Close() // Test attachment specified at load time spec.Programs["replacement"].AttachTarget = target replacement, err := ebpf.NewProgram(spec.Programs["replacement"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create replacement program:", err) } defer replacement.Close() freplace, err := AttachFreplace(nil, "", replacement) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create freplace:", err) } testLink(t, freplace, replacement) }) } func TestTracing(t *testing.T) { testutils.SkipOnOldKernel(t, "5.11", "BPF_LINK_TYPE_TRACING") tests := []struct { name string attachTo string programType ebpf.ProgramType programAttachType, attachTypeOpt ebpf.AttachType cookie uint64 }{ { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, }, { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, attachTypeOpt: ebpf.AttachTraceFEntry, cookie: 1, }, { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, }, { name: "AttachTraceFExit", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFExit, }, { name: "AttachModifyReturn", attachTo: "bpf_modify_return_test", programType: ebpf.Tracing, programAttachType: ebpf.AttachModifyReturn, }, { name: "AttachTraceRawTp", attachTo: "kfree_skb", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceRawTp, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { prog := mustLoadProgram(t, tt.programType, tt.programAttachType, tt.attachTo) opts := TracingOptions{Program: prog, AttachType: tt.attachTypeOpt, Cookie: tt.cookie} link, err := AttachTracing(opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } testLink(t, link, prog) if err = link.Close(); err != nil { t.Fatal(err) } }) } } func TestLSM(t *testing.T) { testutils.SkipOnOldKernel(t, "5.11", "BPF_LINK_TYPE_TRACING") prog := mustLoadProgram(t, ebpf.LSM, ebpf.AttachLSMMac, "file_mprotect") link, err := AttachLSM(LSMOptions{Program: prog}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.11.0/link/uprobe.go000066400000000000000000000221161456504511400213010ustar00rootroot00000000000000package link import ( "debug/elf" "errors" "fmt" "os" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/tracefs" ) var ( uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset" // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799 uprobeRefCtrOffsetShift = 32 haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", "4.20", func() error { _, err := os.Stat(uprobeRefCtrOffsetPMUPath) if err != nil { return internal.ErrNotSupported } return nil }) // ErrNoSymbol indicates that the given symbol was not found // in the ELF symbols table. ErrNoSymbol = errors.New("not found") ) // Executable defines an executable program on the filesystem. type Executable struct { // Path of the executable on the filesystem. path string // Parsed ELF and dynamic symbols' addresses. addresses map[string]uint64 // Keep track of symbol table lazy load. addressesOnce sync.Once } // UprobeOptions defines additional parameters that will be used // when loading Uprobes. type UprobeOptions struct { // Symbol address. Must be provided in case of external symbols (shared libs). // If set, overrides the address eventually parsed from the executable. Address uint64 // The offset relative to given symbol. Useful when tracing an arbitrary point // inside the frame of given symbol. // // Note: this field changed from being an absolute offset to being relative // to Address. Offset uint64 // Only set the uprobe on the given process ID. Useful when tracing // shared library calls or programs that have many running instances. PID int // Automatically manage SDT reference counts (semaphores). // // If this field is set, the Kernel will increment/decrement the // semaphore located in the process memory at the provided address on // probe attach/detach. // // See also: // sourceware.org/systemtap/wiki/UserSpaceProbeImplementation (Semaphore Handling) // github.com/torvalds/linux/commit/1cc33161a83d // github.com/torvalds/linux/commit/a6ca88b241d5 RefCtrOffset uint64 // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 // Prefix used for the event name if the uprobe must be attached using tracefs. // The group name will be formatted as `_`. // The default empty string is equivalent to "ebpf" as the prefix. TraceFSPrefix string } func (uo *UprobeOptions) cookie() uint64 { if uo == nil { return 0 } return uo.Cookie } // To open a new Executable, use: // // OpenExecutable("/bin/bash") // // The returned value can then be used to open Uprobe(s). func OpenExecutable(path string) (*Executable, error) { if path == "" { return nil, fmt.Errorf("path cannot be empty") } f, err := internal.OpenSafeELFFile(path) if err != nil { return nil, fmt.Errorf("parse ELF file: %w", err) } defer f.Close() if f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN { // ELF is not an executable or a shared object. return nil, errors.New("the given file is not an executable or a shared object") } return &Executable{ path: path, addresses: make(map[string]uint64), }, nil } func (ex *Executable) load(f *internal.SafeELFFile) error { syms, err := f.Symbols() if err != nil && !errors.Is(err, elf.ErrNoSymbols) { return err } dynsyms, err := f.DynamicSymbols() if err != nil && !errors.Is(err, elf.ErrNoSymbols) { return err } syms = append(syms, dynsyms...) for _, s := range syms { if elf.ST_TYPE(s.Info) != elf.STT_FUNC { // Symbol not associated with a function or other executable code. continue } address := s.Value // Loop over ELF segments. for _, prog := range f.Progs { // Skip uninteresting segments. if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 { continue } if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) { // If the symbol value is contained in the segment, calculate // the symbol offset. // // fn symbol offset = fn symbol VA - .text VA + .text offset // // stackoverflow.com/a/40249502 address = s.Value - prog.Vaddr + prog.Off break } } ex.addresses[s.Name] = address } return nil } // address calculates the address of a symbol in the executable. // // opts must not be nil. func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error) { if opts.Address > 0 { return opts.Address + opts.Offset, nil } var err error ex.addressesOnce.Do(func() { var f *internal.SafeELFFile f, err = internal.OpenSafeELFFile(ex.path) if err != nil { err = fmt.Errorf("parse ELF file: %w", err) return } defer f.Close() err = ex.load(f) }) if err != nil { return 0, fmt.Errorf("lazy load symbols: %w", err) } address, ok := ex.addresses[symbol] if !ok { return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol) } // Symbols with location 0 from section undef are shared library calls and // are relocated before the binary is executed. Dynamic linking is not // implemented by the library, so mark this as unsupported for now. // // Since only offset values are stored and not elf.Symbol, if the value is 0, // assume it's an external symbol. if address == 0 { return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+ "(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported) } return address + opts.Offset, nil } // Uprobe attaches the given eBPF program to a perf event that fires when the // given symbol starts executing in the given Executable. // For example, /bin/bash::main(): // // ex, _ = OpenExecutable("/bin/bash") // ex.Uprobe("main", prog, nil) // // When using symbols which belongs to shared libraries, // an offset must be provided via options: // // up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) // // Note: Setting the Offset field in the options supersedes the symbol's offset. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Functions provided by shared libraries can currently not be traced and // will result in an ErrNotSupported. func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, false) if err != nil { return nil, err } lnk, err := attachPerfEvent(u, prog, opts.cookie()) if err != nil { u.Close() return nil, err } return lnk, nil } // Uretprobe attaches the given eBPF program to a perf event that fires right // before the given symbol exits. For example, /bin/bash::main(): // // ex, _ = OpenExecutable("/bin/bash") // ex.Uretprobe("main", prog, nil) // // When using symbols which belongs to shared libraries, // an offset must be provided via options: // // up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) // // Note: Setting the Offset field in the options supersedes the symbol's offset. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Functions provided by shared libraries can currently not be traced and // will result in an ErrNotSupported. func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, true) if err != nil { return nil, err } lnk, err := attachPerfEvent(u, prog, opts.cookie()) if err != nil { u.Close() return nil, err } return lnk, nil } // uprobe opens a perf event for the given binary/symbol and attaches prog to it. // If ret is true, create a uretprobe. func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) { if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.Kprobe { return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput) } if opts == nil { opts = &UprobeOptions{} } offset, err := ex.address(symbol, opts) if err != nil { return nil, err } pid := opts.PID if pid == 0 { pid = perfAllThreads } if opts.RefCtrOffset != 0 { if err := haveRefCtrOffsetPMU(); err != nil { return nil, fmt.Errorf("uprobe ref_ctr_offset: %w", err) } } args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: symbol, Path: ex.path, Offset: offset, Pid: pid, RefCtrOffset: opts.RefCtrOffset, Ret: ret, Cookie: opts.Cookie, Group: opts.TraceFSPrefix, } // Use uprobe PMU if the kernel has it available. tp, err := pmuProbe(args) if err == nil { return tp, nil } if err != nil && !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err) } // Use tracefs if uprobe PMU is missing. tp, err = tracefsProbe(args) if err != nil { return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) } return tp, nil } golang-github-cilium-ebpf-0.11.0/link/uprobe_test.go000066400000000000000000000223221456504511400223370ustar00rootroot00000000000000package link import ( "errors" "go/build" "os" "os/exec" "path" "testing" qt "github.com/frankban/quicktest" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) var ( bashEx, _ = OpenExecutable("/bin/bash") bashSym = "main" ) func TestExecutable(t *testing.T) { _, err := OpenExecutable("") if err == nil { t.Fatal("create executable: expected error on empty path") } if bashEx.path != "/bin/bash" { t.Fatalf("create executable: unexpected path '%s'", bashEx.path) } _, err = bashEx.address(bashSym, &UprobeOptions{}) if err != nil { t.Fatalf("find offset: %v", err) } _, err = bashEx.address("bogus", &UprobeOptions{}) if err == nil { t.Fatal("find symbol: expected error") } } func TestExecutableOffset(t *testing.T) { c := qt.New(t) symbolOffset, err := bashEx.address(bashSym, &UprobeOptions{}) if err != nil { t.Fatal(err) } offset, err := bashEx.address(bashSym, &UprobeOptions{Address: 0x1}) if err != nil { t.Fatal(err) } c.Assert(offset, qt.Equals, uint64(0x1)) offset, err = bashEx.address(bashSym, &UprobeOptions{Offset: 0x2}) if err != nil { t.Fatal(err) } c.Assert(offset, qt.Equals, symbolOffset+0x2) offset, err = bashEx.address(bashSym, &UprobeOptions{Address: 0x1, Offset: 0x2}) if err != nil { t.Fatal(err) } c.Assert(offset, qt.Equals, uint64(0x1+0x2)) } func TestExecutableLazyLoadSymbols(t *testing.T) { c := qt.New(t) ex, err := OpenExecutable("/bin/bash") c.Assert(err, qt.IsNil) // Addresses must be empty, will be lazy loaded. c.Assert(ex.addresses, qt.DeepEquals, map[string]uint64{}) prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := ex.Uprobe(bashSym, prog, &UprobeOptions{Address: 123}) c.Assert(err, qt.IsNil) up.Close() // Addresses must still be empty as Address has been provided via options. c.Assert(ex.addresses, qt.DeepEquals, map[string]uint64{}) up, err = ex.Uprobe(bashSym, prog, nil) c.Assert(err, qt.IsNil) up.Close() // Symbol table should be loaded. c.Assert(len(ex.addresses), qt.Not(qt.Equals), 0) } func TestUprobe(t *testing.T) { c := qt.New(t) prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uprobe(bashSym, prog, nil) c.Assert(err, qt.IsNil) defer up.Close() testLink(t, up, prog) } func TestUprobeExtNotFound(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // This symbol will not be present in Executable (elf.SHN_UNDEF). _, err := bashEx.Uprobe("open", prog, nil) if err == nil { t.Fatal("expected error") } } func TestUprobeExtWithOpts(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // NB: It's not possible to invoke the uprobe since we use an arbitrary // address. up, err := bashEx.Uprobe("open", prog, &UprobeOptions{ // arm64 doesn't seem to allow addresses on the first page. Use // the first byte of the second page. Address: uint64(os.Getpagesize()), }) if err != nil { t.Fatal(err) } defer up.Close() } func TestUprobeWithPID(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: os.Getpid()}) if err != nil { t.Fatal(err) } defer up.Close() } func TestUprobeWithNonExistentPID(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // trying to open a perf event on a non-existent PID will return ESRCH. _, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: -2}) if !errors.Is(err, unix.ESRCH) { t.Fatalf("expected ESRCH, got %v", err) } } func TestUretprobe(t *testing.T) { c := qt.New(t) prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uretprobe(bashSym, prog, nil) c.Assert(err, qt.IsNil) defer up.Close() testLink(t, up, prog) } // Test u(ret)probe creation using perf_uprobe PMU. func TestUprobeCreatePMU(t *testing.T) { // Requires at least 4.17 (e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU") testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU") c := qt.New(t) // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, &UprobeOptions{}) c.Assert(err, qt.IsNil) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } // uprobe PMU pu, err := pmuProbe(args) c.Assert(err, qt.IsNil) defer pu.Close() // uretprobe PMU args.Ret = true pr, err := pmuProbe(args) c.Assert(err, qt.IsNil) defer pr.Close() } // Test fallback behaviour on kernels without perf_uprobe PMU available. func TestUprobePMUUnavailable(t *testing.T) { c := qt.New(t) // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, &UprobeOptions{}) c.Assert(err, qt.IsNil) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } pk, err := pmuProbe(args) if err == nil { pk.Close() t.Skipf("Kernel supports perf_uprobe PMU, not asserting error.") } // Expect ErrNotSupported. c.Assert(errors.Is(err, ErrNotSupported), qt.IsTrue, qt.Commentf("got error: %s", err)) } // Test tracefs u(ret)probe creation on all kernel versions. func TestUprobeTraceFS(t *testing.T) { c := qt.New(t) // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, &UprobeOptions{}) c.Assert(err, qt.IsNil) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } // Open and close tracefs u(ret)probes, checking all errors. up, err := tracefsProbe(args) c.Assert(err, qt.IsNil) c.Assert(up.Close(), qt.IsNil) args.Ret = true up, err = tracefsProbe(args) c.Assert(err, qt.IsNil) c.Assert(up.Close(), qt.IsNil) // Create two identical trace events, ensure their IDs differ. args.Ret = false u1, err := tracefsProbe(args) c.Assert(err, qt.IsNil) defer u1.Close() c.Assert(u1.tracefsEvent, qt.IsNotNil) u2, err := tracefsProbe(args) c.Assert(err, qt.IsNil) defer u2.Close() c.Assert(u2.tracefsEvent, qt.IsNotNil) // Compare the uprobes' tracefs IDs. c.Assert(u1.tracefsEvent.ID(), qt.Not(qt.Equals), u2.tracefsEvent.ID()) // Expect an error when supplying an invalid custom group name args.Group = "/" _, err = tracefsProbe(args) c.Assert(err, qt.Not(qt.IsNil)) args.Group = "customgroup" u3, err := tracefsProbe(args) c.Assert(err, qt.IsNil) defer u3.Close() c.Assert(u3.tracefsEvent.Group(), qt.Matches, `customgroup_[a-f0-9]{16}`) } func TestUprobeProgramCall(t *testing.T) { tests := []struct { name string elf string args []string sym string }{ { "bash", "/bin/bash", []string{"--help"}, "main", }, { "go-binary", path.Join(build.Default.GOROOT, "bin/go"), []string{"version"}, "main.main", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.name == "go-binary" { // https://github.com/cilium/ebpf/issues/406 testutils.SkipOnOldKernel(t, "4.14", "uprobes on Go binaries silently fail on kernel < 4.14") } m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Load the executable. ex, err := OpenExecutable(tt.elf) if err != nil { t.Fatal(err) } // Open Uprobe on the executable for the given symbol // and attach it to the ebpf program created above. u, err := ex.Uprobe(tt.sym, p, nil) if errors.Is(err, ErrNoSymbol) { // Assume bash::main and go::main.main always exists // and skip the test if the symbol can't be found as // certain OS (eg. Debian) strip binaries. t.Skipf("executable %s appear to be stripped, skipping", tt.elf) } if err != nil { t.Fatal(err) } // Trigger ebpf program call. trigger := func(t *testing.T) { if err := exec.Command(tt.elf, tt.args...).Run(); err != nil { t.Fatal(err) } } trigger(t) // Assert that the value at index 0 has been updated to 1. assertMapValue(t, m, 0, 1) // Detach the Uprobe. if err := u.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. trigger(t) // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) }) } } func TestUprobeProgramWrongPID(t *testing.T) { m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Load the '/bin/bash' executable. ex, err := OpenExecutable("/bin/bash") if err != nil { t.Fatal(err) } // Open Uprobe on '/bin/bash' for the symbol 'main' // and attach it to the ebpf program created above. // Create the perf-event with the current process' PID // to make sure the event is not fired when we will try // to trigger the program execution via exec. u, err := ex.Uprobe("main", p, &UprobeOptions{PID: os.Getpid()}) if err != nil { t.Fatal(err) } defer u.Close() // Trigger ebpf program call. if err := exec.Command("/bin/bash", "--help").Run(); err != nil { t.Fatal(err) } // Assert that the value at index 0 is still 0. assertMapValue(t, m, 0, 0) } func TestHaveRefCtrOffsetPMU(t *testing.T) { testutils.CheckFeatureTest(t, haveRefCtrOffsetPMU) } golang-github-cilium-ebpf-0.11.0/link/xdp.go000066400000000000000000000025031456504511400205760ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" ) // XDPAttachFlags represents how XDP program will be attached to interface. type XDPAttachFlags uint32 const ( // XDPGenericMode (SKB) links XDP BPF program for drivers which do // not yet support native XDP. XDPGenericMode XDPAttachFlags = 1 << (iota + 1) // XDPDriverMode links XDP BPF program into the driver’s receive path. XDPDriverMode // XDPOffloadMode offloads the entire XDP BPF program into hardware. XDPOffloadMode ) type XDPOptions struct { // Program must be an XDP BPF program. Program *ebpf.Program // Interface is the interface index to attach program to. Interface int // Flags is one of XDPAttachFlags (optional). // // Only one XDP mode should be set, without flag defaults // to driver/generic mode (best effort). Flags XDPAttachFlags } // AttachXDP links an XDP BPF program to an XDP hook. func AttachXDP(opts XDPOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.XDP { return nil, fmt.Errorf("invalid program type %s, expected XDP", t) } if opts.Interface < 1 { return nil, fmt.Errorf("invalid interface index: %d", opts.Interface) } rawLink, err := AttachRawLink(RawLinkOptions{ Program: opts.Program, Attach: ebpf.AttachXDP, Target: opts.Interface, Flags: uint32(opts.Flags), }) return rawLink, err } golang-github-cilium-ebpf-0.11.0/link/xdp_test.go000066400000000000000000000006321456504511400216360ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) const IfIndexLO = 1 func TestAttachXDP(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "BPF_LINK_TYPE_XDP") prog := mustLoadProgram(t, ebpf.XDP, 0, "") l, err := AttachXDP(XDPOptions{ Program: prog, Interface: IfIndexLO, }) if err != nil { t.Fatal(err) } testLink(t, l, prog) } golang-github-cilium-ebpf-0.11.0/linker.go000066400000000000000000000235411456504511400203370ustar00rootroot00000000000000package ebpf import ( "encoding/binary" "errors" "fmt" "io" "math" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" ) // handles stores handle objects to avoid gc cleanup type handles []*btf.Handle func (hs *handles) add(h *btf.Handle) (int, error) { if h == nil { return 0, nil } if len(*hs) == math.MaxInt16 { return 0, fmt.Errorf("can't add more than %d module FDs to fdArray", math.MaxInt16) } *hs = append(*hs, h) // return length of slice so that indexes start at 1 return len(*hs), nil } func (hs handles) fdArray() []int32 { // first element of fda is reserved as no module can be indexed with 0 fda := []int32{0} for _, h := range hs { fda = append(fda, int32(h.FD())) } return fda } func (hs handles) close() { for _, h := range hs { h.Close() } } // splitSymbols splits insns into subsections delimited by Symbol Instructions. // insns cannot be empty and must start with a Symbol Instruction. // // The resulting map is indexed by Symbol name. func splitSymbols(insns asm.Instructions) (map[string]asm.Instructions, error) { if len(insns) == 0 { return nil, errors.New("insns is empty") } if insns[0].Symbol() == "" { return nil, errors.New("insns must start with a Symbol") } var name string progs := make(map[string]asm.Instructions) for _, ins := range insns { if sym := ins.Symbol(); sym != "" { if progs[sym] != nil { return nil, fmt.Errorf("insns contains duplicate Symbol %s", sym) } name = sym } progs[name] = append(progs[name], ins) } return progs, nil } // The linker is responsible for resolving bpf-to-bpf calls between programs // within an ELF. Each BPF program must be a self-contained binary blob, // so when an instruction in one ELF program section wants to jump to // a function in another, the linker needs to pull in the bytecode // (and BTF info) of the target function and concatenate the instruction // streams. // // Later on in the pipeline, all call sites are fixed up with relative jumps // within this newly-created instruction stream to then finally hand off to // the kernel with BPF_PROG_LOAD. // // Each function is denoted by an ELF symbol and the compiler takes care of // register setup before each jump instruction. // hasFunctionReferences returns true if insns contains one or more bpf2bpf // function references. func hasFunctionReferences(insns asm.Instructions) bool { for _, i := range insns { if i.IsFunctionReference() { return true } } return false } // applyRelocations collects and applies any CO-RE relocations in insns. // // Passing a nil target will relocate against the running kernel. insns are // modified in place. func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOrder) error { var relos []*btf.CORERelocation var reloInsns []*asm.Instruction iter := insns.Iterate() for iter.Next() { if relo := btf.CORERelocationMetadata(iter.Ins); relo != nil { relos = append(relos, relo) reloInsns = append(reloInsns, iter.Ins) } } if len(relos) == 0 { return nil } if bo == nil { bo = internal.NativeEndian } fixups, err := btf.CORERelocate(relos, target, bo) if err != nil { return err } for i, fixup := range fixups { if err := fixup.Apply(reloInsns[i]); err != nil { return fmt.Errorf("fixup for %s: %w", relos[i], err) } } return nil } // flattenPrograms resolves bpf-to-bpf calls for a set of programs. // // Links all programs in names by modifying their ProgramSpec in progs. func flattenPrograms(progs map[string]*ProgramSpec, names []string) { // Pre-calculate all function references. refs := make(map[*ProgramSpec][]string) for _, prog := range progs { refs[prog] = prog.Instructions.FunctionReferences() } // Create a flattened instruction stream, but don't modify progs yet to // avoid linking multiple times. flattened := make([]asm.Instructions, 0, len(names)) for _, name := range names { flattened = append(flattened, flattenInstructions(name, progs, refs)) } // Finally, assign the flattened instructions. for i, name := range names { progs[name].Instructions = flattened[i] } } // flattenInstructions resolves bpf-to-bpf calls for a single program. // // Flattens the instructions of prog by concatenating the instructions of all // direct and indirect dependencies. // // progs contains all referenceable programs, while refs contain the direct // dependencies of each program. func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions { prog := progs[name] insns := make(asm.Instructions, len(prog.Instructions)) copy(insns, prog.Instructions) // Add all direct references of prog to the list of to be linked programs. pending := make([]string, len(refs[prog])) copy(pending, refs[prog]) // All references for which we've appended instructions. linked := make(map[string]bool) // Iterate all pending references. We can't use a range since pending is // modified in the body below. for len(pending) > 0 { var ref string ref, pending = pending[0], pending[1:] if linked[ref] { // We've already linked this ref, don't append instructions again. continue } progRef := progs[ref] if progRef == nil { // We don't have instructions that go with this reference. This // happens when calling extern functions. continue } insns = append(insns, progRef.Instructions...) linked[ref] = true // Make sure we link indirect references. pending = append(pending, refs[progRef]...) } return insns } // fixupAndValidate is called by the ELF reader right before marshaling the // instruction stream. It performs last-minute adjustments to the program and // runs some sanity checks before sending it off to the kernel. func fixupAndValidate(insns asm.Instructions) error { iter := insns.Iterate() for iter.Next() { ins := iter.Ins // Map load was tagged with a Reference, but does not contain a Map pointer. needsMap := ins.Reference() != "" || ins.Metadata.Get(kconfigMetaKey{}) != nil if ins.IsLoadFromMap() && needsMap && ins.Map() == nil { return fmt.Errorf("instruction %d: %w", iter.Index, asm.ErrUnsatisfiedMapReference) } fixupProbeReadKernel(ins) } return nil } // fixupKfuncs loops over all instructions in search for kfunc calls. // If at least one is found, the current kernels BTF and module BTFis are searched to set Instruction.Constant // and Instruction.Offset to the correct values. func fixupKfuncs(insns asm.Instructions) (handles, error) { iter := insns.Iterate() for iter.Next() { ins := iter.Ins if ins.IsKfuncCall() { goto fixups } } return nil, nil fixups: // only load the kernel spec if we found at least one kfunc call kernelSpec, err := btf.LoadKernelSpec() if err != nil { return nil, err } fdArray := make(handles, 0) for { ins := iter.Ins if !ins.IsKfuncCall() { if !iter.Next() { // break loop if this was the last instruction in the stream. break } continue } // check meta, if no meta return err kfm, _ := ins.Metadata.Get(kfuncMeta{}).(*btf.Func) if kfm == nil { return nil, fmt.Errorf("kfunc call has no kfuncMeta") } target := btf.Type((*btf.Func)(nil)) spec, module, err := findTargetInKernel(kernelSpec, kfm.Name, &target) if errors.Is(err, btf.ErrNotFound) { return nil, fmt.Errorf("kfunc %q: %w", kfm.Name, ErrNotSupported) } if err != nil { return nil, err } if err := btf.CheckTypeCompatibility(kfm.Type, target.(*btf.Func).Type); err != nil { return nil, &incompatibleKfuncError{kfm.Name, err} } id, err := spec.TypeID(target) if err != nil { return nil, err } idx, err := fdArray.add(module) if err != nil { return nil, err } ins.Constant = int64(id) ins.Offset = int16(idx) if !iter.Next() { break } } return fdArray, nil } type incompatibleKfuncError struct { name string err error } func (ike *incompatibleKfuncError) Error() string { return fmt.Sprintf("kfunc %q: %s", ike.name, ike.err) } // fixupProbeReadKernel replaces calls to bpf_probe_read_{kernel,user}(_str) // with bpf_probe_read(_str) on kernels that don't support it yet. func fixupProbeReadKernel(ins *asm.Instruction) { if !ins.IsBuiltinCall() { return } // Kernel supports bpf_probe_read_kernel, nothing to do. if haveProbeReadKernel() == nil { return } switch asm.BuiltinFunc(ins.Constant) { case asm.FnProbeReadKernel, asm.FnProbeReadUser: ins.Constant = int64(asm.FnProbeRead) case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr: ins.Constant = int64(asm.FnProbeReadStr) } } // resolveKconfigReferences creates and populates a .kconfig map if necessary. // // Returns a nil Map and no error if no references exist. func resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } var spec *MapSpec iter := insns.Iterate() for iter.Next() { meta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta) if meta != nil { spec = meta.Map break } } if spec == nil { return nil, nil } cpy := spec.Copy() if err := resolveKconfig(cpy); err != nil { return nil, err } kconfig, err := NewMap(cpy) if err != nil { return nil, err } defer closeOnError(kconfig) // Resolve all instructions which load from .kconfig map with actual map // and offset inside it. iter = insns.Iterate() for iter.Next() { meta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta) if meta == nil { continue } if meta.Map != spec { return nil, fmt.Errorf("instruction %d: reference to multiple .kconfig maps is not allowed", iter.Index) } if err := iter.Ins.AssociateMap(kconfig); err != nil { return nil, fmt.Errorf("instruction %d: %w", iter.Index, err) } // Encode a map read at the offset of the var in the datasec. iter.Ins.Constant = int64(uint64(meta.Offset) << 32) iter.Ins.Metadata.Set(kconfigMetaKey{}, nil) } return kconfig, nil } golang-github-cilium-ebpf-0.11.0/linker_test.go000066400000000000000000000103311456504511400213670ustar00rootroot00000000000000package ebpf import ( "errors" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" qt "github.com/frankban/quicktest" ) func TestFindReferences(t *testing.T) { progs := map[string]*ProgramSpec{ "entrypoint": { Type: SocketFilter, Instructions: asm.Instructions{ // Make sure the call doesn't happen at instruction 0 // to exercise the relative offset calculation. asm.Mov.Reg(asm.R0, asm.R1), asm.Call.Label("my_func"), asm.Return(), }, License: "MIT", }, "my_other_func": { Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 1337, asm.DWord).WithSymbol("my_other_func"), asm.Return(), }, }, "my_func": { Instructions: asm.Instructions{ asm.Call.Label("my_other_func").WithSymbol("my_func"), asm.Return(), }, }, } flattenPrograms(progs, []string{"entrypoint"}) prog, err := NewProgram(progs["entrypoint"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil { t.Fatal(err) } if ret != 1337 { t.Errorf("Expected return code 1337, got %d", ret) } } func TestForwardFunctionDeclaration(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/fwd_decl-*.elf"), func(t *testing.T, file string) { coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } if coll.ByteOrder != internal.NativeEndian { return } spec := coll.Programs["call_fwd"] // This program calls an unimplemented forward function declaration. _, err = NewProgram(spec) if !errors.Is(err, asm.ErrUnsatisfiedProgramReference) { t.Fatal("Expected an error wrapping ErrUnsatisfiedProgramReference, got:", err) } // Append the implementation of fwd(). spec.Instructions = append(spec.Instructions, asm.Mov.Imm32(asm.R0, 23).WithSymbol("fwd"), asm.Return(), ) // The body of the subprog we appended does not come with BTF func_infos, // so the verifier will reject it. Load without BTF. for i, ins := range spec.Instructions { if btf.FuncMetadata(&ins) != nil || ins.Source() != nil { sym := ins.Symbol() ref := ins.Reference() ins.Metadata = asm.Metadata{} spec.Instructions[i] = ins.WithSymbol(sym).WithReference(ref) } } prog, err := NewProgram(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%+v", err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil { t.Fatal("Running program:", err) } if ret != 23 { t.Fatalf("Expected 23, got %d", ret) } }) } func TestSplitSymbols(t *testing.T) { c := qt.New(t) // Splitting an empty insns results in an error. _, err := splitSymbols(asm.Instructions{}) c.Assert(err, qt.IsNotNil, qt.Commentf("empty insns")) // Splitting non-empty insns without a leading Symbol is an error. _, err = splitSymbols(asm.Instructions{ asm.Return(), }) c.Assert(err, qt.IsNotNil, qt.Commentf("insns without leading Symbol")) // Non-empty insns with a single Instruction that is a Symbol. insns := asm.Instructions{ asm.Return().WithSymbol("sym"), } m, err := splitSymbols(insns) c.Assert(err, qt.IsNil, qt.Commentf("insns with a single Symbol")) c.Assert(len(m), qt.Equals, 1) c.Assert(len(m["sym"]), qt.Equals, 1) // Insns containing duplicate Symbols. _, err = splitSymbols(asm.Instructions{ asm.Return().WithSymbol("sym"), asm.Return().WithSymbol("sym"), }) c.Assert(err, qt.IsNotNil, qt.Commentf("insns containing duplicate Symbols")) // Insns with multiple Symbols and subprogs of various lengths. m, err = splitSymbols(asm.Instructions{ asm.Return().WithSymbol("sym1"), asm.Mov.Imm(asm.R0, 0).WithSymbol("sym2"), asm.Return(), asm.Mov.Imm(asm.R0, 0).WithSymbol("sym3"), asm.Mov.Imm(asm.R0, 1), asm.Return(), asm.Mov.Imm(asm.R0, 0).WithSymbol("sym4"), asm.Mov.Imm(asm.R0, 1), asm.Mov.Imm(asm.R0, 2), asm.Return(), }) c.Assert(err, qt.IsNil, qt.Commentf("insns with multiple Symbols")) c.Assert(len(m), qt.Equals, 4) c.Assert(len(m["sym1"]), qt.Equals, 1) c.Assert(len(m["sym2"]), qt.Equals, 2) c.Assert(len(m["sym3"]), qt.Equals, 3) c.Assert(len(m["sym4"]), qt.Equals, 4) } golang-github-cilium-ebpf-0.11.0/map.go000066400000000000000000001151711456504511400176310ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "io" "math/rand" "os" "path/filepath" "reflect" "time" "unsafe" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // Errors returned by Map and MapIterator methods. var ( ErrKeyNotExist = errors.New("key does not exist") ErrKeyExist = errors.New("key already exists") ErrIterationAborted = errors.New("iteration aborted") ErrMapIncompatible = errors.New("map spec is incompatible with existing map") errMapNoBTFValue = errors.New("map spec does not contain a BTF Value") ) // MapOptions control loading a map into the kernel. type MapOptions struct { // The base path to pin maps in if requested via PinByName. // Existing maps will be re-used if they are compatible, otherwise an // error is returned. PinPath string LoadPinOptions LoadPinOptions } // MapID represents the unique ID of an eBPF map type MapID uint32 // MapSpec defines a Map. type MapSpec struct { // Name is passed to the kernel as a debug aid. Must only contain // alpha numeric and '_' characters. Name string Type MapType KeySize uint32 ValueSize uint32 MaxEntries uint32 // Flags is passed to the kernel and specifies additional map // creation attributes. Flags uint32 // Automatically pin and load a map from MapOptions.PinPath. // Generates an error if an existing pinned map is incompatible with the MapSpec. Pinning PinType // Specify numa node during map creation // (effective only if unix.BPF_F_NUMA_NODE flag is set, // which can be imported from golang.org/x/sys/unix) NumaNode uint32 // The initial contents of the map. May be nil. Contents []MapKV // Whether to freeze a map after setting its initial contents. Freeze bool // InnerMap is used as a template for ArrayOfMaps and HashOfMaps InnerMap *MapSpec // Extra trailing bytes found in the ELF map definition when using structs // larger than libbpf's bpf_map_def. nil if no trailing bytes were present. // Must be nil or empty before instantiating the MapSpec into a Map. Extra *bytes.Reader // The key and value type of this map. May be nil. Key, Value btf.Type } func (ms *MapSpec) String() string { return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags) } // Copy returns a copy of the spec. // // MapSpec.Contents is a shallow copy. func (ms *MapSpec) Copy() *MapSpec { if ms == nil { return nil } cpy := *ms cpy.Contents = make([]MapKV, len(ms.Contents)) copy(cpy.Contents, ms.Contents) cpy.InnerMap = ms.InnerMap.Copy() return &cpy } func (ms *MapSpec) clampPerfEventArraySize() error { if ms.Type != PerfEventArray { return nil } n, err := internal.PossibleCPUs() if err != nil { return fmt.Errorf("perf event array: %w", err) } if n := uint32(n); ms.MaxEntries > n { ms.MaxEntries = n } return nil } // dataSection returns the contents and BTF Datasec descriptor of the spec. func (ms *MapSpec) dataSection() ([]byte, *btf.Datasec, error) { if ms.Value == nil { return nil, nil, errMapNoBTFValue } ds, ok := ms.Value.(*btf.Datasec) if !ok { return nil, nil, fmt.Errorf("map value BTF is a %T, not a *btf.Datasec", ms.Value) } if n := len(ms.Contents); n != 1 { return nil, nil, fmt.Errorf("expected one key, found %d", n) } kv := ms.Contents[0] value, ok := kv.Value.([]byte) if !ok { return nil, nil, fmt.Errorf("value at first map key is %T, not []byte", kv.Value) } return value, ds, nil } // MapKV is used to initialize the contents of a Map. type MapKV struct { Key interface{} Value interface{} } // Compatible returns nil if an existing map may be used instead of creating // one from the spec. // // Returns an error wrapping [ErrMapIncompatible] otherwise. func (ms *MapSpec) Compatible(m *Map) error { switch { case m.typ != ms.Type: return fmt.Errorf("expected type %v, got %v: %w", ms.Type, m.typ, ErrMapIncompatible) case m.keySize != ms.KeySize: return fmt.Errorf("expected key size %v, got %v: %w", ms.KeySize, m.keySize, ErrMapIncompatible) case m.valueSize != ms.ValueSize: return fmt.Errorf("expected value size %v, got %v: %w", ms.ValueSize, m.valueSize, ErrMapIncompatible) case !(ms.Type == PerfEventArray && ms.MaxEntries == 0) && m.maxEntries != ms.MaxEntries: return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible) // BPF_F_RDONLY_PROG is set unconditionally for devmaps. Explicitly allow // this mismatch. case !((ms.Type == DevMap || ms.Type == DevMapHash) && m.flags^ms.Flags == unix.BPF_F_RDONLY_PROG) && m.flags != ms.Flags: return fmt.Errorf("expected flags %v, got %v: %w", ms.Flags, m.flags, ErrMapIncompatible) } return nil } // Map represents a Map file descriptor. // // It is not safe to close a map which is used by other goroutines. // // Methods which take interface{} arguments by default encode // them using binary.Read/Write in the machine's native endianness. // // Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler // if you require custom encoding. type Map struct { name string fd *sys.FD typ MapType keySize uint32 valueSize uint32 maxEntries uint32 flags uint32 pinnedPath string // Per CPU maps return values larger than the size in the spec fullValueSize int } // NewMapFromFD creates a map from a raw fd. // // You should not use fd after calling this function. func NewMapFromFD(fd int) (*Map, error) { f, err := sys.NewFD(fd) if err != nil { return nil, err } return newMapFromFD(f) } func newMapFromFD(fd *sys.FD) (*Map, error) { info, err := newMapInfoFromFd(fd) if err != nil { fd.Close() return nil, fmt.Errorf("get map info: %w", err) } return newMap(fd, info.Name, info.Type, info.KeySize, info.ValueSize, info.MaxEntries, info.Flags) } // NewMap creates a new Map. // // It's equivalent to calling NewMapWithOptions with default options. func NewMap(spec *MapSpec) (*Map, error) { return NewMapWithOptions(spec, MapOptions{}) } // NewMapWithOptions creates a new Map. // // Creating a map for the first time will perform feature detection // by creating small, temporary maps. // // The caller is responsible for ensuring the process' rlimit is set // sufficiently high for locking memory during map creation. This can be done // by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions. // // May return an error wrapping ErrMapIncompatible. func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) { m, err := newMapWithOptions(spec, opts) if err != nil { return nil, fmt.Errorf("creating map: %w", err) } if err := m.finalize(spec); err != nil { m.Close() return nil, fmt.Errorf("populating map: %w", err) } return m, nil } func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } switch spec.Pinning { case PinByName: if spec.Name == "" { return nil, fmt.Errorf("pin by name: missing Name") } if opts.PinPath == "" { return nil, fmt.Errorf("pin by name: missing MapOptions.PinPath") } path := filepath.Join(opts.PinPath, spec.Name) m, err := LoadPinnedMap(path, &opts.LoadPinOptions) if errors.Is(err, unix.ENOENT) { break } if err != nil { return nil, fmt.Errorf("load pinned map: %w", err) } defer closeOnError(m) if err := spec.Compatible(m); err != nil { return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err) } return m, nil case PinNone: // Nothing to do here default: return nil, fmt.Errorf("pin type %d: %w", int(spec.Pinning), ErrNotSupported) } var innerFd *sys.FD if spec.Type == ArrayOfMaps || spec.Type == HashOfMaps { if spec.InnerMap == nil { return nil, fmt.Errorf("%s requires InnerMap", spec.Type) } if spec.InnerMap.Pinning != PinNone { return nil, errors.New("inner maps cannot be pinned") } template, err := spec.InnerMap.createMap(nil, opts) if err != nil { return nil, fmt.Errorf("inner map: %w", err) } defer template.Close() // Intentionally skip populating and freezing (finalizing) // the inner map template since it will be removed shortly. innerFd = template.fd } m, err := spec.createMap(innerFd, opts) if err != nil { return nil, err } defer closeOnError(m) if spec.Pinning == PinByName { path := filepath.Join(opts.PinPath, spec.Name) if err := m.Pin(path); err != nil { return nil, fmt.Errorf("pin map to %s: %w", path, err) } } return m, nil } // createMap validates the spec's properties and creates the map in the kernel // using the given opts. It does not populate or freeze the map. func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err error) { closeOnError := func(closer io.Closer) { if err != nil { closer.Close() } } // Kernels 4.13 through 5.4 used a struct bpf_map_def that contained // additional 'inner_map_idx' and later 'numa_node' fields. // In order to support loading these definitions, tolerate the presence of // extra bytes, but require them to be zeroes. if spec.Extra != nil { if _, err := io.Copy(internal.DiscardZeroes{}, spec.Extra); err != nil { return nil, errors.New("extra contains unhandled non-zero bytes, drain before creating map") } } switch spec.Type { case ArrayOfMaps, HashOfMaps: if err := haveNestedMaps(); err != nil { return nil, err } if spec.ValueSize != 0 && spec.ValueSize != 4 { return nil, errors.New("ValueSize must be zero or four for map of map") } spec = spec.Copy() spec.ValueSize = 4 case PerfEventArray: if spec.KeySize != 0 && spec.KeySize != 4 { return nil, errors.New("KeySize must be zero or four for perf event array") } if spec.ValueSize != 0 && spec.ValueSize != 4 { return nil, errors.New("ValueSize must be zero or four for perf event array") } spec = spec.Copy() spec.KeySize = 4 spec.ValueSize = 4 if spec.MaxEntries == 0 { n, err := internal.PossibleCPUs() if err != nil { return nil, fmt.Errorf("perf event array: %w", err) } spec.MaxEntries = uint32(n) } } if spec.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { if err := haveMapMutabilityModifiers(); err != nil { return nil, fmt.Errorf("map create: %w", err) } } if spec.Flags&unix.BPF_F_MMAPABLE > 0 { if err := haveMmapableMaps(); err != nil { return nil, fmt.Errorf("map create: %w", err) } } if spec.Flags&unix.BPF_F_INNER_MAP > 0 { if err := haveInnerMaps(); err != nil { return nil, fmt.Errorf("map create: %w", err) } } if spec.Flags&unix.BPF_F_NO_PREALLOC > 0 { if err := haveNoPreallocMaps(); err != nil { return nil, fmt.Errorf("map create: %w", err) } } attr := sys.MapCreateAttr{ MapType: sys.MapType(spec.Type), KeySize: spec.KeySize, ValueSize: spec.ValueSize, MaxEntries: spec.MaxEntries, MapFlags: sys.MapFlags(spec.Flags), NumaNode: spec.NumaNode, } if inner != nil { attr.InnerMapFd = inner.Uint() } if haveObjName() == nil { attr.MapName = sys.NewObjName(spec.Name) } if spec.Key != nil || spec.Value != nil { handle, keyTypeID, valueTypeID, err := btf.MarshalMapKV(spec.Key, spec.Value) if err != nil && !errors.Is(err, btf.ErrNotSupported) { return nil, fmt.Errorf("load BTF: %w", err) } if handle != nil { defer handle.Close() // Use BTF k/v during map creation. attr.BtfFd = uint32(handle.FD()) attr.BtfKeyTypeId = keyTypeID attr.BtfValueTypeId = valueTypeID } } fd, err := sys.MapCreate(&attr) // Some map types don't support BTF k/v in earlier kernel versions. // Remove BTF metadata and retry map creation. if (errors.Is(err, sys.ENOTSUPP) || errors.Is(err, unix.EINVAL)) && attr.BtfFd != 0 { attr.BtfFd, attr.BtfKeyTypeId, attr.BtfValueTypeId = 0, 0, 0 fd, err = sys.MapCreate(&attr) } if err != nil { if errors.Is(err, unix.EPERM) { return nil, fmt.Errorf("map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } if errors.Is(err, unix.EINVAL) && attr.MaxEntries == 0 { return nil, fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err) } if errors.Is(err, unix.EINVAL) && spec.Type == UnspecifiedMap { return nil, fmt.Errorf("map create: cannot use type %s", UnspecifiedMap) } if attr.BtfFd == 0 { return nil, fmt.Errorf("map create: %w (without BTF k/v)", err) } return nil, fmt.Errorf("map create: %w", err) } defer closeOnError(fd) m, err := newMap(fd, spec.Name, spec.Type, spec.KeySize, spec.ValueSize, spec.MaxEntries, spec.Flags) if err != nil { return nil, fmt.Errorf("map create: %w", err) } return m, nil } // newMap allocates and returns a new Map structure. // Sets the fullValueSize on per-CPU maps. func newMap(fd *sys.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) { m := &Map{ name, fd, typ, keySize, valueSize, maxEntries, flags, "", int(valueSize), } if !typ.hasPerCPUValue() { return m, nil } possibleCPUs, err := internal.PossibleCPUs() if err != nil { return nil, err } m.fullValueSize = int(internal.Align(valueSize, 8)) * possibleCPUs return m, nil } func (m *Map) String() string { if m.name != "" { return fmt.Sprintf("%s(%s)#%v", m.typ, m.name, m.fd) } return fmt.Sprintf("%s#%v", m.typ, m.fd) } // Type returns the underlying type of the map. func (m *Map) Type() MapType { return m.typ } // KeySize returns the size of the map key in bytes. func (m *Map) KeySize() uint32 { return m.keySize } // ValueSize returns the size of the map value in bytes. func (m *Map) ValueSize() uint32 { return m.valueSize } // MaxEntries returns the maximum number of elements the map can hold. func (m *Map) MaxEntries() uint32 { return m.maxEntries } // Flags returns the flags of the map. func (m *Map) Flags() uint32 { return m.flags } // Info returns metadata about the map. func (m *Map) Info() (*MapInfo, error) { return newMapInfoFromFd(m.fd) } // MapLookupFlags controls the behaviour of the map lookup calls. type MapLookupFlags uint64 // LookupLock look up the value of a spin-locked map. const LookupLock MapLookupFlags = 4 // Lookup retrieves a value from a Map. // // Calls Close() on valueOut if it is of type **Map or **Program, // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. func (m *Map) Lookup(key, valueOut interface{}) error { return m.LookupWithFlags(key, valueOut, 0) } // LookupWithFlags retrieves a value from a Map with flags. // // Passing LookupLock flag will look up the value of a spin-locked // map without returning the lock. This must be specified if the // elements contain a spinlock. // // Calls Close() on valueOut if it is of type **Map or **Program, // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupPerCPU(key, valueOut, flags) } valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) if err := m.lookup(key, valuePtr, flags); err != nil { return err } return m.unmarshalValue(valueOut, valueBytes) } // LookupAndDelete retrieves and deletes a value from a Map. // // Returns ErrKeyNotExist if the key doesn't exist. func (m *Map) LookupAndDelete(key, valueOut interface{}) error { return m.LookupAndDeleteWithFlags(key, valueOut, 0) } // LookupAndDeleteWithFlags retrieves and deletes a value from a Map. // // Passing LookupLock flag will look up and delete the value of a spin-locked // map without returning the lock. This must be specified if the elements // contain a spinlock. // // Returns ErrKeyNotExist if the key doesn't exist. func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupAndDeletePerCPU(key, valueOut, flags) } valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) if err := m.lookupAndDelete(key, valuePtr, flags); err != nil { return err } return m.unmarshalValue(valueOut, valueBytes) } // LookupBytes gets a value from Map. // // Returns a nil value if a key doesn't exist. func (m *Map) LookupBytes(key interface{}) ([]byte, error) { valueBytes := make([]byte, m.fullValueSize) valuePtr := sys.NewSlicePointer(valueBytes) err := m.lookup(key, valuePtr, 0) if errors.Is(err, ErrKeyNotExist) { return nil, nil } return valueBytes, err } func (m *Map) lookupPerCPU(key, valueOut any, flags MapLookupFlags) error { valueBytes := make([]byte, m.fullValueSize) if err := m.lookup(key, sys.NewSlicePointer(valueBytes), flags); err != nil { return err } return unmarshalPerCPUValue(valueOut, int(m.valueSize), valueBytes) } func (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapLookupElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valueOut, Flags: uint64(flags), } if err = sys.MapLookupElem(&attr); err != nil { return fmt.Errorf("lookup: %w", wrapMapError(err)) } return nil } func (m *Map) lookupAndDeletePerCPU(key, valueOut any, flags MapLookupFlags) error { valueBytes := make([]byte, m.fullValueSize) if err := m.lookupAndDelete(key, sys.NewSlicePointer(valueBytes), flags); err != nil { return err } return unmarshalPerCPUValue(valueOut, int(m.valueSize), valueBytes) } func (m *Map) lookupAndDelete(key any, valuePtr sys.Pointer, flags MapLookupFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapLookupAndDeleteElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valuePtr, Flags: uint64(flags), } if err := sys.MapLookupAndDeleteElem(&attr); err != nil { return fmt.Errorf("lookup and delete: %w", wrapMapError(err)) } return nil } // MapUpdateFlags controls the behaviour of the Map.Update call. // // The exact semantics depend on the specific MapType. type MapUpdateFlags uint64 const ( // UpdateAny creates a new element or update an existing one. UpdateAny MapUpdateFlags = iota // UpdateNoExist creates a new element. UpdateNoExist MapUpdateFlags = 1 << (iota - 1) // UpdateExist updates an existing element. UpdateExist // UpdateLock updates elements under bpf_spin_lock. UpdateLock ) // Put replaces or creates a value in map. // // It is equivalent to calling Update with UpdateAny. func (m *Map) Put(key, value interface{}) error { return m.Update(key, value, UpdateAny) } // Update changes the value of a key. func (m *Map) Update(key, value any, flags MapUpdateFlags) error { if m.typ.hasPerCPUValue() { return m.updatePerCPU(key, value, flags) } valuePtr, err := m.marshalValue(value) if err != nil { return fmt.Errorf("marshal value: %w", err) } return m.update(key, valuePtr, flags) } func (m *Map) updatePerCPU(key, value any, flags MapUpdateFlags) error { valuePtr, err := marshalPerCPUValue(value, int(m.valueSize)) if err != nil { return fmt.Errorf("marshal value: %w", err) } return m.update(key, valuePtr, flags) } func (m *Map) update(key any, valuePtr sys.Pointer, flags MapUpdateFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("marshal key: %w", err) } attr := sys.MapUpdateElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valuePtr, Flags: uint64(flags), } if err = sys.MapUpdateElem(&attr); err != nil { return fmt.Errorf("update: %w", wrapMapError(err)) } return nil } // Delete removes a value. // // Returns ErrKeyNotExist if the key does not exist. func (m *Map) Delete(key interface{}) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapDeleteElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, } if err = sys.MapDeleteElem(&attr); err != nil { return fmt.Errorf("delete: %w", wrapMapError(err)) } return nil } // NextKey finds the key following an initial key. // // See NextKeyBytes for details. // // Returns ErrKeyNotExist if there is no next key. func (m *Map) NextKey(key, nextKeyOut interface{}) error { nextKeyPtr, nextKeyBytes := makeBuffer(nextKeyOut, int(m.keySize)) if err := m.nextKey(key, nextKeyPtr); err != nil { return err } if err := m.unmarshalKey(nextKeyOut, nextKeyBytes); err != nil { return fmt.Errorf("can't unmarshal next key: %w", err) } return nil } // NextKeyBytes returns the key following an initial key as a byte slice. // // Passing nil will return the first key. // // Use Iterate if you want to traverse all entries in the map. // // Returns nil if there are no more keys. func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { nextKey := make([]byte, m.keySize) nextKeyPtr := sys.NewSlicePointer(nextKey) err := m.nextKey(key, nextKeyPtr) if errors.Is(err, ErrKeyNotExist) { return nil, nil } return nextKey, err } func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error { var ( keyPtr sys.Pointer err error ) if key != nil { keyPtr, err = m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } } attr := sys.MapGetNextKeyAttr{ MapFd: m.fd.Uint(), Key: keyPtr, NextKey: nextKeyOut, } if err = sys.MapGetNextKey(&attr); err != nil { // Kernels 4.4.131 and earlier return EFAULT instead of a pointer to the // first map element when a nil key pointer is specified. if key == nil && errors.Is(err, unix.EFAULT) { var guessKey []byte guessKey, err = m.guessNonExistentKey() if err != nil { return err } // Retry the syscall with a valid non-existing key. attr.Key = sys.NewSlicePointer(guessKey) if err = sys.MapGetNextKey(&attr); err == nil { return nil } } return fmt.Errorf("next key: %w", wrapMapError(err)) } return nil } var mmapProtectedPage = internal.Memoize(func() ([]byte, error) { return unix.Mmap(-1, 0, os.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_SHARED) }) // guessNonExistentKey attempts to perform a map lookup that returns ENOENT. // This is necessary on kernels before 4.4.132, since those don't support // iterating maps from the start by providing an invalid key pointer. func (m *Map) guessNonExistentKey() ([]byte, error) { // Map a protected page and use that as the value pointer. This saves some // work copying out the value, which we're not interested in. page, err := mmapProtectedPage() if err != nil { return nil, err } valuePtr := sys.NewSlicePointer(page) randKey := make([]byte, int(m.keySize)) for i := 0; i < 4; i++ { switch i { // For hash maps, the 0 key is less likely to be occupied. They're often // used for storing data related to pointers, and their access pattern is // generally scattered across the keyspace. case 0: // An all-0xff key is guaranteed to be out of bounds of any array, since // those have a fixed key size of 4 bytes. The only corner case being // arrays with 2^32 max entries, but those are prohibitively expensive // in many environments. case 1: for r := range randKey { randKey[r] = 0xff } // Inspired by BCC, 0x55 is an alternating binary pattern (0101), so // is unlikely to be taken. case 2: for r := range randKey { randKey[r] = 0x55 } // Last ditch effort, generate a random key. case 3: rand.New(rand.NewSource(time.Now().UnixNano())).Read(randKey) } err := m.lookup(randKey, valuePtr, 0) if errors.Is(err, ErrKeyNotExist) { return randKey, nil } } return nil, errors.New("couldn't find non-existing key") } // BatchLookup looks up many elements in a map at once. // // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "prevKey" is the key to start the batch lookup from, it will // *not* be included in the results. Use nil to start at the first key. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". func (m *Map) BatchLookup(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { return m.batchLookup(sys.BPF_MAP_LOOKUP_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) } // BatchLookupAndDelete looks up many elements in a map at once, // // It then deletes all those elements. // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "prevKey" is the key to start the batch lookup from, it will // *not* be included in the results. Use nil to start at the first key. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". func (m *Map) BatchLookupAndDelete(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { return m.batchLookup(sys.BPF_MAP_LOOKUP_AND_DELETE_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) } func (m *Map) batchLookup(cmd sys.Cmd, startKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { if err := haveBatchAPI(); err != nil { return 0, err } if m.typ.hasPerCPUValue() { return 0, ErrNotSupported } keysValue := reflect.ValueOf(keysOut) if keysValue.Kind() != reflect.Slice { return 0, fmt.Errorf("keys must be a slice") } valuesValue := reflect.ValueOf(valuesOut) if valuesValue.Kind() != reflect.Slice { return 0, fmt.Errorf("valuesOut must be a slice") } count := keysValue.Len() if count != valuesValue.Len() { return 0, fmt.Errorf("keysOut and valuesOut must be the same length") } keyBuf := make([]byte, count*int(m.keySize)) keyPtr := sys.NewSlicePointer(keyBuf) valueBuf := make([]byte, count*int(m.fullValueSize)) valuePtr := sys.NewSlicePointer(valueBuf) nextPtr, nextBuf := makeBuffer(nextKeyOut, int(m.keySize)) attr := sys.MapLookupBatchAttr{ MapFd: m.fd.Uint(), Keys: keyPtr, Values: valuePtr, Count: uint32(count), OutBatch: nextPtr, } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } var err error if startKey != nil { attr.InBatch, err = marshalPtr(startKey, int(m.keySize)) if err != nil { return 0, err } } _, sysErr := sys.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) sysErr = wrapMapError(sysErr) if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) { return 0, sysErr } err = m.unmarshalKey(nextKeyOut, nextBuf) if err != nil { return 0, err } err = unmarshalBytes(keysOut, keyBuf) if err != nil { return 0, err } err = unmarshalBytes(valuesOut, valueBuf) if err != nil { return 0, err } return int(attr.Count), sysErr } // BatchUpdate updates the map with multiple keys and values // simultaneously. // "keys" and "values" must be of type slice, a pointer // to a slice or buffer will not work. func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) { if err := haveBatchAPI(); err != nil { return 0, err } if m.typ.hasPerCPUValue() { return 0, ErrNotSupported } keysValue := reflect.ValueOf(keys) if keysValue.Kind() != reflect.Slice { return 0, fmt.Errorf("keys must be a slice") } valuesValue := reflect.ValueOf(values) if valuesValue.Kind() != reflect.Slice { return 0, fmt.Errorf("values must be a slice") } var ( count = keysValue.Len() valuePtr sys.Pointer err error ) if count != valuesValue.Len() { return 0, fmt.Errorf("keys and values must be the same length") } keyPtr, err := marshalPtr(keys, count*int(m.keySize)) if err != nil { return 0, err } valuePtr, err = marshalPtr(values, count*int(m.valueSize)) if err != nil { return 0, err } attr := sys.MapUpdateBatchAttr{ MapFd: m.fd.Uint(), Keys: keyPtr, Values: valuePtr, Count: uint32(count), } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } err = sys.MapUpdateBatch(&attr) if err != nil { return int(attr.Count), fmt.Errorf("batch update: %w", wrapMapError(err)) } return int(attr.Count), nil } // BatchDelete batch deletes entries in the map by keys. // "keys" must be of type slice, a pointer to a slice or buffer will not work. func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) { if err := haveBatchAPI(); err != nil { return 0, err } if m.typ.hasPerCPUValue() { return 0, ErrNotSupported } keysValue := reflect.ValueOf(keys) if keysValue.Kind() != reflect.Slice { return 0, fmt.Errorf("keys must be a slice") } count := keysValue.Len() keyPtr, err := marshalPtr(keys, count*int(m.keySize)) if err != nil { return 0, fmt.Errorf("cannot marshal keys: %v", err) } attr := sys.MapDeleteBatchAttr{ MapFd: m.fd.Uint(), Keys: keyPtr, Count: uint32(count), } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } if err = sys.MapDeleteBatch(&attr); err != nil { return int(attr.Count), fmt.Errorf("batch delete: %w", wrapMapError(err)) } return int(attr.Count), nil } // Iterate traverses a map. // // It's safe to create multiple iterators at the same time. // // It's not possible to guarantee that all keys in a map will be // returned if there are concurrent modifications to the map. func (m *Map) Iterate() *MapIterator { return newMapIterator(m) } // Close the Map's underlying file descriptor, which could unload the // Map from the kernel if it is not pinned or in use by a loaded Program. func (m *Map) Close() error { if m == nil { // This makes it easier to clean up when iterating maps // of maps / programs. return nil } return m.fd.Close() } // FD gets the file descriptor of the Map. // // Calling this function is invalid after Close has been called. func (m *Map) FD() int { return m.fd.Int() } // Clone creates a duplicate of the Map. // // Closing the duplicate does not affect the original, and vice versa. // Changes made to the map are reflected by both instances however. // If the original map was pinned, the cloned map will not be pinned by default. // // Cloning a nil Map returns nil. func (m *Map) Clone() (*Map, error) { if m == nil { return nil, nil } dup, err := m.fd.Dup() if err != nil { return nil, fmt.Errorf("can't clone map: %w", err) } return &Map{ m.name, dup, m.typ, m.keySize, m.valueSize, m.maxEntries, m.flags, "", m.fullValueSize, }, nil } // Pin persists the map on the BPF virtual file system past the lifetime of // the process that created it . // // Calling Pin on a previously pinned map will overwrite the path, except when // the new path already exists. Re-pinning across filesystems is not supported. // You can Clone a map to pin it to a different path. // // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (m *Map) Pin(fileName string) error { if err := internal.Pin(m.pinnedPath, fileName, m.fd); err != nil { return err } m.pinnedPath = fileName return nil } // Unpin removes the persisted state for the map from the BPF virtual filesystem. // // Failed calls to Unpin will not alter the state returned by IsPinned. // // Unpinning an unpinned Map returns nil. func (m *Map) Unpin() error { if err := internal.Unpin(m.pinnedPath); err != nil { return err } m.pinnedPath = "" return nil } // IsPinned returns true if the map has a non-empty pinned path. func (m *Map) IsPinned() bool { return m.pinnedPath != "" } // Freeze prevents a map to be modified from user space. // // It makes no changes to kernel-side restrictions. func (m *Map) Freeze() error { if err := haveMapMutabilityModifiers(); err != nil { return fmt.Errorf("can't freeze map: %w", err) } attr := sys.MapFreezeAttr{ MapFd: m.fd.Uint(), } if err := sys.MapFreeze(&attr); err != nil { return fmt.Errorf("can't freeze map: %w", err) } return nil } // finalize populates the Map according to the Contents specified // in spec and freezes the Map if requested by spec. func (m *Map) finalize(spec *MapSpec) error { for _, kv := range spec.Contents { if err := m.Put(kv.Key, kv.Value); err != nil { return fmt.Errorf("putting value: key %v: %w", kv.Key, err) } } if spec.Freeze { if err := m.Freeze(); err != nil { return fmt.Errorf("freezing map: %w", err) } } return nil } func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) { if data == nil { if m.keySize == 0 { // Queues have a key length of zero, so passing nil here is valid. return sys.NewPointer(nil), nil } return sys.Pointer{}, errors.New("can't use nil as key of map") } return marshalPtr(data, int(m.keySize)) } func (m *Map) unmarshalKey(data interface{}, buf []byte) error { if buf == nil { // This is from a makeBuffer call, nothing do do here. return nil } return unmarshalBytes(data, buf) } func (m *Map) marshalValue(data interface{}) (sys.Pointer, error) { var ( buf []byte err error ) switch value := data.(type) { case *Map: if !m.typ.canStoreMap() { return sys.Pointer{}, fmt.Errorf("can't store map in %s", m.typ) } buf, err = marshalMap(value, int(m.valueSize)) case *Program: if !m.typ.canStoreProgram() { return sys.Pointer{}, fmt.Errorf("can't store program in %s", m.typ) } buf, err = marshalProgram(value, int(m.valueSize)) default: return marshalPtr(data, int(m.valueSize)) } if err != nil { return sys.Pointer{}, err } return sys.NewSlicePointer(buf), nil } func (m *Map) unmarshalValue(value interface{}, buf []byte) error { if buf == nil { // This is from a makeBuffer call, nothing do do here. return nil } if m.typ.hasPerCPUValue() { return unmarshalPerCPUValue(value, int(m.valueSize), buf) } switch value := value.(type) { case **Map: if !m.typ.canStoreMap() { return fmt.Errorf("can't read a map from %s", m.typ) } other, err := unmarshalMap(buf) if err != nil { return err } // The caller might close the map externally, so ignore errors. _ = (*value).Close() *value = other return nil case *Map: if !m.typ.canStoreMap() { return fmt.Errorf("can't read a map from %s", m.typ) } return errors.New("require pointer to *Map") case **Program: if !m.typ.canStoreProgram() { return fmt.Errorf("can't read a program from %s", m.typ) } other, err := unmarshalProgram(buf) if err != nil { return err } // The caller might close the program externally, so ignore errors. _ = (*value).Close() *value = other return nil case *Program: if !m.typ.canStoreProgram() { return fmt.Errorf("can't read a program from %s", m.typ) } return errors.New("require pointer to *Program") } return unmarshalBytes(value, buf) } // LoadPinnedMap loads a Map from a BPF file. func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) { fd, err := sys.ObjGet(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, err } m, err := newMapFromFD(fd) if err == nil { m.pinnedPath = fileName } return m, err } // unmarshalMap creates a map from a map ID encoded in host endianness. func unmarshalMap(buf []byte) (*Map, error) { if len(buf) != 4 { return nil, errors.New("map id requires 4 byte value") } id := internal.NativeEndian.Uint32(buf) return NewMapFromID(MapID(id)) } // marshalMap marshals the fd of a map into a buffer in host endianness. func marshalMap(m *Map, length int) ([]byte, error) { if length != 4 { return nil, fmt.Errorf("can't marshal map to %d bytes", length) } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, m.fd.Uint()) return buf, nil } // MapIterator iterates a Map. // // See Map.Iterate. type MapIterator struct { target *Map curKey []byte count, maxEntries uint32 done bool err error } func newMapIterator(target *Map) *MapIterator { return &MapIterator{ target: target, maxEntries: target.maxEntries, } } // Next decodes the next key and value. // // Iterating a hash map from which keys are being deleted is not // safe. You may see the same key multiple times. Iteration may // also abort with an error, see IsIterationAborted. // // Returns false if there are no more entries. You must check // the result of Err afterwards. // // See Map.Get for further caveats around valueOut. func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { if mi.err != nil || mi.done { return false } // For array-like maps NextKeyBytes returns nil only on after maxEntries // iterations. for mi.count <= mi.maxEntries { var nextKey []byte if mi.curKey == nil { // Pass nil interface to NextKeyBytes to make sure the Map's first key // is returned. If we pass an uninitialized []byte instead, it'll see a // non-nil interface and try to marshal it. nextKey, mi.err = mi.target.NextKeyBytes(nil) mi.curKey = make([]byte, mi.target.keySize) } else { nextKey, mi.err = mi.target.NextKeyBytes(mi.curKey) } if mi.err != nil { mi.err = fmt.Errorf("get next key: %w", mi.err) return false } if nextKey == nil { mi.done = true return false } // The user can get access to nextKey since unmarshalBytes // does not copy when unmarshaling into a []byte. // Make a copy to prevent accidental corruption of // iterator state. copy(mi.curKey, nextKey) mi.count++ mi.err = mi.target.Lookup(nextKey, valueOut) if errors.Is(mi.err, ErrKeyNotExist) { // Even though the key should be valid, we couldn't look up // its value. If we're iterating a hash map this is probably // because a concurrent delete removed the value before we // could get it. This means that the next call to NextKeyBytes // is very likely to restart iteration. // If we're iterating one of the fd maps like // ProgramArray it means that a given slot doesn't have // a valid fd associated. It's OK to continue to the next slot. continue } if mi.err != nil { mi.err = fmt.Errorf("look up next key: %w", mi.err) return false } mi.err = mi.target.unmarshalKey(keyOut, nextKey) return mi.err == nil } mi.err = fmt.Errorf("%w", ErrIterationAborted) return false } // Err returns any encountered error. // // The method must be called after Next returns nil. // // Returns ErrIterationAborted if it wasn't possible to do a full iteration. func (mi *MapIterator) Err() error { return mi.err } // MapGetNextID returns the ID of the next eBPF map. // // Returns ErrNotExist, if there is no next eBPF map. func MapGetNextID(startID MapID) (MapID, error) { attr := &sys.MapGetNextIdAttr{Id: uint32(startID)} return MapID(attr.NextId), sys.MapGetNextId(attr) } // NewMapFromID returns the map for a given id. // // Returns ErrNotExist, if there is no eBPF map with the given id. func NewMapFromID(id MapID) (*Map, error) { fd, err := sys.MapGetFdById(&sys.MapGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, err } return newMapFromFD(fd) } golang-github-cilium-ebpf-0.11.0/map_test.go000066400000000000000000001370541456504511400206740ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "math" "os" "path/filepath" "reflect" "sort" "testing" "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) var ( spec1 = &MapSpec{ Name: "foo", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } ) // newHash returns a new Map of type Hash. Cleanup is handled automatically. func newHash(t *testing.T) *Map { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { t.Fatal(err) } t.Cleanup(func() { hash.Close() }) return hash } func TestMap(t *testing.T) { m := createArray(t) defer m.Close() t.Log(m) if err := m.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't put:", err) } if err := m.Put(uint32(1), uint32(4242)); err != nil { t.Fatal("Can't put:", err) } m2, err := m.Clone() if err != nil { t.Fatal("Can't clone map:", err) } defer m2.Close() m.Close() m = m2 var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } var k uint32 if err := m.NextKey(uint32(0), &k); err != nil { t.Fatal("Can't get:", err) } if k != 1 { t.Error("Want key 1, got", k) } } func TestBatchAPIArray(t *testing.T) { if err := haveBatchAPI(); err != nil { t.Skipf("batch api not available: %v", err) } m, err := NewMap(&MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } defer m.Close() var ( nextKey uint32 keys = []uint32{0, 1} values = []uint32{42, 4242} lookupKeys = make([]uint32, 2) lookupValues = make([]uint32, 2) deleteKeys = make([]uint32, 2) deleteValues = make([]uint32, 2) ) count, err := m.BatchUpdate(keys, values, nil) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil) if err != nil { t.Fatalf("BatchLookup: %v", err) } if count != len(lookupKeys) { t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys)) } if nextKey != lookupKeys[1] { t.Fatalf("BatchLookup: expected nextKey, %d, to be the same as the lastKey returned, %d", nextKey, lookupKeys[1]) } if !reflect.DeepEqual(keys, lookupKeys) { t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys) } if !reflect.DeepEqual(values, lookupValues) { t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues) } _, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil) if !errors.Is(err, ErrNotSupported) { t.Fatalf("BatchLookUpDelete: expected error %v, but got %v", ErrNotSupported, err) } } func TestBatchAPIHash(t *testing.T) { if err := haveBatchAPI(); err != nil { t.Skipf("batch api not available: %v", err) } m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 10, }) if err != nil { t.Fatal(err) } defer m.Close() var ( nextKey uint32 keys = []uint32{0, 1} values = []uint32{42, 4242} lookupKeys = make([]uint32, 2) lookupValues = make([]uint32, 2) deleteKeys = make([]uint32, 2) deleteValues = make([]uint32, 2) ) count, err := m.BatchUpdate(keys, values, nil) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookup: expected %v got %v", ErrKeyNotExist, err) } if count != len(lookupKeys) { t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys)) } sort.Slice(lookupKeys, func(i, j int) bool { return lookupKeys[i] < lookupKeys[j] }) if !reflect.DeepEqual(keys, lookupKeys) { t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys) } sort.Slice(lookupValues, func(i, j int) bool { return lookupValues[i] < lookupValues[j] }) if !reflect.DeepEqual(values, lookupValues) { t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues) } count, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookupAndDelete: expected %v got %v", ErrKeyNotExist, err) } if count != len(deleteKeys) { t.Fatalf("BatchLookupAndDelete: returned %d results, expected %d", count, len(deleteKeys)) } sort.Slice(deleteKeys, func(i, j int) bool { return deleteKeys[i] < deleteKeys[j] }) if !reflect.DeepEqual(keys, deleteKeys) { t.Errorf("BatchUpdate and BatchLookupAndDelete keys disagree: %v %v", keys, deleteKeys) } sort.Slice(deleteValues, func(i, j int) bool { return deleteValues[i] < deleteValues[j] }) if !reflect.DeepEqual(values, deleteValues) { t.Errorf("BatchUpdate and BatchLookupAndDelete values disagree: %v %v", values, deleteValues) } if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) { t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err) } } func TestBatchAPIMapDelete(t *testing.T) { if err := haveBatchAPI(); err != nil { t.Skipf("batch api not available: %v", err) } m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 10, }) if err != nil { t.Fatal(err) } defer m.Close() var ( keys = []uint32{0, 1} values = []uint32{42, 4242} ) count, err := m.BatchUpdate(keys, values, nil) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } count, err = m.BatchDelete(keys, nil) if err != nil { t.Fatalf("BatchDelete: %v", err) } if count != len(keys) { t.Fatalf("BatchDelete: expected %d deletions got %d", len(keys), count) } if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) { t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err) } } func TestMapClose(t *testing.T) { m := createArray(t) if err := m.Close(); err != nil { t.Fatal("Can't close map:", err) } if err := m.Put(uint32(0), uint32(42)); !errors.Is(err, sys.ErrClosedFd) { t.Fatal("Put doesn't check for closed fd", err) } if _, err := m.LookupBytes(uint32(0)); !errors.Is(err, sys.ErrClosedFd) { t.Fatal("Get doesn't check for closed fd", err) } } func TestBatchMapWithLock(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "MAP BATCH BPF_F_LOCK") testutils.Files(t, testutils.Glob(t, "./testdata/map_spin_lock-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } if spec.ByteOrder != internal.NativeEndian { return } coll, err := NewCollection(spec) if err != nil { t.Fatal("Can't parse ELF:", err) } defer coll.Close() type spinLockValue struct { Cnt uint32 Padding uint32 } m, ok := coll.Maps["spin_lock_map"] if !ok { t.Fatal(err) } keys := []uint32{0, 1} values := []spinLockValue{{Cnt: 42}, {Cnt: 4242}} count, err := m.BatchUpdate(keys, values, &BatchOptions{ElemFlags: uint64(UpdateLock)}) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } nextKey := uint32(0) lookupKeys := make([]uint32, 2) lookupValues := make([]spinLockValue, 2) count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, &BatchOptions{ElemFlags: uint64(LookupLock)}) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookup: %v", err) } if count != 2 { t.Fatalf("BatchLookup: expected two keys, got %d", count) } nextKey = uint32(0) deleteKeys := []uint32{0, 1} deleteValues := make([]spinLockValue, 2) count, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookupAndDelete: %v", err) } if count != 2 { t.Fatalf("BatchLookupAndDelete: expected two keys, got %d", count) } }) } func TestMapWithLock(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "MAP BPF_F_LOCK") testutils.Files(t, testutils.Glob(t, "./testdata/map_spin_lock-*.elf"), func(t *testing.T, file string) { spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } if spec.ByteOrder != internal.NativeEndian { return } coll, err := NewCollection(spec) if err != nil { t.Fatal("Can't parse ELF:", err) } defer coll.Close() type spinLockValue struct { Cnt uint32 Padding uint32 } m, ok := coll.Maps["spin_lock_map"] if !ok { t.Fatal(err) } key := uint32(1) value := spinLockValue{Cnt: 5} err = m.Update(key, value, UpdateLock) if err != nil { t.Fatal(err) } value.Cnt = 0 err = m.LookupWithFlags(&key, &value, LookupLock) if err != nil { t.Fatal(err) } if value.Cnt != 5 { t.Fatalf("Want value 5, got %d", value.Cnt) } t.Run("LookupAndDelete", func(t *testing.T) { testutils.SkipOnOldKernel(t, "5.14", "LOOKUP_AND_DELETE flags") value.Cnt = 0 err = m.LookupAndDeleteWithFlags(&key, &value, LookupLock) if err != nil { t.Fatal(err) } if value.Cnt != 5 { t.Fatalf("Want value 5, got %d", value.Cnt) } err = m.LookupWithFlags(&key, &value, LookupLock) if err != nil && !errors.Is(err, ErrKeyNotExist) { t.Fatal(err) } }) }) } func TestMapCloneNil(t *testing.T) { m, err := (*Map)(nil).Clone() if err != nil { t.Fatal(err) } if m != nil { t.Fatal("Cloning a nil map doesn't return nil") } } func TestMapPin(t *testing.T) { m := createArray(t) c := qt.New(t) defer m.Close() if err := m.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't put:", err) } tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "map") if err := m.Pin(path); err != nil { testutils.SkipIfNotSupported(t, err) t.Fatal(err) } pinned := m.IsPinned() c.Assert(pinned, qt.IsTrue) m.Close() m, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m.Close() var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } } func TestNestedMapPin(t *testing.T) { m, err := NewMap(&MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m.Close() tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) path := filepath.Join(tmp, "nested") if err := m.Pin(path); err != nil { t.Fatal(err) } m.Close() m, err = LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m.Close() } func TestNestedMapPinNested(t *testing.T) { if _, err := NewMap(&MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Name: "inner", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, }, }); err == nil { t.Error("Inner maps should not be pinnable") } } func TestMapPinMultiple(t *testing.T) { testutils.SkipOnOldKernel(t, "4.9", "atomic re-pinning was introduced in 4.9 series") tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := spec1.Copy() m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Can't create map:", err) } defer m1.Close() pinned := m1.IsPinned() c.Assert(pinned, qt.IsTrue) newPath := filepath.Join(tmp, "bar") err = m1.Pin(newPath) testutils.SkipIfNotSupported(t, err) c.Assert(err, qt.IsNil) oldPath := filepath.Join(tmp, spec.Name) if _, err := os.Stat(oldPath); err == nil { t.Fatal("Previous pinned map path still exists:", err) } m2, err := LoadPinnedMap(newPath, nil) c.Assert(err, qt.IsNil) pinned = m2.IsPinned() c.Assert(pinned, qt.IsTrue) defer m2.Close() } func TestMapPinWithEmptyPath(t *testing.T) { m := createArray(t) c := qt.New(t) defer m.Close() err := m.Pin("") c.Assert(err, qt.Not(qt.IsNil)) } func TestMapPinFailReplace(t *testing.T) { tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := spec1.Copy() spec2 := spec1.Copy() spec2.Name = spec1.Name + "bar" m, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Failed to create map:", err) } defer m.Close() m2, err := NewMapWithOptions(spec2, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Failed to create map2:", err) } defer m2.Close() c.Assert(m.IsPinned(), qt.IsTrue) newPath := filepath.Join(tmp, spec2.Name) c.Assert(m.Pin(newPath), qt.Not(qt.IsNil), qt.Commentf("Pin didn't"+ " fail new path from replacing an existing path")) } func TestMapUnpin(t *testing.T) { tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := spec1.Copy() m, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Failed to create map:", err) } defer m.Close() pinned := m.IsPinned() c.Assert(pinned, qt.IsTrue) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) c.Assert(err, qt.IsNil) defer m2.Close() if err = m.Unpin(); err != nil { t.Fatal("Failed to unpin map:", err) } if _, err := os.Stat(path); err == nil { t.Fatal("Pinned map path still exists after unpinning:", err) } } func TestMapLoadPinned(t *testing.T) { tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := spec1.Copy() m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) c.Assert(err, qt.IsNil) defer m1.Close() pinned := m1.IsPinned() c.Assert(pinned, qt.IsTrue) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) c.Assert(err, qt.IsNil) defer m2.Close() pinned = m2.IsPinned() c.Assert(pinned, qt.IsTrue) } func TestMapLoadReusePinned(t *testing.T) { c := qt.New(t) for _, typ := range []MapType{Array, Hash, DevMap, DevMapHash} { t.Run(typ.String(), func(t *testing.T) { if typ == DevMap { testutils.SkipOnOldKernel(t, "4.14", "devmap") } if typ == DevMapHash { testutils.SkipOnOldKernel(t, "5.4", "devmap_hash") } tmp := testutils.TempBPFFS(t) spec := &MapSpec{ Name: "pinmap", Type: typ, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) c.Assert(err, qt.IsNil) defer m1.Close() m2, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) c.Assert(err, qt.IsNil) defer m2.Close() }) } } func TestMapLoadPinnedUnpin(t *testing.T) { tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := spec1.Copy() m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) c.Assert(err, qt.IsNil) defer m1.Close() pinned := m1.IsPinned() c.Assert(pinned, qt.IsTrue) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) c.Assert(err, qt.IsNil) defer m2.Close() err = m1.Unpin() c.Assert(err, qt.IsNil) err = m2.Unpin() c.Assert(err, qt.IsNil) } func TestMapLoadPinnedWithOptions(t *testing.T) { // Introduced in commit 6e71b04a8224. testutils.SkipOnOldKernel(t, "4.15", "file_flags in BPF_OBJ_GET") array := createArray(t) defer array.Close() tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "map") if err := array.Pin(path); err != nil { t.Fatal(err) } if err := array.Put(uint32(0), uint32(123)); err != nil { t.Fatal(err) } array.Close() t.Run("read-only", func(t *testing.T) { array, err := LoadPinnedMap(path, &LoadPinOptions{ ReadOnly: true, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load map:", err) } defer array.Close() if err := array.Put(uint32(0), uint32(1)); !errors.Is(err, unix.EPERM) { t.Fatal("Expected EPERM from Put, got", err) } }) t.Run("write-only", func(t *testing.T) { array, err := LoadPinnedMap(path, &LoadPinOptions{ WriteOnly: true, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load map:", err) } defer array.Close() var value uint32 if err := array.Lookup(uint32(0), &value); !errors.Is(err, unix.EPERM) { t.Fatal("Expected EPERM from Lookup, got", err) } }) } func TestMapPinFlags(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := &MapSpec{ Name: "map", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } m, err := NewMapWithOptions(spec, MapOptions{ PinPath: tmp, }) qt.Assert(t, err, qt.IsNil) m.Close() _, err = NewMapWithOptions(spec, MapOptions{ PinPath: tmp, LoadPinOptions: LoadPinOptions{ Flags: math.MaxUint32, }, }) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags should trigger EINVAL:", err) } } func createArray(t *testing.T) *Map { t.Helper() m, err := NewMap(&MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } return m } func TestMapQueue(t *testing.T) { testutils.SkipOnOldKernel(t, "4.20", "map type queue") m, err := NewMap(&MapSpec{ Type: Queue, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } defer m.Close() for _, v := range []uint32{42, 4242} { if err := m.Put(nil, v); err != nil { t.Fatalf("Can't put %d: %s", v, err) } } var v uint32 if err := m.LookupAndDelete(nil, &v); err != nil { t.Fatal("Can't lookup and delete element:", err) } if v != 42 { t.Error("Want value 42, got", v) } v = 0 if err := m.LookupAndDelete(nil, unsafe.Pointer(&v)); err != nil { t.Fatal("Can't lookup and delete element using unsafe.Pointer:", err) } if v != 4242 { t.Error("Want value 4242, got", v) } if err := m.LookupAndDelete(nil, &v); !errors.Is(err, ErrKeyNotExist) { t.Fatal("Lookup and delete on empty Queue:", err) } } func TestMapInMap(t *testing.T) { for _, typ := range []MapType{ArrayOfMaps, HashOfMaps} { t.Run(typ.String(), func(t *testing.T) { spec := &MapSpec{ Type: typ, KeySize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, } inner, err := NewMap(spec.InnerMap) if err != nil { t.Fatal(err) } if err := inner.Put(uint32(1), uint32(4242)); err != nil { t.Fatal(err) } defer inner.Close() outer, err := NewMap(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer outer.Close() if err := outer.Put(uint32(0), inner); err != nil { t.Fatal("Can't put inner map:", err) } var inner2 *Map if err := outer.Lookup(uint32(0), &inner2); err != nil { t.Fatal("Can't lookup 0:", err) } defer inner2.Close() var v uint32 if err := inner2.Lookup(uint32(1), &v); err != nil { t.Fatal("Can't lookup 1 in inner2:", err) } if v != 4242 { t.Error("Expected value 4242, got", v) } inner2.Close() // Make sure we can still access the original map if err := inner.Lookup(uint32(1), &v); err != nil { t.Fatal("Can't lookup 1 in inner:", err) } if v != 4242 { t.Error("Expected value 4242, got", v) } }) } } func TestNewMapInMapFromFD(t *testing.T) { nested, err := NewMap(&MapSpec{ Type: ArrayOfMaps, KeySize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer nested.Close() // Do not copy this, use Clone instead. another, err := NewMapFromFD(dupFD(t, nested.FD())) if err != nil { t.Fatal("Can't create a new nested map from an FD") } another.Close() } func TestPerfEventArray(t *testing.T) { specs := []*MapSpec{ {Type: PerfEventArray}, {Type: PerfEventArray, KeySize: 4}, {Type: PerfEventArray, ValueSize: 4}, } for _, spec := range specs { m, err := NewMap(spec) if err != nil { t.Errorf("Can't create perf event array from %v: %s", spec, err) } else { m.Close() } } } func createMapInMap(t *testing.T, typ MapType) *Map { t.Helper() spec := &MapSpec{ Type: typ, KeySize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, } m, err := NewMap(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } return m } func TestMapInMapValueSize(t *testing.T) { spec := &MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 0, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, } m, err := NewMap(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } m.Close() spec.ValueSize = 4 m, err = NewMap(spec) if err != nil { t.Fatal(err) } m.Close() spec.ValueSize = 1 if _, err := NewMap(spec); err == nil { t.Fatal("Expected an error") } } func TestIterateEmptyMap(t *testing.T) { makeMap := func(t *testing.T, mapType MapType) *Map { m, err := NewMap(&MapSpec{ Type: mapType, KeySize: 4, ValueSize: 8, MaxEntries: 2, }) if errors.Is(err, unix.EINVAL) { t.Skip(mapType, "is not supported") } if err != nil { t.Fatal("Can't create map:", err) } t.Cleanup(func() { m.Close() }) return m } for _, mapType := range []MapType{ Hash, SockHash, } { t.Run(mapType.String(), func(t *testing.T) { m := makeMap(t, mapType) entries := m.Iterate() var key string var value uint32 if entries.Next(&key, &value) != false { t.Error("Empty hash should not be iterable") } if err := entries.Err(); err != nil { t.Error("Empty hash shouldn't return an error:", err) } }) } for _, mapType := range []MapType{ Array, SockMap, } { t.Run(mapType.String(), func(t *testing.T) { m := makeMap(t, mapType) entries := m.Iterate() var key string var value uint32 for entries.Next(&key, &value) { // Some empty arrays like sockmap don't return any keys. } if err := entries.Err(); err != nil { t.Error("Empty array shouldn't return an error:", err) } }) } } func TestMapIterate(t *testing.T) { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } defer hash.Close() if err := hash.Put("hello", uint32(21)); err != nil { t.Fatal(err) } if err := hash.Put("world", uint32(42)); err != nil { t.Fatal(err) } var key string var value uint32 var keys []string entries := hash.Iterate() for entries.Next(&key, &value) { keys = append(keys, key) } if err := entries.Err(); err != nil { t.Fatal(err) } sort.Strings(keys) if n := len(keys); n != 2 { t.Fatal("Expected to get 2 keys, have", n) } if keys[0] != "hello" { t.Error("Expected index 0 to be hello, got", keys[0]) } if keys[1] != "world" { t.Error("Expected index 1 to be hello, got", keys[1]) } } func TestMapIterateHashKeyOneByteFull(t *testing.T) { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 1, ValueSize: 1, MaxEntries: 256, }) if err != nil { t.Fatal(err) } defer hash.Close() for i := 0; i < int(hash.MaxEntries()); i++ { if err := hash.Put(uint8(i), uint8(i)); err != nil { t.Fatal(err) } } var key uint8 var value uint8 var keys int entries := hash.Iterate() for entries.Next(&key, &value) { if key != value { t.Fatalf("Expected key == value, got key %v value %v", key, value) } keys++ } if err := entries.Err(); err != nil { t.Fatal(err) } if keys != int(hash.MaxEntries()) { t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), keys) } } func TestMapGuessNonExistentKey(t *testing.T) { tests := []struct { name string mapType MapType keys []uint32 }{ { "empty", Hash, []uint32{}, }, { "all zero key", Hash, []uint32{0}, }, { "all ones key", Hash, []uint32{math.MaxUint32}, }, { "alternating bits key", Hash, []uint32{0x5555_5555}, }, { "all special patterns", Hash, []uint32{0, math.MaxUint32, 0x5555_5555}, }, { "empty", Array, []uint32{}, }, { "all zero key", Array, []uint32{0}, }, { "full", Array, []uint32{0, 1}, }, } for _, tt := range tests { t.Run(fmt.Sprintf("%s: %s", tt.mapType, tt.name), func(t *testing.T) { maxEntries := uint32(len(tt.keys)) if maxEntries == 0 { maxEntries = 1 } m, err := NewMap(&MapSpec{ Type: tt.mapType, KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, }) if err != nil { t.Fatal(err) } defer m.Close() for _, key := range tt.keys { if err := m.Put(key, key); err != nil { t.Fatal(err) } } guess, err := m.guessNonExistentKey() if err != nil { t.Fatal(err) } if len(guess) != int(m.keySize) { t.Fatal("Guessed key has wrong size") } var value uint32 if err := m.Lookup(guess, &value); !errors.Is(err, unix.ENOENT) { t.Fatal("Doesn't return ENOENT:", err) } }) } t.Run("Hash: full", func(t *testing.T) { const n = math.MaxUint8 + 1 hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 1, ValueSize: 1, MaxEntries: n, }) if err != nil { t.Fatal(err) } defer hash.Close() for i := 0; i < n; i++ { if err := hash.Put(uint8(i), uint8(i)); err != nil { t.Fatal(err) } } _, err = hash.guessNonExistentKey() if err == nil { t.Fatal("guessNonExistentKey doesn't return error on full hash table") } }) } func TestNotExist(t *testing.T) { hash := newHash(t) var tmp uint32 err := hash.Lookup("hello", &tmp) if !errors.Is(err, ErrKeyNotExist) { t.Error("Lookup doesn't return ErrKeyNotExist") } buf, err := hash.LookupBytes("hello") if err != nil { t.Error("Looking up non-existent key return an error:", err) } if buf != nil { t.Error("LookupBytes returns non-nil buffer for non-existent key") } if err := hash.Delete("hello"); !errors.Is(err, ErrKeyNotExist) { t.Error("Deleting unknown key doesn't return ErrKeyNotExist", err) } var k = []byte{1, 2, 3, 4, 5} if err := hash.NextKey(&k, &tmp); !errors.Is(err, ErrKeyNotExist) { t.Error("Looking up next key in empty map doesn't return a non-existing error", err) } if err := hash.NextKey(nil, &tmp); !errors.Is(err, ErrKeyNotExist) { t.Error("Looking up next key in empty map doesn't return a non-existing error", err) } } func TestExist(t *testing.T) { hash := newHash(t) if err := hash.Put("hello", uint32(21)); err != nil { t.Errorf("Failed to put key/value pair into hash: %v", err) } if err := hash.Update("hello", uint32(42), UpdateNoExist); !errors.Is(err, ErrKeyExist) { t.Error("Updating existing key doesn't return ErrKeyExist") } } func TestIterateMapInMap(t *testing.T) { const idx = uint32(1) parent := createMapInMap(t, ArrayOfMaps) defer parent.Close() a := createArray(t) defer a.Close() if err := parent.Put(idx, a); err != nil { t.Fatal(err) } var ( key uint32 m *Map entries = parent.Iterate() ) if !entries.Next(&key, &m) { t.Fatal("Iterator encountered error:", entries.Err()) } m.Close() if key != 1 { t.Error("Iterator didn't skip first entry") } if m == nil { t.Fatal("Map is nil") } } func TestPerCPUMarshaling(t *testing.T) { for _, typ := range []MapType{PerCPUHash, PerCPUArray, LRUCPUHash} { t.Run(typ.String(), func(t *testing.T) { numCPU, err := internal.PossibleCPUs() if err != nil { t.Fatal(err) } if numCPU < 2 { t.Skip("Test requires at least two CPUs") } if typ == PerCPUHash || typ == PerCPUArray { testutils.SkipOnOldKernel(t, "4.6", "per-CPU hash and array") } if typ == LRUCPUHash { testutils.SkipOnOldKernel(t, "4.10", "LRU per-CPU hash") } arr, err := NewMap(&MapSpec{ Type: typ, KeySize: 4, ValueSize: 5, MaxEntries: 1, }) if err != nil { t.Fatal(err) } defer arr.Close() values := []*customEncoding{ {"hello"}, {"world"}, } if err := arr.Put(uint32(0), values); err != nil { t.Fatal(err) } // Make sure unmarshaling works on slices containing pointers var retrieved []*customEncoding if err := arr.Lookup(uint32(0), &retrieved); err != nil { t.Fatal("Can't retrieve key 0:", err) } for i, want := range []string{"HELLO", "WORLD"} { if retrieved[i] == nil { t.Error("First item is nil") } else if have := retrieved[i].data; have != want { t.Errorf("Put doesn't use BinaryMarshaler, expected %s but got %s", want, have) } } }) } } type bpfCgroupStorageKey struct { CgroupInodeId uint64 AttachType AttachType _ [4]byte // Padding } func TestCgroupPerCPUStorageMarshaling(t *testing.T) { numCPU, err := internal.PossibleCPUs() if err != nil { t.Fatal(err) } if numCPU < 2 { t.Skip("Test requires at least two CPUs") } testutils.SkipOnOldKernel(t, "5.9", "per-CPU CGoup storage with write from user space support") cgroup := testutils.CreateCgroup(t) arr, err := NewMap(&MapSpec{ Type: PerCPUCGroupStorage, KeySize: uint32(unsafe.Sizeof(bpfCgroupStorageKey{})), ValueSize: uint32(unsafe.Sizeof(uint64(0))), }) if err != nil { t.Fatal(err) } t.Cleanup(func() { arr.Close() }) prog, err := NewProgram(&ProgramSpec{ Type: CGroupSKB, AttachType: AttachCGroupInetEgress, License: "MIT", Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R1, arr.FD()), asm.Mov.Imm(asm.R2, 0), asm.FnGetLocalStorage.Call(), asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { t.Fatal(err) } defer prog.Close() progAttachAttrs := sys.ProgAttachAttr{ TargetFd: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), AttachFlags: 0, ReplaceBpfFd: 0, } err = sys.ProgAttach(&progAttachAttrs) if err != nil { t.Fatal(err) } defer func() { attr := sys.ProgDetachAttr{ TargetFd: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), } if err := sys.ProgDetach(&attr); err != nil { t.Fatal(err) } }() var mapKey = &bpfCgroupStorageKey{ CgroupInodeId: testutils.GetCgroupIno(t, cgroup), AttachType: AttachCGroupInetEgress, } values := []uint64{1, 2} if err := arr.Put(mapKey, values); err != nil { t.Fatalf("Can't set cgroup %s storage: %s", cgroup.Name(), err) } var retrieved []uint64 if err := arr.Lookup(mapKey, &retrieved); err != nil { t.Fatalf("Can't retrieve cgroup %s storage: %s", cgroup.Name(), err) } for i, want := range []uint64{1, 2} { if retrieved[i] == 0 { t.Errorf("Item %d is 0", i) } else if have := retrieved[i]; have != want { t.Errorf("PerCPUCGroupStorage map is not correctly unmarshaled, expected %d but got %d", want, have) } } } func TestMapMarshalUnsafe(t *testing.T) { m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Fatal(err) } defer m.Close() key := uint32(1) value := uint32(42) if err := m.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { t.Fatal(err) } var res uint32 if err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&res)); err != nil { t.Fatal("Can't get item:", err) } var sum uint32 iter := m.Iterate() for iter.Next(&key, unsafe.Pointer(&res)) { sum += res } if err := iter.Err(); err != nil { t.Fatal(err) } if res != 42 { t.Fatalf("Expected 42, got %d", res) } iter = m.Iterate() iter.Next(unsafe.Pointer(&key), &res) if err := iter.Err(); err != nil { t.Error(err) } if key != 1 { t.Errorf("Expected key 1, got %d", key) } if err := m.Delete(unsafe.Pointer(&key)); err != nil { t.Fatal("Can't delete:", err) } } func TestMapName(t *testing.T) { if err := haveObjName(); err != nil { t.Skip(err) } m, err := NewMap(&MapSpec{ Name: "test", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Fatal(err) } defer m.Close() var info sys.MapInfo if err := sys.ObjInfo(m.fd, &info); err != nil { t.Fatal(err) } if name := unix.ByteSliceToString(info.Name[:]); name != "test" { t.Error("Expected name to be test, got", name) } } func TestMapFromFD(t *testing.T) { m := createArray(t) defer m.Close() if err := m.Put(uint32(0), uint32(123)); err != nil { t.Fatal(err) } // If you're thinking about copying this, don't. Use // Clone() instead. m2, err := NewMapFromFD(dupFD(t, m.FD())) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m2.Close() var val uint32 if err := m2.Lookup(uint32(0), &val); err != nil { t.Fatal("Can't look up key:", err) } if val != 123 { t.Error("Wrong value") } } func TestMapContents(t *testing.T) { spec := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, Contents: []MapKV{ {uint32(0), uint32(23)}, {uint32(1), uint32(42)}, }, } m, err := NewMap(spec) if err != nil { t.Fatal("Can't create map:", err) } defer m.Close() var value uint32 if err := m.Lookup(uint32(0), &value); err != nil { t.Error("Can't look up key 0:", err) } else if value != 23 { t.Errorf("Incorrect value for key 0, expected 23, have %d", value) } if err := m.Lookup(uint32(1), &value); err != nil { t.Error("Can't look up key 1:", err) } else if value != 42 { t.Errorf("Incorrect value for key 0, expected 23, have %d", value) } spec.Contents = []MapKV{ // Key is larger than MaxEntries {uint32(14), uint32(0)}, } if _, err = NewMap(spec); err == nil { t.Error("Invalid contents should be rejected") } } func TestMapFreeze(t *testing.T) { arr := createArray(t) defer arr.Close() err := arr.Freeze() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't freeze map:", err) } if err := arr.Put(uint32(0), uint32(1)); err == nil { t.Error("Freeze doesn't prevent modification from user space") } } func TestMapGetNextID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_map_get_next_id") var next MapID var err error // Ensure there is at least one map on the system. _ = newHash(t) if next, err = MapGetNextID(MapID(0)); err != nil { t.Fatal("Can't get next ID:", err) } if next == MapID(0) { t.Fatal("Expected next ID other than 0") } // As there can be multiple eBPF maps, we loop over all of them and // make sure, the IDs increase and the last call will return ErrNotExist for { last := next if next, err = MapGetNextID(last); err != nil { if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } break } if next <= last { t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last) } } } func TestNewMapFromID(t *testing.T) { hash := newHash(t) info, err := hash.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Couldn't get map info:", err) } id, ok := info.ID() if !ok { t.Skip("Map ID not supported") } hash2, err := NewMapFromID(id) if err != nil { t.Fatalf("Can't get map for ID %d: %v", id, err) } hash2.Close() // As there can be multiple maps, we use max(uint32) as MapID to trigger an expected error. _, err = NewMapFromID(MapID(math.MaxUint32)) if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } } func TestMapPinning(t *testing.T) { tmp := testutils.TempBPFFS(t) c := qt.New(t) spec := &MapSpec{ Name: "test", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } m1, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Can't create map:", err) } defer m1.Close() pinned := m1.IsPinned() c.Assert(pinned, qt.IsTrue) m1Info, err := m1.Info() c.Assert(err, qt.IsNil) if err := m1.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't write value:", err) } m2, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create map:", err) } defer m2.Close() m2Info, err := m2.Info() c.Assert(err, qt.IsNil) if m1ID, ok := m1Info.ID(); ok { m2ID, _ := m2Info.ID() c.Assert(m2ID, qt.Equals, m1ID) } var value uint32 if err := m2.Lookup(uint32(0), &value); err != nil { t.Fatal("Can't read from map:", err) } if value != 42 { t.Fatal("Pinning doesn't use pinned maps") } spec.KeySize = 8 m3, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err == nil { m3.Close() t.Fatalf("Opening a pinned map with a mismatching spec did not fail") } if !errors.Is(err, ErrMapIncompatible) { t.Fatalf("Opening a pinned map with a mismatching spec failed with the wrong error") } } type benchValue struct { ID uint32 Val16 uint16 Val16_2 uint16 Name [8]byte LID uint64 } type customBenchValue benchValue func (cbv *customBenchValue) UnmarshalBinary(buf []byte) error { cbv.ID = internal.NativeEndian.Uint32(buf) cbv.Val16 = internal.NativeEndian.Uint16(buf[4:]) cbv.Val16_2 = internal.NativeEndian.Uint16(buf[6:]) copy(cbv.Name[:], buf[8:]) cbv.LID = internal.NativeEndian.Uint64(buf[16:]) return nil } func (cbv *customBenchValue) MarshalBinary() ([]byte, error) { buf := make([]byte, 24) internal.NativeEndian.PutUint32(buf, cbv.ID) internal.NativeEndian.PutUint16(buf[4:], cbv.Val16) internal.NativeEndian.PutUint16(buf[6:], cbv.Val16_2) copy(buf[8:], cbv.Name[:]) internal.NativeEndian.PutUint64(buf[16:], cbv.LID) return buf, nil } type benchKey struct { id uint64 } func (bk *benchKey) MarshalBinary() ([]byte, error) { buf := make([]byte, 8) internal.NativeEndian.PutUint64(buf, bk.id) return buf, nil } func BenchmarkMarshaling(b *testing.B) { newMap := func(valueSize uint32) *Map { m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 8, ValueSize: valueSize, MaxEntries: 1, }) if err != nil { b.Fatal(err) } return m } key := uint64(0) m := newMap(24) if err := m.Put(key, benchValue{}); err != nil { b.Fatal(err) } b.Cleanup(func() { m.Close() }) b.Run("ValueUnmarshalReflect", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var value benchValue for i := 0; i < b.N; i++ { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyMarshalReflect", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var value benchValue for i := 0; i < b.N; i++ { err := m.Lookup(&key, unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("ValueBinaryUnmarshaler", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var value customBenchValue for i := 0; i < b.N; i++ { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyBinaryMarshaler", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var key benchKey var value customBenchValue for i := 0; i < b.N; i++ { err := m.Lookup(&key, unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyValueUnsafe", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var value benchValue for i := 0; i < b.N; i++ { err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) } func BenchmarkPerCPUMarshalling(b *testing.B) { newMap := func(valueSize uint32) *Map { m, err := NewMap(&MapSpec{ Type: PerCPUHash, KeySize: 8, ValueSize: valueSize, MaxEntries: 1, }) if err != nil { b.Fatal(err) } return m } key := uint64(1) val := []uint64{1, 2, 3, 4, 5, 6, 7, 8} m := newMap(8) if err := m.Put(key, val[0:]); err != nil { b.Fatal(err) } b.Cleanup(func() { m.Close() }) b.Run("reflection", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() var value []uint64 for i := 0; i < b.N; i++ { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) } func BenchmarkMap(b *testing.B) { m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { b.Fatal(err) } b.Cleanup(func() { m.Close() }) if err := m.Put(uint32(0), uint32(42)); err != nil { b.Fatal(err) } b.Run("Lookup", func(b *testing.B) { var key, value uint32 b.ReportAllocs() for i := 0; i < b.N; i++ { err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)) if err != nil { b.Fatal(err) } } }) b.Run("Update", func(b *testing.B) { var key, value uint32 b.ReportAllocs() for i := 0; i < b.N; i++ { err := m.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), UpdateAny) if err != nil { b.Fatal(err) } } }) b.Run("NextKey", func(b *testing.B) { var key uint32 b.ReportAllocs() for i := 0; i < b.N; i++ { err := m.NextKey(nil, unsafe.Pointer(&key)) if err != nil { b.Fatal(err) } } }) b.Run("Delete", func(b *testing.B) { var key uint32 b.ReportAllocs() for i := 0; i < b.N; i++ { err := m.Delete(unsafe.Pointer(&key)) if err != nil && !errors.Is(err, ErrKeyNotExist) { b.Fatal(err) } } }) } func BenchmarkIterate(b *testing.B) { m, err := NewMap(&MapSpec{ Type: Hash, KeySize: 8, ValueSize: 8, MaxEntries: 1000, }) if err != nil { b.Fatal(err) } b.Cleanup(func() { m.Close() }) var ( n = m.MaxEntries() keys = make([]uint64, n) values = make([]uint64, n) ) for i := 0; uint32(i) < n; i++ { keys[i] = uint64(i) values[i] = uint64(i) } if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.Run("MapIterator", func(b *testing.B) { var k, v uint64 b.ReportAllocs() for i := 0; i < b.N; i++ { iter := m.Iterate() for iter.Next(&k, &v) { continue } if err := iter.Err(); err != nil { b.Fatal(err) } } }) b.Run("MapIteratorDelete", func(b *testing.B) { var k, v uint64 b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() iter := m.Iterate() for iter.Next(&k, &v) { if err := m.Delete(&k); err != nil { b.Fatal(err) } } if err := iter.Err(); err != nil { b.Fatal(err) } } }) b.Run("BatchLookup", func(b *testing.B) { k := make([]uint64, m.MaxEntries()) v := make([]uint64, m.MaxEntries()) b.ReportAllocs() for i := 0; i < b.N; i++ { var next uint32 _, err := m.BatchLookup(nil, &next, k, v, nil) if err != nil && !errors.Is(err, ErrKeyNotExist) { b.Fatal(err) } } }) b.Run("BatchLookupAndDelete", func(b *testing.B) { k := make([]uint64, m.MaxEntries()) v := make([]uint64, m.MaxEntries()) b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() var next uint32 _, err := m.BatchLookupAndDelete(nil, &next, k, v, nil) if err != nil && !errors.Is(err, ErrKeyNotExist) { b.Fatal(err) } } }) b.Run("BatchDelete", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() if _, err := m.BatchDelete(keys, nil); err != nil { b.Fatal(err) } } }) } // Per CPU maps store a distinct value for each CPU. They are useful // to collect metrics. func ExampleMap_perCPU() { arr, err := NewMap(&MapSpec{ Type: PerCPUArray, KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { panic(err) } defer arr.Close() first := []uint32{4, 5} if err := arr.Put(uint32(0), first); err != nil { panic(err) } second := []uint32{2, 8} if err := arr.Put(uint32(1), second); err != nil { panic(err) } var values []uint32 if err := arr.Lookup(uint32(0), &values); err != nil { panic(err) } fmt.Println("First two values:", values[:2]) var ( key uint32 entries = arr.Iterate() ) for entries.Next(&key, &values) { // NB: sum can overflow, real code should check for this var sum uint32 for _, n := range values { sum += n } fmt.Printf("Sum of %d: %d\n", key, sum) } if err := entries.Err(); err != nil { panic(err) } } // It is possible to use unsafe.Pointer to avoid marshalling // and copy overhead. It is the resposibility of the caller to ensure // the correct size of unsafe.Pointers. // // Note that using unsafe.Pointer is only marginally faster than // implementing Marshaler on the type. func ExampleMap_zeroCopy() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() key := [5]byte{'h', 'e', 'l', 'l', 'o'} value := uint32(23) if err := hash.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { panic(err) } value = 0 if err := hash.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { panic("can't get value:" + err.Error()) } fmt.Printf("The value is: %d\n", value) // Output: The value is: 23 } func ExampleMap_NextKey() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() if err := hash.Put("hello", uint32(21)); err != nil { panic(err) } if err := hash.Put("world", uint32(42)); err != nil { panic(err) } var firstKey string if err := hash.NextKey(nil, &firstKey); err != nil { panic(err) } var nextKey string if err := hash.NextKey(firstKey, &nextKey); err != nil { panic(err) } // Order of keys is non-deterministic due to randomized map seed } // ExampleMap_Iterate demonstrates how to iterate over all entries // in a map. func ExampleMap_Iterate() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() if err := hash.Put("hello", uint32(21)); err != nil { panic(err) } if err := hash.Put("world", uint32(42)); err != nil { panic(err) } var ( key string value uint32 entries = hash.Iterate() ) for entries.Next(&key, &value) { // Order of keys is non-deterministic due to randomized map seed fmt.Printf("key: %s, value: %d\n", key, value) } if err := entries.Err(); err != nil { panic(fmt.Sprint("Iterator encountered an error:", err)) } } // It is possible to iterate nested maps and program arrays by // unmarshaling into a *Map or *Program. func ExampleMap_Iterate_nestedMapsAndProgramArrays() { var arrayOfMaps *Map // Set this up somehow var ( key uint32 m *Map entries = arrayOfMaps.Iterate() ) // Make sure that the iterated map is closed after // we are done. defer m.Close() for entries.Next(&key, &m) { // Order of keys is non-deterministic due to randomized map seed fmt.Printf("key: %v, map: %v\n", key, m) } if err := entries.Err(); err != nil { panic(fmt.Sprint("Iterator encountered an error:", err)) } } golang-github-cilium-ebpf-0.11.0/marshaler_example_test.go000066400000000000000000000021321456504511400235740ustar00rootroot00000000000000package ebpf import ( "encoding" "fmt" "strings" ) // Assert that customEncoding implements the correct interfaces. var ( _ encoding.BinaryMarshaler = (*customEncoding)(nil) _ encoding.BinaryUnmarshaler = (*customEncoding)(nil) ) type customEncoding struct { data string } func (ce *customEncoding) MarshalBinary() ([]byte, error) { return []byte(strings.ToUpper(ce.data)), nil } func (ce *customEncoding) UnmarshalBinary(buf []byte) error { ce.data = string(buf) return nil } // ExampleMarshaler shows how to use custom encoding with map methods. func Example_customMarshaler() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() if err := hash.Put(&customEncoding{"hello"}, uint32(111)); err != nil { panic(err) } var ( key customEncoding value uint32 entries = hash.Iterate() ) for entries.Next(&key, &value) { fmt.Printf("key: %s, value: %d\n", key.data, value) } if err := entries.Err(); err != nil { panic(err) } // Output: key: HELLO, value: 111 } golang-github-cilium-ebpf-0.11.0/marshalers.go000066400000000000000000000146271456504511400212210ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding" "encoding/binary" "errors" "fmt" "reflect" "runtime" "sync" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) // marshalPtr converts an arbitrary value into a pointer suitable // to be passed to the kernel. // // As an optimization, it returns the original value if it is an // unsafe.Pointer. func marshalPtr(data interface{}, length int) (sys.Pointer, error) { if ptr, ok := data.(unsafe.Pointer); ok { return sys.NewPointer(ptr), nil } buf, err := marshalBytes(data, length) if err != nil { return sys.Pointer{}, err } return sys.NewSlicePointer(buf), nil } // marshalBytes converts an arbitrary value into a byte buffer. // // Prefer using Map.marshalKey and Map.marshalValue if possible, since // those have special cases that allow more types to be encoded. // // Returns an error if the given value isn't representable in exactly // length bytes. func marshalBytes(data interface{}, length int) (buf []byte, err error) { if data == nil { return nil, errors.New("can't marshal a nil value") } switch value := data.(type) { case encoding.BinaryMarshaler: buf, err = value.MarshalBinary() case string: buf = []byte(value) case []byte: buf = value case unsafe.Pointer: err = errors.New("can't marshal from unsafe.Pointer") case Map, *Map, Program, *Program: err = fmt.Errorf("can't marshal %T", value) default: wr := internal.NewBuffer(make([]byte, 0, length)) defer internal.PutBuffer(wr) err = binary.Write(wr, internal.NativeEndian, value) if err != nil { err = fmt.Errorf("encoding %T: %v", value, err) } buf = wr.Bytes() } if err != nil { return nil, err } if len(buf) != length { return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length) } return buf, nil } func makeBuffer(dst interface{}, length int) (sys.Pointer, []byte) { if ptr, ok := dst.(unsafe.Pointer); ok { return sys.NewPointer(ptr), nil } buf := make([]byte, length) return sys.NewSlicePointer(buf), buf } var bytesReaderPool = sync.Pool{ New: func() interface{} { return new(bytes.Reader) }, } // unmarshalBytes converts a byte buffer into an arbitrary value. // // Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since // those have special cases that allow more types to be encoded. // // The common int32 and int64 types are directly handled to avoid // unnecessary heap allocations as happening in the default case. func unmarshalBytes(data interface{}, buf []byte) error { switch value := data.(type) { case unsafe.Pointer: dst := unsafe.Slice((*byte)(value), len(buf)) copy(dst, buf) runtime.KeepAlive(value) return nil case Map, *Map, Program, *Program: return fmt.Errorf("can't unmarshal into %T", value) case encoding.BinaryUnmarshaler: return value.UnmarshalBinary(buf) case *string: *value = string(buf) return nil case *[]byte: *value = buf return nil case *int32: if len(buf) < 4 { return errors.New("int32 requires 4 bytes") } *value = int32(internal.NativeEndian.Uint32(buf)) return nil case *uint32: if len(buf) < 4 { return errors.New("uint32 requires 4 bytes") } *value = internal.NativeEndian.Uint32(buf) return nil case *int64: if len(buf) < 8 { return errors.New("int64 requires 8 bytes") } *value = int64(internal.NativeEndian.Uint64(buf)) return nil case *uint64: if len(buf) < 8 { return errors.New("uint64 requires 8 bytes") } *value = internal.NativeEndian.Uint64(buf) return nil case string: return errors.New("require pointer to string") case []byte: return errors.New("require pointer to []byte") default: rd := bytesReaderPool.Get().(*bytes.Reader) rd.Reset(buf) defer bytesReaderPool.Put(rd) if err := binary.Read(rd, internal.NativeEndian, value); err != nil { return fmt.Errorf("decoding %T: %v", value, err) } return nil } } // marshalPerCPUValue encodes a slice containing one value per // possible CPU into a buffer of bytes. // // Values are initialized to zero if the slice has less elements than CPUs. // // slice must have a type like []elementType. func marshalPerCPUValue(slice interface{}, elemLength int) (sys.Pointer, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return sys.Pointer{}, errors.New("per-CPU value requires slice") } possibleCPUs, err := internal.PossibleCPUs() if err != nil { return sys.Pointer{}, err } sliceValue := reflect.ValueOf(slice) sliceLen := sliceValue.Len() if sliceLen > possibleCPUs { return sys.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs") } alignedElemLength := internal.Align(elemLength, 8) buf := make([]byte, alignedElemLength*possibleCPUs) for i := 0; i < sliceLen; i++ { elem := sliceValue.Index(i).Interface() elemBytes, err := marshalBytes(elem, elemLength) if err != nil { return sys.Pointer{}, err } offset := i * alignedElemLength copy(buf[offset:offset+elemLength], elemBytes) } return sys.NewSlicePointer(buf), nil } // unmarshalPerCPUValue decodes a buffer into a slice containing one value per // possible CPU. // // valueOut must have a type like *[]elementType func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error { slicePtrType := reflect.TypeOf(slicePtr) if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { return fmt.Errorf("per-cpu value requires pointer to slice") } possibleCPUs, err := internal.PossibleCPUs() if err != nil { return err } sliceType := slicePtrType.Elem() slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs) sliceElemType := sliceType.Elem() sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr if sliceElemIsPointer { sliceElemType = sliceElemType.Elem() } step := len(buf) / possibleCPUs if step < elemLength { return fmt.Errorf("per-cpu element length is larger than available data") } for i := 0; i < possibleCPUs; i++ { var elem interface{} if sliceElemIsPointer { newElem := reflect.New(sliceElemType) slice.Index(i).Set(newElem) elem = newElem.Interface() } else { elem = slice.Index(i).Addr().Interface() } // Make a copy, since unmarshal can hold on to itemBytes elemBytes := make([]byte, elemLength) copy(elemBytes, buf[:elemLength]) err := unmarshalBytes(elem, elemBytes) if err != nil { return fmt.Errorf("cpu %d: %w", i, err) } buf = buf[step:] } reflect.ValueOf(slicePtr).Elem().Set(slice) return nil } golang-github-cilium-ebpf-0.11.0/perf/000077500000000000000000000000001456504511400174535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/perf/doc.go000066400000000000000000000004061456504511400205470ustar00rootroot00000000000000// Package perf allows interacting with Linux perf_events. // // BPF allows submitting custom perf_events to a ring-buffer set up // by userspace. This is very useful to push things like packet samples // from BPF to a daemon running in user space. package perf golang-github-cilium-ebpf-0.11.0/perf/reader.go000066400000000000000000000271031456504511400212470ustar00rootroot00000000000000package perf import ( "encoding/binary" "errors" "fmt" "io" "os" "runtime" "sync" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/epoll" "github.com/cilium/ebpf/internal/unix" ) var ( ErrClosed = os.ErrClosed errEOR = errors.New("end of ring") ) var perfEventHeaderSize = binary.Size(perfEventHeader{}) // perfEventHeader must match 'struct perf_event_header` in . type perfEventHeader struct { Type uint32 Misc uint16 Size uint16 } func cpuForEvent(event *unix.EpollEvent) int { return int(event.Pad) } // Record contains either a sample or a counter of the // number of lost samples. type Record struct { // The CPU this record was generated on. CPU int // The data submitted via bpf_perf_event_output. // Due to a kernel bug, this can contain between 0 and 7 bytes of trailing // garbage from the ring depending on the input sample's length. RawSample []byte // The number of samples which could not be output, since // the ring buffer was full. LostSamples uint64 } // Read a record from a reader and tag it as being from the given CPU. // // buf must be at least perfEventHeaderSize bytes long. func readRecord(rd io.Reader, rec *Record, buf []byte, overwritable bool) error { // Assert that the buffer is large enough. buf = buf[:perfEventHeaderSize] _, err := io.ReadFull(rd, buf) if errors.Is(err, io.EOF) { return errEOR } else if err != nil { return fmt.Errorf("read perf event header: %v", err) } header := perfEventHeader{ internal.NativeEndian.Uint32(buf[0:4]), internal.NativeEndian.Uint16(buf[4:6]), internal.NativeEndian.Uint16(buf[6:8]), } switch header.Type { case unix.PERF_RECORD_LOST: rec.RawSample = rec.RawSample[:0] rec.LostSamples, err = readLostRecords(rd) return err case unix.PERF_RECORD_SAMPLE: rec.LostSamples = 0 // We can reuse buf here because perfEventHeaderSize > perfEventSampleSize. rec.RawSample, err = readRawSample(rd, buf, rec.RawSample) return err default: return &unknownEventError{header.Type} } } func readLostRecords(rd io.Reader) (uint64, error) { // lostHeader must match 'struct perf_event_lost in kernel sources. var lostHeader struct { ID uint64 Lost uint64 } err := binary.Read(rd, internal.NativeEndian, &lostHeader) if err != nil { return 0, fmt.Errorf("can't read lost records header: %v", err) } return lostHeader.Lost, nil } var perfEventSampleSize = binary.Size(uint32(0)) // This must match 'struct perf_event_sample in kernel sources. type perfEventSample struct { Size uint32 } func readRawSample(rd io.Reader, buf, sampleBuf []byte) ([]byte, error) { buf = buf[:perfEventSampleSize] if _, err := io.ReadFull(rd, buf); err != nil { return nil, fmt.Errorf("read sample size: %w", err) } sample := perfEventSample{ internal.NativeEndian.Uint32(buf), } var data []byte if size := int(sample.Size); cap(sampleBuf) < size { data = make([]byte, size) } else { data = sampleBuf[:size] } if _, err := io.ReadFull(rd, data); err != nil { return nil, fmt.Errorf("read sample: %w", err) } return data, nil } // Reader allows reading bpf_perf_event_output // from user space. type Reader struct { poller *epoll.Poller deadline time.Time // mu protects read/write access to the Reader structure with the // exception of 'pauseFds', which is protected by 'pauseMu'. // If locking both 'mu' and 'pauseMu', 'mu' must be locked first. mu sync.Mutex // Closing a PERF_EVENT_ARRAY removes all event fds // stored in it, so we keep a reference alive. array *ebpf.Map rings []*perfEventRing epollEvents []unix.EpollEvent epollRings []*perfEventRing eventHeader []byte // pauseFds are a copy of the fds in 'rings', protected by 'pauseMu'. // These allow Pause/Resume to be executed independently of any ongoing // Read calls, which would otherwise need to be interrupted. pauseMu sync.Mutex pauseFds []int paused bool overwritable bool } // ReaderOptions control the behaviour of the user // space reader. type ReaderOptions struct { // The number of written bytes required in any per CPU buffer before // Read will process data. Must be smaller than PerCPUBuffer. // The default is to start processing as soon as data is available. Watermark int // This perf ring buffer is overwritable, once full the oldest event will be // overwritten by newest. Overwritable bool } // NewReader creates a new reader with default options. // // array must be a PerfEventArray. perCPUBuffer gives the size of the // per CPU buffer in bytes. It is rounded up to the nearest multiple // of the current page size. func NewReader(array *ebpf.Map, perCPUBuffer int) (*Reader, error) { return NewReaderWithOptions(array, perCPUBuffer, ReaderOptions{}) } // NewReaderWithOptions creates a new reader with the given options. func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) (pr *Reader, err error) { if perCPUBuffer < 1 { return nil, errors.New("perCPUBuffer must be larger than 0") } var ( fds []int nCPU = int(array.MaxEntries()) rings = make([]*perfEventRing, 0, nCPU) pauseFds = make([]int, 0, nCPU) ) poller, err := epoll.New() if err != nil { return nil, err } defer func() { if err != nil { poller.Close() for _, fd := range fds { unix.Close(fd) } for _, ring := range rings { if ring != nil { ring.Close() } } } }() // bpf_perf_event_output checks which CPU an event is enabled on, // but doesn't allow using a wildcard like -1 to specify "all CPUs". // Hence we have to create a ring for each CPU. for i := 0; i < nCPU; i++ { ring, err := newPerfEventRing(i, perCPUBuffer, opts.Watermark, opts.Overwritable) if errors.Is(err, unix.ENODEV) { // The requested CPU is currently offline, skip it. rings = append(rings, nil) pauseFds = append(pauseFds, -1) continue } if err != nil { return nil, fmt.Errorf("failed to create perf ring for CPU %d: %v", i, err) } rings = append(rings, ring) pauseFds = append(pauseFds, ring.fd) if err := poller.Add(ring.fd, i); err != nil { return nil, err } } array, err = array.Clone() if err != nil { return nil, err } pr = &Reader{ array: array, rings: rings, poller: poller, deadline: time.Time{}, epollEvents: make([]unix.EpollEvent, len(rings)), epollRings: make([]*perfEventRing, 0, len(rings)), eventHeader: make([]byte, perfEventHeaderSize), pauseFds: pauseFds, overwritable: opts.Overwritable, } if err = pr.Resume(); err != nil { return nil, err } runtime.SetFinalizer(pr, (*Reader).Close) return pr, nil } // Close frees resources used by the reader. // // It interrupts calls to Read. // // Calls to perf_event_output from eBPF programs will return // ENOENT after calling this method. func (pr *Reader) Close() error { if err := pr.poller.Close(); err != nil { if errors.Is(err, os.ErrClosed) { return nil } return fmt.Errorf("close poller: %w", err) } // Trying to poll will now fail, so Read() can't block anymore. Acquire the // lock so that we can clean up. pr.mu.Lock() defer pr.mu.Unlock() for _, ring := range pr.rings { if ring != nil { ring.Close() } } pr.rings = nil pr.pauseFds = nil pr.array.Close() return nil } // SetDeadline controls how long Read and ReadInto will block waiting for samples. // // Passing a zero time.Time will remove the deadline. Passing a deadline in the // past will prevent the reader from blocking if there are no records to be read. func (pr *Reader) SetDeadline(t time.Time) { pr.mu.Lock() defer pr.mu.Unlock() pr.deadline = t } // Read the next record from the perf ring buffer. // // The function blocks until there are at least Watermark bytes in one // of the per CPU buffers. Records from buffers below the Watermark // are not returned. // // Records can contain between 0 and 7 bytes of trailing garbage from the ring // depending on the input sample's length. // // Calling Close interrupts the function. // // Returns os.ErrDeadlineExceeded if a deadline was set. func (pr *Reader) Read() (Record, error) { var r Record return r, pr.ReadInto(&r) } var errMustBePaused = fmt.Errorf("perf ringbuffer: must have been paused before reading overwritable buffer") // ReadInto is like Read except that it allows reusing Record and associated buffers. func (pr *Reader) ReadInto(rec *Record) error { pr.mu.Lock() defer pr.mu.Unlock() pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.overwritable && !pr.paused { return errMustBePaused } if pr.rings == nil { return fmt.Errorf("perf ringbuffer: %w", ErrClosed) } for { if len(pr.epollRings) == 0 { // NB: The deferred pauseMu.Unlock will panic if Wait panics, which // might obscure the original panic. pr.pauseMu.Unlock() nEvents, err := pr.poller.Wait(pr.epollEvents, pr.deadline) pr.pauseMu.Lock() if err != nil { return err } // Re-validate pr.paused since we dropped pauseMu. if pr.overwritable && !pr.paused { return errMustBePaused } for _, event := range pr.epollEvents[:nEvents] { ring := pr.rings[cpuForEvent(&event)] pr.epollRings = append(pr.epollRings, ring) // Read the current head pointer now, not every time // we read a record. This prevents a single fast producer // from keeping the reader busy. ring.loadHead() } } // Start at the last available event. The order in which we // process them doesn't matter, and starting at the back allows // resizing epollRings to keep track of processed rings. err := pr.readRecordFromRing(rec, pr.epollRings[len(pr.epollRings)-1]) if err == errEOR { // We've emptied the current ring buffer, process // the next one. pr.epollRings = pr.epollRings[:len(pr.epollRings)-1] continue } return err } } // Pause stops all notifications from this Reader. // // While the Reader is paused, any attempts to write to the event buffer from // BPF programs will return -ENOENT. // // Subsequent calls to Read will block until a call to Resume. func (pr *Reader) Pause() error { pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.pauseFds == nil { return fmt.Errorf("%w", ErrClosed) } for i := range pr.pauseFds { if err := pr.array.Delete(uint32(i)); err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) { return fmt.Errorf("could't delete event fd for CPU %d: %w", i, err) } } pr.paused = true return nil } // Resume allows this perf reader to emit notifications. // // Subsequent calls to Read will block until the next event notification. func (pr *Reader) Resume() error { pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.pauseFds == nil { return fmt.Errorf("%w", ErrClosed) } for i, fd := range pr.pauseFds { if fd == -1 { continue } if err := pr.array.Put(uint32(i), uint32(fd)); err != nil { return fmt.Errorf("couldn't put event fd %d for CPU %d: %w", fd, i, err) } } pr.paused = false return nil } // NB: Has to be preceded by a call to ring.loadHead. func (pr *Reader) readRecordFromRing(rec *Record, ring *perfEventRing) error { defer ring.writeTail() rec.CPU = ring.cpu err := readRecord(ring, rec, pr.eventHeader, pr.overwritable) if pr.overwritable && (errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) { return errEOR } return err } type unknownEventError struct { eventType uint32 } func (uev *unknownEventError) Error() string { return fmt.Sprintf("unknown event type: %d", uev.eventType) } // IsUnknownEvent returns true if the error occurred // because an unknown event was submitted to the perf event ring. func IsUnknownEvent(err error) bool { var uee *unknownEventError return errors.As(err, &uee) } golang-github-cilium-ebpf-0.11.0/perf/reader_test.go000066400000000000000000000367751456504511400223250ustar00rootroot00000000000000package perf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "os" "syscall" "testing" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/fdtrace" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) var ( readTimeout = 250 * time.Millisecond ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestPerfReader(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() outputSamples(t, events, 5) checkRecord(t, rd) rd.SetDeadline(time.Now().Add(4 * time.Millisecond)) _, err = rd.Read() qt.Assert(t, errors.Is(err, os.ErrDeadlineExceeded), qt.IsTrue, qt.Commentf("expected os.ErrDeadlineExceeded")) } func TestReaderSetDeadline(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() rd.SetDeadline(time.Now().Add(-time.Second)) if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from first Read, got:", err) } if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from second Read, got:", err) } } func outputSamples(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) { prog := outputSamplesProg(tb, events, sampleSizes...) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(tb, err) if err != nil { tb.Fatal(err) } if errno := syscall.Errno(-int32(ret)); errno != 0 { tb.Fatal("Expected 0 as return value, got", errno) } } // outputSamplesProg creates a program which submits a series of samples to a PerfEventArray. // // The format of each sample is: // // index: 0 1 2 3 ... size - 1 // content: size id 0xff 0xff ... 0xff [padding] // // padding is an implementation detail of the perf buffer and 1-7 bytes long. The // contents are undefined. func outputSamplesProg(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) *ebpf.Program { tb.Helper() // Requires at least 4.9 (0515e5999a46 "bpf: introduce BPF_PROG_TYPE_PERF_EVENT program type") testutils.SkipOnOldKernel(tb, "4.9", "perf events support") const bpfFCurrentCPU = 0xffffffff var maxSampleSize byte for _, sampleSize := range sampleSizes { if sampleSize < 2 { tb.Fatalf("Sample size %d is too small to contain size and counter", sampleSize) } if sampleSize > maxSampleSize { maxSampleSize = sampleSize } } // Fill a buffer on the stack, and stash context somewhere insns := asm.Instructions{ asm.LoadImm(asm.R0, ^int64(0), asm.DWord), asm.Mov.Reg(asm.R9, asm.R1), } bufDwords := int(maxSampleSize/8) + 1 for i := 0; i < bufDwords; i++ { insns = append(insns, asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord), ) } for i, sampleSize := range sampleSizes { insns = append(insns, // Restore stashed context. asm.Mov.Reg(asm.R1, asm.R9), // map asm.LoadMapPtr(asm.R2, events.FD()), // flags asm.LoadImm(asm.R3, bpfFCurrentCPU, asm.DWord), // buffer asm.Mov.Reg(asm.R4, asm.RFP), asm.Add.Imm(asm.R4, int32(bufDwords*-8)), // buffer[0] = size asm.StoreImm(asm.R4, 0, int64(sampleSize), asm.Byte), // buffer[1] = i asm.StoreImm(asm.R4, 1, int64(i&math.MaxUint8), asm.Byte), // size asm.Mov.Imm(asm.R5, int32(sampleSize)), asm.FnPerfEventOutput.Call(), ) } insns = append(insns, asm.Return()) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ License: "GPL", Type: ebpf.XDP, Instructions: insns, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return prog } func checkRecord(tb testing.TB, rd *Reader) (id int) { tb.Helper() rec, err := rd.Read() qt.Assert(tb, err, qt.IsNil) qt.Assert(tb, rec.CPU >= 0, qt.IsTrue, qt.Commentf("Record has invalid CPU number")) size := int(rec.RawSample[0]) qt.Assert(tb, len(rec.RawSample) >= size, qt.IsTrue, qt.Commentf("RawSample is at least size bytes")) for i, v := range rec.RawSample[2:size] { qt.Assert(tb, v, qt.Equals, byte(0xff), qt.Commentf("filler at position %d should match", i+2)) } // padding is ignored since it's value is undefined. return int(rec.RawSample[1]) } func TestPerfReaderLostSample(t *testing.T) { // To generate a lost sample perf record: // // 1. Fill the perf ring buffer almost completely, with the output_large program. // The buffer is sized in number of pages, which are architecture dependant. // // 2. Write an extra event that doesn't fit in the space remaining. // // 3. Write a smaller event that does fit, with output_single program. // Lost sample records are generated opportunistically, when the kernel // is writing an event and realizes that there were events lost previously. // // The event size is hardcoded in the test BPF programs, there's no way // to parametrize it without rebuilding the programs. // // The event size needs to be selected so that, for any page size, there are at least // 48 bytes left in the perf ring page after filling it with a whole number of events: // // - PERF_RECORD_LOST: 8 (perf_event_header) + 16 (PERF_RECORD_LOST) // // - output_single: 8 (perf_event_header) + 4 (size) + 5 (payload) + 7 (padding to 64bits) // // By selecting an event size of the form 2^n + 2^(n+1), for any page size 2^(n+m), m >= 0, // the number of bytes left, x, after filling a page with a whole number of events is: // // 2^(n+m) 2^n * 2^m // x = 2^n * frac(---------------) <=> x = 2^n * frac(---------------) // 2^n + 2^(n+1) 2^n + 2^n * 2 // // 2^n * 2^m // <=> x = 2^n * frac(---------------) // 2^n * (1 + 2) // // 2^m // <=> x = 2^n * frac(-----) // 3 // // 1 2 // <=> x = 2^n * - or x = 2^n * - // 3 3 // // Selecting n = 6, we have: // // x = 64 or x = 128, no matter the page size 2^(6+m) // // event size = 2^6 + 2^7 = 192 // // Accounting for perf headers, output_large uses a 180 byte payload: // // 8 (perf_event_header) + 4 (size) + 180 (payload) const ( eventSize = 192 ) var ( pageSize = os.Getpagesize() maxEvents = (pageSize / eventSize) ) if remainder := pageSize % eventSize; remainder != 64 && remainder != 128 { // Page size isn't 2^(6+m), m >= 0 t.Fatal("unsupported page size:", pageSize) } var sampleSizes []byte // Fill the ring with the maximum number of output_large events that will fit, // and generate a lost event by writing an additional event. for i := 0; i < maxEvents+1; i++ { sampleSizes = append(sampleSizes, 180) } // Generate a small event to trigger the lost record sampleSizes = append(sampleSizes, 5) events := perfEventArray(t) rd, err := NewReader(events, pageSize) if err != nil { t.Fatal(err) } defer rd.Close() outputSamples(t, events, sampleSizes...) for range sampleSizes { record, err := rd.Read() if err != nil { t.Fatal(err) } if record.RawSample == nil && record.LostSamples != 1 { t.Fatal("Expected a record with LostSamples 1, got", record.LostSamples) } } } func TestPerfReaderOverwritable(t *testing.T) { // Smallest buffer size. pageSize := os.Getpagesize() const sampleSize = math.MaxUint8 // Account for perf header (8) and size (4), align to 8 bytes as perf does. realSampleSize := internal.Align(sampleSize+8+4, 8) maxEvents := pageSize / realSampleSize var sampleSizes []byte for i := 0; i < maxEvents; i++ { sampleSizes = append(sampleSizes, sampleSize) } // Append an extra sample that will overwrite the first sample. sampleSizes = append(sampleSizes, sampleSize) events := perfEventArray(t) rd, err := NewReaderWithOptions(events, pageSize, ReaderOptions{Overwritable: true}) if err != nil { t.Fatal(err) } defer rd.Close() _, err = rd.Read() qt.Assert(t, err, qt.ErrorIs, errMustBePaused) outputSamples(t, events, sampleSizes...) qt.Assert(t, rd.Pause(), qt.IsNil) rd.SetDeadline(time.Now()) nextID := maxEvents for i := 0; i < maxEvents; i++ { id := checkRecord(t, rd) qt.Assert(t, id, qt.Equals, nextID) nextID-- } } func TestPerfReaderOverwritableEmpty(t *testing.T) { events := perfEventArray(t) rd, err := NewReaderWithOptions(events, os.Getpagesize(), ReaderOptions{Overwritable: true}) if err != nil { t.Fatal(err) } defer rd.Close() err = rd.Pause() if err != nil { t.Fatal(err) } rd.SetDeadline(time.Now().Add(4 * time.Millisecond)) _, err = rd.Read() qt.Assert(t, errors.Is(err, os.ErrDeadlineExceeded), qt.IsTrue, qt.Commentf("expected os.ErrDeadlineExceeded")) err = rd.Resume() if err != nil { t.Fatal(err) } } func TestPerfReaderClose(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() errs := make(chan error, 1) waiting := make(chan struct{}) go func() { close(waiting) _, err := rd.Read() errs <- err }() <-waiting // Close should interrupt Read if err := rd.Close(); err != nil { t.Fatal(err) } select { case <-errs: case <-time.After(time.Second): t.Fatal("Close doesn't interrupt Read") } // And we should be able to call it multiple times if err := rd.Close(); err != nil { t.Fatal(err) } if _, err := rd.Read(); err == nil { t.Fatal("Read on a closed PerfReader doesn't return an error") } } func TestCreatePerfEvent(t *testing.T) { fd, err := createPerfEvent(0, 1, false) if err != nil { t.Fatal("Can't create perf event:", err) } unix.Close(fd) } func TestReadRecord(t *testing.T) { var buf bytes.Buffer err := binary.Write(&buf, internal.NativeEndian, &perfEventHeader{}) if err != nil { t.Fatal(err) } var rec Record err = readRecord(&buf, &rec, make([]byte, perfEventHeaderSize), false) if !IsUnknownEvent(err) { t.Error("readRecord should return unknown event error, got", err) } } func TestPause(t *testing.T) { t.Parallel() events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() // Reader is already unpaused by default. It should be idempotent. if err = rd.Resume(); err != nil { t.Fatal(err) } // Write a sample. The reader should read it. prog := outputSamplesProg(t, events, 5) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil || ret != 0 { t.Fatal("Can't write sample") } if _, err := rd.Read(); err != nil { t.Fatal(err) } // Pause. No notification should trigger. if err = rd.Pause(); err != nil { t.Fatal(err) } errChan := make(chan error, 1) go func() { // Read one notification then send any errors and exit. _, err := rd.Read() errChan <- err }() ret, _, err = prog.Test(internal.EmptyBPFContext) if err == nil && ret == 0 { t.Fatal("Unexpectedly wrote sample while paused") } // else Success select { case err := <-errChan: // Failure: Pause was unsuccessful. t.Fatalf("received notification on paused reader: %s", err) case <-time.After(readTimeout): // Success } // Pause should be idempotent. if err = rd.Pause(); err != nil { t.Fatal(err) } // Resume. Now notifications should continue. if err = rd.Resume(); err != nil { t.Fatal(err) } ret, _, err = prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { t.Fatal("Can't write sample") } select { case err := <-errChan: if err != nil { t.Fatal(err) } // else Success case <-time.After(readTimeout): t.Fatal("timed out waiting for notification after resume") } if err = rd.Close(); err != nil { t.Fatal(err) } // Pause/Resume after close should be no-op. err = rd.Pause() qt.Assert(t, err, qt.Not(qt.Equals), ErrClosed, qt.Commentf("returns unwrapped ErrClosed")) qt.Assert(t, errors.Is(err, ErrClosed), qt.IsTrue, qt.Commentf("doesn't wrap ErrClosed")) err = rd.Resume() qt.Assert(t, err, qt.Not(qt.Equals), ErrClosed, qt.Commentf("returns unwrapped ErrClosed")) qt.Assert(t, errors.Is(err, ErrClosed), qt.IsTrue, qt.Commentf("doesn't wrap ErrClosed")) } func BenchmarkReader(b *testing.B) { events := perfEventArray(b) prog := outputSamplesProg(b, events, 80) rd, err := NewReader(events, 4096) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } if _, err = rd.Read(); err != nil { b.Fatal(err) } } } func BenchmarkReadInto(b *testing.B) { events := perfEventArray(b) prog := outputSamplesProg(b, events, 80) rd, err := NewReader(events, 4096) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ResetTimer() b.ReportAllocs() var rec Record for i := 0; i < b.N; i++ { // NB: Submitting samples into the perf event ring dominates // the benchmark time unfortunately. ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } if err := rd.ReadInto(&rec); err != nil { b.Fatal(err) } } } // This exists just to make the example below nicer. func bpfPerfEventOutputProgram() (*ebpf.Program, *ebpf.Map) { return nil, nil } // ExamplePerfReader submits a perf event using BPF, // and then reads it in user space. // // The BPF will look something like this: // // struct map events __section("maps") = { // .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, // }; // // __section("xdp") int output_single(void *ctx) { // unsigned char buf[] = { // 1, 2, 3, 4, 5 // }; // // return perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &buf[0], 5); // } // // Also see BPF_F_CTXLEN_MASK if you want to sample packet data // from SKB or XDP programs. func ExampleReader() { prog, events := bpfPerfEventOutputProgram() defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { panic(err) } defer rd.Close() // Writes out a sample with content 1,2,3,4,4 ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { panic("Can't write sample") } record, err := rd.Read() if err != nil { panic(err) } // Data is padded with 0 for alignment fmt.Println("Sample:", record.RawSample) } // ReadRecord allows reducing memory allocations. func ExampleReader_ReadInto() { prog, events := bpfPerfEventOutputProgram() defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { panic(err) } defer rd.Close() for i := 0; i < 2; i++ { // Write out two samples ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { panic("Can't write sample") } } var rec Record for i := 0; i < 2; i++ { if err := rd.ReadInto(&rec); err != nil { panic(err) } fmt.Println("Sample:", rec.RawSample[:5]) } } func perfEventArray(tb testing.TB) *ebpf.Map { events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.PerfEventArray, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { events.Close() }) return events } golang-github-cilium-ebpf-0.11.0/perf/ring.go000066400000000000000000000147201456504511400207450ustar00rootroot00000000000000package perf import ( "errors" "fmt" "io" "math" "os" "runtime" "sync/atomic" "unsafe" "github.com/cilium/ebpf/internal/unix" ) // perfEventRing is a page of metadata followed by // a variable number of pages which form a ring buffer. type perfEventRing struct { fd int cpu int mmap []byte ringReader } func newPerfEventRing(cpu, perCPUBuffer, watermark int, overwritable bool) (*perfEventRing, error) { if watermark >= perCPUBuffer { return nil, errors.New("watermark must be smaller than perCPUBuffer") } fd, err := createPerfEvent(cpu, watermark, overwritable) if err != nil { return nil, err } if err := unix.SetNonblock(fd, true); err != nil { unix.Close(fd) return nil, err } protections := unix.PROT_READ if !overwritable { protections |= unix.PROT_WRITE } mmap, err := unix.Mmap(fd, 0, perfBufferSize(perCPUBuffer), protections, unix.MAP_SHARED) if err != nil { unix.Close(fd) return nil, fmt.Errorf("can't mmap: %v", err) } // This relies on the fact that we allocate an extra metadata page, // and that the struct is smaller than an OS page. // This use of unsafe.Pointer isn't explicitly sanctioned by the // documentation, since a byte is smaller than sampledPerfEvent. meta := (*unix.PerfEventMmapPage)(unsafe.Pointer(&mmap[0])) var reader ringReader if overwritable { reader = newReverseReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) } else { reader = newForwardReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) } ring := &perfEventRing{ fd: fd, cpu: cpu, mmap: mmap, ringReader: reader, } runtime.SetFinalizer(ring, (*perfEventRing).Close) return ring, nil } // perfBufferSize returns a valid mmap buffer size for use with perf_event_open (1+2^n pages) func perfBufferSize(perCPUBuffer int) int { pageSize := os.Getpagesize() // Smallest whole number of pages nPages := (perCPUBuffer + pageSize - 1) / pageSize // Round up to nearest power of two number of pages nPages = int(math.Pow(2, math.Ceil(math.Log2(float64(nPages))))) // Add one for metadata nPages += 1 return nPages * pageSize } func (ring *perfEventRing) Close() { runtime.SetFinalizer(ring, nil) _ = unix.Close(ring.fd) _ = unix.Munmap(ring.mmap) ring.fd = -1 ring.mmap = nil } func createPerfEvent(cpu, watermark int, overwritable bool) (int, error) { if watermark == 0 { watermark = 1 } bits := unix.PerfBitWatermark if overwritable { bits |= unix.PerfBitWriteBackward } attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_SOFTWARE, Config: unix.PERF_COUNT_SW_BPF_OUTPUT, Bits: uint64(bits), Sample_type: unix.PERF_SAMPLE_RAW, Wakeup: uint32(watermark), } attr.Size = uint32(unsafe.Sizeof(attr)) fd, err := unix.PerfEventOpen(&attr, -1, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { return -1, fmt.Errorf("can't create perf event: %w", err) } return fd, nil } type ringReader interface { loadHead() size() int writeTail() Read(p []byte) (int, error) } type forwardReader struct { meta *unix.PerfEventMmapPage head, tail uint64 mask uint64 ring []byte } func newForwardReader(meta *unix.PerfEventMmapPage, ring []byte) *forwardReader { return &forwardReader{ meta: meta, head: atomic.LoadUint64(&meta.Data_head), tail: atomic.LoadUint64(&meta.Data_tail), // cap is always a power of two mask: uint64(cap(ring) - 1), ring: ring, } } func (rr *forwardReader) loadHead() { rr.head = atomic.LoadUint64(&rr.meta.Data_head) } func (rr *forwardReader) size() int { return len(rr.ring) } func (rr *forwardReader) writeTail() { // Commit the new tail. This lets the kernel know that // the ring buffer has been consumed. atomic.StoreUint64(&rr.meta.Data_tail, rr.tail) } func (rr *forwardReader) Read(p []byte) (int, error) { start := int(rr.tail & rr.mask) n := len(p) // Truncate if the read wraps in the ring buffer if remainder := cap(rr.ring) - start; n > remainder { n = remainder } // Truncate if there isn't enough data if remainder := int(rr.head - rr.tail); n > remainder { n = remainder } copy(p, rr.ring[start:start+n]) rr.tail += uint64(n) if rr.tail == rr.head { return n, io.EOF } return n, nil } type reverseReader struct { meta *unix.PerfEventMmapPage // head is the position where the kernel last wrote data. head uint64 // read is the position we read the next data from. Updated as reads are made. read uint64 // tail is the end of the ring buffer. No reads must be made past it. tail uint64 mask uint64 ring []byte } func newReverseReader(meta *unix.PerfEventMmapPage, ring []byte) *reverseReader { rr := &reverseReader{ meta: meta, mask: uint64(cap(ring) - 1), ring: ring, } rr.loadHead() return rr } func (rr *reverseReader) loadHead() { // The diagram below represents an overwritable perf ring buffer: // // head read tail // | | | // V V V // +---+--------+------------+---------+--------+ // | |H-D....D|H-C........C|H-B.....B|H-A....A| // +---+--------+------------+---------+--------+ // <--Write from right to left // Read from left to right--> // (H means header) // // The buffer is read left to right beginning from head to tail. // [head, read) is the read portion of the buffer, [read, tail) the unread one. // read is adjusted as we progress through the buffer. // Avoid reading sample D multiple times by discarding unread samples C, B, A. rr.tail = rr.head // Get the new head and starting reading from it. rr.head = atomic.LoadUint64(&rr.meta.Data_head) rr.read = rr.head if rr.tail-rr.head > uint64(cap(rr.ring)) { // ring has been fully written, only permit at most cap(rr.ring) // bytes to be read. rr.tail = rr.head + uint64(cap(rr.ring)) } } func (rr *reverseReader) size() int { return len(rr.ring) } func (rr *reverseReader) writeTail() { // We do not care about tail for over writable perf buffer. // So, this function is noop. } func (rr *reverseReader) Read(p []byte) (int, error) { start := int(rr.read & rr.mask) n := len(p) // Truncate if the read wraps in the ring buffer if remainder := cap(rr.ring) - start; n > remainder { n = remainder } // Truncate if there isn't enough data if remainder := int(rr.tail - rr.read); n > remainder { n = remainder } copy(p, rr.ring[start:start+n]) rr.read += uint64(n) if rr.read == rr.tail { return n, io.EOF } return n, nil } golang-github-cilium-ebpf-0.11.0/perf/ring_test.go000066400000000000000000000110351456504511400220000ustar00rootroot00000000000000package perf import ( "io" "os" "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestRingBufferReader(t *testing.T) { ring := makeForwardRing(2, 0) checkRead(t, ring, []byte{0, 1}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) // Wrapping read ring = makeForwardRing(2, 1) checkRead(t, ring, []byte{1}, nil) checkRead(t, ring, []byte{0}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) } func TestRingBufferReverseReader(t *testing.T) { // First case: read 4, starting from offset 2. // The buffer should contain the following: // // [0 1 2 3] // ^ // | // head // // As we read from position 2, we should get [2, 3]. // Then, when we read it for the second time, we should get [0, 1] as we would // have looped around the buffer. ring := makeReverseRing(4, 2) checkRead(t, ring, []byte{2, 3}, nil) checkRead(t, ring, []byte{0, 1}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) // Complicated case: read bytes until previous_head. // // [0 1 2 3] // ^ ^ // | | // | +---previous_head // head ring = makeReverseRing(4, 2) checkReadBuffer(t, ring, []byte{2}, nil, make([]byte, 1)) // Next read would be {3}, but we don't consume it. // Pretend the kernel wrote another 2 bytes. ring.meta.Data_head -= 2 ring.loadHead() // {3} is discarded. checkRead(t, ring, []byte{0, 1}, io.EOF) // Complicated case: read the whole buffer because it was "overwritten". // // [0 1 2 3] // ^ // | // +---previous_head // | // head // // So, we should first read [2, 3] then [0, 1]. ring = makeReverseRing(4, 2) ring.meta.Data_head -= ring.meta.Data_size ring.loadHead() checkRead(t, ring, []byte{2, 3}, nil) checkRead(t, ring, []byte{0, 1}, io.EOF) } // ensure that the next call to Read() yields the correct result. // // Read is called with a buffer that is larger than want so // that corner cases around wrapping can be checked. Use // checkReadBuffer if that is not desired. func checkRead(t *testing.T, r io.Reader, want []byte, wantErr error) { checkReadBuffer(t, r, want, wantErr, make([]byte, len(want)+1)) } func checkReadBuffer(t *testing.T, r io.Reader, want []byte, wantErr error, buf []byte) { t.Helper() n, err := r.Read(buf) buf = buf[:n] qt.Assert(t, err, qt.Equals, wantErr) qt.Assert(t, buf, qt.DeepEquals, want) } func makeBuffer(size int) []byte { buf := make([]byte, size) for i := range buf { buf[i] = byte(i) } return buf } func makeReverseRing(size, offset int) *reverseReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } meta := unix.PerfEventMmapPage{ Data_head: 0 - uint64(size) - uint64(offset), Data_tail: 0, // never written by the kernel Data_size: uint64(size), } return newReverseReader(&meta, makeBuffer(size)) } func makeForwardRing(size, offset int) *forwardReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } meta := unix.PerfEventMmapPage{ Data_head: uint64(size + offset), Data_tail: uint64(offset), Data_size: uint64(size), } return newForwardReader(&meta, makeBuffer(size)) } func TestPerfEventRing(t *testing.T) { check := func(buffer, watermark int, overwritable bool) { ring, err := newPerfEventRing(0, buffer, watermark, overwritable) if err != nil { t.Fatal(err) } size := ring.size() // Ring size should be at least as big as buffer if size < buffer { t.Fatalf("ring size %d smaller than buffer %d", size, buffer) } // Ring size should be of the form 2^n pages (meta page has already been removed) if size%os.Getpagesize() != 0 { t.Fatalf("ring size %d not whole number of pages (pageSize %d)", size, os.Getpagesize()) } nPages := size / os.Getpagesize() if nPages&(nPages-1) != 0 { t.Fatalf("ring size %d (%d pages) not a power of two pages (pageSize %d)", size, nPages, os.Getpagesize()) } } // watermark > buffer _, err := newPerfEventRing(0, 8192, 8193, false) if err == nil { t.Fatal("watermark > buffer allowed") } _, err = newPerfEventRing(0, 8192, 8193, true) if err == nil { t.Fatal("watermark > buffer allowed") } // watermark == buffer _, err = newPerfEventRing(0, 8192, 8192, false) if err == nil { t.Fatal("watermark == buffer allowed") } _, err = newPerfEventRing(0, 8192, 8192, true) if err == nil { t.Fatal("watermark == buffer allowed") } // buffer not a power of two, watermark < buffer check(8193, 8192, false) check(8193, 8192, true) // large buffer not a multiple of page size at all (prime) check(65537, 8192, false) check(65537, 8192, true) } golang-github-cilium-ebpf-0.11.0/prog.go000066400000000000000000000733601456504511400200260ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "path/filepath" "runtime" "strings" "time" "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // ErrNotSupported is returned whenever the kernel doesn't support a feature. var ErrNotSupported = internal.ErrNotSupported // ProgramID represents the unique ID of an eBPF program. type ProgramID uint32 const ( // Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN. // This is currently the maximum of spare space allocated for SKB // and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN. outputPad = 256 + 2 ) // DefaultVerifierLogSize is the default number of bytes allocated for the // verifier log. const DefaultVerifierLogSize = 64 * 1024 // maxVerifierLogSize is the maximum size of verifier log buffer the kernel // will accept before returning EINVAL. const maxVerifierLogSize = math.MaxUint32 >> 2 // ProgramOptions control loading a program into the kernel. type ProgramOptions struct { // Bitmap controlling the detail emitted by the kernel's eBPF verifier log. // LogLevel-type values can be ORed together to request specific kinds of // verifier output. See the documentation on [ebpf.LogLevel] for details. // // opts.LogLevel = (ebpf.LogLevelBranch | ebpf.LogLevelStats) // // If left to its default value, the program will first be loaded without // verifier output enabled. Upon error, the program load will be repeated // with LogLevelBranch and the given (or default) LogSize value. // // Setting this to a non-zero value will unconditionally enable the verifier // log, populating the [ebpf.Program.VerifierLog] field on successful loads // and including detailed verifier errors if the program is rejected. This // will always allocate an output buffer, but will result in only a single // attempt at loading the program. LogLevel LogLevel // Controls the output buffer size for the verifier log, in bytes. See the // documentation on ProgramOptions.LogLevel for details about how this value // is used. // // If this value is set too low to fit the verifier log, the resulting // [ebpf.VerifierError]'s Truncated flag will be true, and the error string // will also contain a hint to that effect. // // Defaults to DefaultVerifierLogSize. LogSize int // Disables the verifier log completely, regardless of other options. LogDisabled bool // Type information used for CO-RE relocations. // // This is useful in environments where the kernel BTF is not available // (containers) or where it is in a non-standard location. Defaults to // use the kernel BTF from a well-known location if nil. KernelTypes *btf.Spec } // ProgramSpec defines a Program. type ProgramSpec struct { // Name is passed to the kernel as a debug aid. Must only contain // alpha numeric and '_' characters. Name string // Type determines at which hook in the kernel a program will run. Type ProgramType // AttachType of the program, needed to differentiate allowed context // accesses in some newer program types like CGroupSockAddr. // // Available on kernels 4.17 and later. AttachType AttachType // Name of a kernel data structure or function to attach to. Its // interpretation depends on Type and AttachType. AttachTo string // The program to attach to. Must be provided manually. AttachTarget *Program // The name of the ELF section this program originated from. SectionName string Instructions asm.Instructions // Flags is passed to the kernel and specifies additional program // load attributes. Flags uint32 // License of the program. Some helpers are only available if // the license is deemed compatible with the GPL. // // See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1 License string // Version used by Kprobe programs. // // Deprecated on kernels 5.0 and later. Leave empty to let the library // detect this value automatically. KernelVersion uint32 // The byte order this program was compiled for, may be nil. ByteOrder binary.ByteOrder } // Copy returns a copy of the spec. func (ps *ProgramSpec) Copy() *ProgramSpec { if ps == nil { return nil } cpy := *ps cpy.Instructions = make(asm.Instructions, len(ps.Instructions)) copy(cpy.Instructions, ps.Instructions) return &cpy } // Tag calculates the kernel tag for a series of instructions. // // Use asm.Instructions.Tag if you need to calculate for non-native endianness. func (ps *ProgramSpec) Tag() (string, error) { return ps.Instructions.Tag(internal.NativeEndian) } // VerifierError is returned by [NewProgram] and [NewProgramWithOptions] if a // program is rejected by the verifier. // // Use [errors.As] to access the error. type VerifierError = internal.VerifierError // Program represents BPF program loaded into the kernel. // // It is not safe to close a Program which is used by other goroutines. type Program struct { // Contains the output of the kernel verifier if enabled, // otherwise it is empty. VerifierLog string fd *sys.FD name string pinnedPath string typ ProgramType } // NewProgram creates a new Program. // // See [NewProgramWithOptions] for details. // // Returns a [VerifierError] containing the full verifier log if the program is // rejected by the kernel. func NewProgram(spec *ProgramSpec) (*Program, error) { return NewProgramWithOptions(spec, ProgramOptions{}) } // NewProgramWithOptions creates a new Program. // // Loading a program for the first time will perform // feature detection by loading small, temporary programs. // // Returns a [VerifierError] containing the full verifier log if the program is // rejected by the kernel. func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { if spec == nil { return nil, errors.New("can't load a program from a nil spec") } prog, err := newProgramWithOptions(spec, opts) if errors.Is(err, asm.ErrUnsatisfiedMapReference) { return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err) } return prog, err } func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { if len(spec.Instructions) == 0 { return nil, errors.New("instructions cannot be empty") } if spec.Type == UnspecifiedProgram { return nil, errors.New("can't load program of unspecified type") } if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian) } if opts.LogSize < 0 { return nil, errors.New("ProgramOptions.LogSize must be a positive value; disable verifier logs using ProgramOptions.LogDisabled") } // Kernels before 5.0 (6c4fc209fcf9 "bpf: remove useless version check for prog load") // require the version field to be set to the value of the KERNEL_VERSION // macro for kprobe-type programs. // Overwrite Kprobe program version if set to zero or the magic version constant. kv := spec.KernelVersion if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) { v, err := internal.KernelVersion() if err != nil { return nil, fmt.Errorf("detecting kernel version: %w", err) } kv = v.Kernel() } attr := &sys.ProgLoadAttr{ ProgType: sys.ProgType(spec.Type), ProgFlags: spec.Flags, ExpectedAttachType: sys.AttachType(spec.AttachType), License: sys.NewStringPointer(spec.License), KernVersion: kv, } if haveObjName() == nil { attr.ProgName = sys.NewObjName(spec.Name) } insns := make(asm.Instructions, len(spec.Instructions)) copy(insns, spec.Instructions) handle, fib, lib, err := btf.MarshalExtInfos(insns) if err != nil && !errors.Is(err, btf.ErrNotSupported) { return nil, fmt.Errorf("load ext_infos: %w", err) } if handle != nil { defer handle.Close() attr.ProgBtfFd = uint32(handle.FD()) attr.FuncInfoRecSize = btf.FuncInfoSize attr.FuncInfoCnt = uint32(len(fib)) / btf.FuncInfoSize attr.FuncInfo = sys.NewSlicePointer(fib) attr.LineInfoRecSize = btf.LineInfoSize attr.LineInfoCnt = uint32(len(lib)) / btf.LineInfoSize attr.LineInfo = sys.NewSlicePointer(lib) } if err := applyRelocations(insns, opts.KernelTypes, spec.ByteOrder); err != nil { return nil, fmt.Errorf("apply CO-RE relocations: %w", err) } kconfig, err := resolveKconfigReferences(insns) if err != nil { return nil, fmt.Errorf("resolve .kconfig: %w", err) } defer kconfig.Close() if err := fixupAndValidate(insns); err != nil { return nil, err } handles, err := fixupKfuncs(insns) if err != nil { return nil, fmt.Errorf("fixing up kfuncs: %w", err) } defer handles.close() if len(handles) > 0 { fdArray := handles.fdArray() attr.FdArray = sys.NewPointer(unsafe.Pointer(&fdArray[0])) } buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) err = insns.Marshal(buf, internal.NativeEndian) if err != nil { return nil, err } bytecode := buf.Bytes() attr.Insns = sys.NewSlicePointer(bytecode) attr.InsnCnt = uint32(len(bytecode) / asm.InstructionSize) if spec.AttachTarget != nil { targetID, err := findTargetInProgram(spec.AttachTarget, spec.AttachTo, spec.Type, spec.AttachType) if err != nil { return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err) } attr.AttachBtfId = targetID attr.AttachBtfObjFd = uint32(spec.AttachTarget.FD()) defer runtime.KeepAlive(spec.AttachTarget) } else if spec.AttachTo != "" { module, targetID, err := findProgramTargetInKernel(spec.AttachTo, spec.Type, spec.AttachType) if err != nil && !errors.Is(err, errUnrecognizedAttachType) { // We ignore errUnrecognizedAttachType since AttachTo may be non-empty // for programs that don't attach anywhere. return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err) } attr.AttachBtfId = targetID if module != nil { attr.AttachBtfObjFd = uint32(module.FD()) defer module.Close() } } if opts.LogSize == 0 { opts.LogSize = DefaultVerifierLogSize } // The caller requested a specific verifier log level. Set up the log buffer. var logBuf []byte if !opts.LogDisabled && opts.LogLevel != 0 { logBuf = make([]byte, opts.LogSize) attr.LogLevel = opts.LogLevel attr.LogSize = uint32(len(logBuf)) attr.LogBuf = sys.NewSlicePointer(logBuf) } fd, err := sys.ProgLoad(attr) if err == nil { return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil } // An error occurred loading the program, but the caller did not explicitly // enable the verifier log. Re-run with branch-level verifier logs enabled to // obtain more info. Preserve the original error to return it to the caller. // An undersized log buffer will result in ENOSPC regardless of the underlying // cause. var err2 error if !opts.LogDisabled && opts.LogLevel == 0 { logBuf = make([]byte, opts.LogSize) attr.LogLevel = LogLevelBranch attr.LogSize = uint32(len(logBuf)) attr.LogBuf = sys.NewSlicePointer(logBuf) _, err2 = sys.ProgLoad(attr) } switch { case errors.Is(err, unix.EPERM): if len(logBuf) > 0 && logBuf[0] == 0 { // EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can // check that the log is empty to reduce false positives. return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } fallthrough case errors.Is(err, unix.EINVAL): if hasFunctionReferences(spec.Instructions) { if err := haveBPFToBPFCalls(); err != nil { return nil, fmt.Errorf("load program: %w", err) } } if opts.LogSize > maxVerifierLogSize { return nil, fmt.Errorf("load program: %w (ProgramOptions.LogSize exceeds maximum value of %d)", err, maxVerifierLogSize) } } truncated := errors.Is(err, unix.ENOSPC) || errors.Is(err2, unix.ENOSPC) return nil, internal.ErrorWithLog("load program", err, logBuf, truncated) } // NewProgramFromFD creates a program from a raw fd. // // You should not use fd after calling this function. // // Requires at least Linux 4.10. func NewProgramFromFD(fd int) (*Program, error) { f, err := sys.NewFD(fd) if err != nil { return nil, err } return newProgramFromFD(f) } // NewProgramFromID returns the program for a given id. // // Returns ErrNotExist, if there is no eBPF program with the given id. func NewProgramFromID(id ProgramID) (*Program, error) { fd, err := sys.ProgGetFdById(&sys.ProgGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, fmt.Errorf("get program by id: %w", err) } return newProgramFromFD(fd) } func newProgramFromFD(fd *sys.FD) (*Program, error) { info, err := newProgramInfoFromFd(fd) if err != nil { fd.Close() return nil, fmt.Errorf("discover program type: %w", err) } return &Program{"", fd, info.Name, "", info.Type}, nil } func (p *Program) String() string { if p.name != "" { return fmt.Sprintf("%s(%s)#%v", p.typ, p.name, p.fd) } return fmt.Sprintf("%s(%v)", p.typ, p.fd) } // Type returns the underlying type of the program. func (p *Program) Type() ProgramType { return p.typ } // Info returns metadata about the program. // // Requires at least 4.10. func (p *Program) Info() (*ProgramInfo, error) { return newProgramInfoFromFd(p.fd) } // Handle returns a reference to the program's type information in the kernel. // // Returns ErrNotSupported if the kernel has no BTF support, or if there is no // BTF associated with the program. func (p *Program) Handle() (*btf.Handle, error) { info, err := p.Info() if err != nil { return nil, err } id, ok := info.BTFID() if !ok { return nil, fmt.Errorf("program %s: retrieve BTF ID: %w", p, ErrNotSupported) } return btf.NewHandleFromID(id) } // FD gets the file descriptor of the Program. // // It is invalid to call this function after Close has been called. func (p *Program) FD() int { return p.fd.Int() } // Clone creates a duplicate of the Program. // // Closing the duplicate does not affect the original, and vice versa. // // Cloning a nil Program returns nil. func (p *Program) Clone() (*Program, error) { if p == nil { return nil, nil } dup, err := p.fd.Dup() if err != nil { return nil, fmt.Errorf("can't clone program: %w", err) } return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil } // Pin persists the Program on the BPF virtual file system past the lifetime of // the process that created it // // Calling Pin on a previously pinned program will overwrite the path, except when // the new path already exists. Re-pinning across filesystems is not supported. // // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (p *Program) Pin(fileName string) error { if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil { return err } p.pinnedPath = fileName return nil } // Unpin removes the persisted state for the Program from the BPF virtual filesystem. // // Failed calls to Unpin will not alter the state returned by IsPinned. // // Unpinning an unpinned Program returns nil. func (p *Program) Unpin() error { if err := internal.Unpin(p.pinnedPath); err != nil { return err } p.pinnedPath = "" return nil } // IsPinned returns true if the Program has a non-empty pinned path. func (p *Program) IsPinned() bool { return p.pinnedPath != "" } // Close the Program's underlying file descriptor, which could unload // the program from the kernel if it is not pinned or attached to a // kernel hook. func (p *Program) Close() error { if p == nil { return nil } return p.fd.Close() } // Various options for Run'ing a Program type RunOptions struct { // Program's data input. Required field. // // The kernel expects at least 14 bytes input for an ethernet header for // XDP and SKB programs. Data []byte // Program's data after Program has run. Caller must allocate. Optional field. DataOut []byte // Program's context input. Optional field. Context interface{} // Program's context after Program has run. Must be a pointer or slice. Optional field. ContextOut interface{} // Minimum number of times to run Program. Optional field. Defaults to 1. // // The program may be executed more often than this due to interruptions, e.g. // when runtime.AllThreadsSyscall is invoked. Repeat uint32 // Optional flags. Flags uint32 // CPU to run Program on. Optional field. // Note not all program types support this field. CPU uint32 // Called whenever the syscall is interrupted, and should be set to testing.B.ResetTimer // or similar. Typically used during benchmarking. Optional field. // // Deprecated: use [testing.B.ReportMetric] with unit "ns/op" instead. Reset func() } // Test runs the Program in the kernel with the given input and returns the // value returned by the eBPF program. outLen may be zero. // // Note: the kernel expects at least 14 bytes input for an ethernet header for // XDP and SKB programs. // // This function requires at least Linux 4.12. func (p *Program) Test(in []byte) (uint32, []byte, error) { // Older kernels ignore the dataSizeOut argument when copying to user space. // Combined with things like bpf_xdp_adjust_head() we don't really know what the final // size will be. Hence we allocate an output buffer which we hope will always be large // enough, and panic if the kernel wrote past the end of the allocation. // See https://patchwork.ozlabs.org/cover/1006822/ var out []byte if len(in) > 0 { out = make([]byte, len(in)+outputPad) } opts := RunOptions{ Data: in, DataOut: out, Repeat: 1, } ret, _, err := p.run(&opts) if err != nil { return ret, nil, fmt.Errorf("test program: %w", err) } return ret, opts.DataOut, nil } // Run runs the Program in kernel with given RunOptions. // // Note: the same restrictions from Test apply. func (p *Program) Run(opts *RunOptions) (uint32, error) { ret, _, err := p.run(opts) if err != nil { return ret, fmt.Errorf("run program: %w", err) } return ret, nil } // Benchmark runs the Program with the given input for a number of times // and returns the time taken per iteration. // // Returns the result of the last execution of the program and the time per // run or an error. reset is called whenever the benchmark syscall is // interrupted, and should be set to testing.B.ResetTimer or similar. // // This function requires at least Linux 4.12. func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { if uint(repeat) > math.MaxUint32 { return 0, 0, fmt.Errorf("repeat is too high") } opts := RunOptions{ Data: in, Repeat: uint32(repeat), Reset: reset, } ret, total, err := p.run(&opts) if err != nil { return ret, total, fmt.Errorf("benchmark program: %w", err) } return ret, total, nil } var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", "4.12", func() error { prog, err := NewProgram(&ProgramSpec{ // SocketFilter does not require privileges on newer kernels. Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { // This may be because we lack sufficient permissions, etc. return err } defer prog.Close() in := internal.EmptyBPFContext attr := sys.ProgRunAttr{ ProgFd: uint32(prog.FD()), DataSizeIn: uint32(len(in)), DataIn: sys.NewSlicePointer(in), } err = sys.ProgRun(&attr) switch { case errors.Is(err, unix.EINVAL): // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. return internal.ErrNotSupported case errors.Is(err, unix.EINTR): // We know that PROG_TEST_RUN is supported if we get EINTR. return nil case errors.Is(err, sys.ENOTSUPP): // The first PROG_TEST_RUN patches shipped in 4.12 didn't include // a test runner for SocketFilter. ENOTSUPP means PROG_TEST_RUN is // supported, but not for the program type used in the probe. return nil } return err }) func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) { if uint(len(opts.Data)) > math.MaxUint32 { return 0, 0, fmt.Errorf("input is too long") } if err := haveProgRun(); err != nil { return 0, 0, err } var ctxBytes []byte if opts.Context != nil { ctx := new(bytes.Buffer) if err := binary.Write(ctx, internal.NativeEndian, opts.Context); err != nil { return 0, 0, fmt.Errorf("cannot serialize context: %v", err) } ctxBytes = ctx.Bytes() } var ctxOut []byte if opts.ContextOut != nil { ctxOut = make([]byte, binary.Size(opts.ContextOut)) } attr := sys.ProgRunAttr{ ProgFd: p.fd.Uint(), DataSizeIn: uint32(len(opts.Data)), DataSizeOut: uint32(len(opts.DataOut)), DataIn: sys.NewSlicePointer(opts.Data), DataOut: sys.NewSlicePointer(opts.DataOut), Repeat: uint32(opts.Repeat), CtxSizeIn: uint32(len(ctxBytes)), CtxSizeOut: uint32(len(ctxOut)), CtxIn: sys.NewSlicePointer(ctxBytes), CtxOut: sys.NewSlicePointer(ctxOut), Flags: opts.Flags, Cpu: opts.CPU, } if attr.Repeat == 0 { attr.Repeat = 1 } retry: for { err := sys.ProgRun(&attr) if err == nil { break retry } if errors.Is(err, unix.EINTR) { if attr.Repeat == 1 { // Older kernels check whether enough repetitions have been // executed only after checking for pending signals. // // run signal? done? run ... // // As a result we can get EINTR for repeat==1 even though // the program was run exactly once. Treat this as a // successful run instead. // // Since commit 607b9cc92bd7 ("bpf: Consolidate shared test timing code") // the conditions are reversed: // run done? signal? ... break retry } if opts.Reset != nil { opts.Reset() } continue retry } if errors.Is(err, sys.ENOTSUPP) { return 0, 0, fmt.Errorf("kernel doesn't support running %s: %w", p.Type(), ErrNotSupported) } return 0, 0, err } if opts.DataOut != nil { if int(attr.DataSizeOut) > cap(opts.DataOut) { // Houston, we have a problem. The program created more data than we allocated, // and the kernel wrote past the end of our buffer. panic("kernel wrote past end of output buffer") } opts.DataOut = opts.DataOut[:int(attr.DataSizeOut)] } if len(ctxOut) != 0 { b := bytes.NewReader(ctxOut) if err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil { return 0, 0, fmt.Errorf("failed to decode ContextOut: %v", err) } } total := time.Duration(attr.Duration) * time.Nanosecond return attr.Retval, total, nil } func unmarshalProgram(buf []byte) (*Program, error) { if len(buf) != 4 { return nil, errors.New("program id requires 4 byte value") } // Looking up an entry in a nested map or prog array returns an id, // not an fd. id := internal.NativeEndian.Uint32(buf) return NewProgramFromID(ProgramID(id)) } func marshalProgram(p *Program, length int) ([]byte, error) { if length != 4 { return nil, fmt.Errorf("can't marshal program to %d bytes", length) } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, p.fd.Uint()) return buf, nil } // LoadPinnedProgram loads a Program from a BPF file. // // Requires at least Linux 4.11. func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) { fd, err := sys.ObjGet(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, err } info, err := newProgramInfoFromFd(fd) if err != nil { _ = fd.Close() return nil, fmt.Errorf("info for %s: %w", fileName, err) } var progName string if haveObjName() == nil { progName = info.Name } else { progName = filepath.Base(fileName) } return &Program{"", fd, progName, fileName, info.Type}, nil } // SanitizeName replaces all invalid characters in name with replacement. // Passing a negative value for replacement will delete characters instead // of replacing them. Use this to automatically generate valid names for maps // and programs at runtime. // // The set of allowed characters depends on the running kernel version. // Dots are only allowed as of kernel 5.2. func SanitizeName(name string, replacement rune) string { return strings.Map(func(char rune) rune { if invalidBPFObjNameChar(char) { return replacement } return char }, name) } // ProgramGetNextID returns the ID of the next eBPF program. // // Returns ErrNotExist, if there is no next eBPF program. func ProgramGetNextID(startID ProgramID) (ProgramID, error) { attr := &sys.ProgGetNextIdAttr{Id: uint32(startID)} return ProgramID(attr.NextId), sys.ProgGetNextId(attr) } // BindMap binds map to the program and is only released once program is released. // // This may be used in cases where metadata should be associated with the program // which otherwise does not contain any references to the map. func (p *Program) BindMap(m *Map) error { attr := &sys.ProgBindMapAttr{ ProgFd: uint32(p.FD()), MapFd: uint32(m.FD()), } return sys.ProgBindMap(attr) } var errUnrecognizedAttachType = errors.New("unrecognized attach type") // find an attach target type in the kernel. // // name, progType and attachType determine which type we need to attach to. // // The attach target may be in a loaded kernel module. // In that case the returned handle will be non-nil. // The caller is responsible for closing the handle. // // Returns errUnrecognizedAttachType if the combination of progType and attachType // is not recognised. func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType) (*btf.Handle, btf.TypeID, error) { type match struct { p ProgramType a AttachType } var ( typeName, featureName string target btf.Type ) switch (match{progType, attachType}) { case match{LSM, AttachLSMMac}: typeName = "bpf_lsm_" + name featureName = name + " LSM hook" target = (*btf.Func)(nil) case match{Tracing, AttachTraceIter}: typeName = "bpf_iter_" + name featureName = name + " iterator" target = (*btf.Func)(nil) case match{Tracing, AttachTraceFEntry}: typeName = name featureName = fmt.Sprintf("fentry %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachTraceFExit}: typeName = name featureName = fmt.Sprintf("fexit %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachModifyReturn}: typeName = name featureName = fmt.Sprintf("fmod_ret %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachTraceRawTp}: typeName = fmt.Sprintf("btf_trace_%s", name) featureName = fmt.Sprintf("raw_tp %s", name) target = (*btf.Typedef)(nil) default: return nil, 0, errUnrecognizedAttachType } spec, err := btf.LoadKernelSpec() if err != nil { return nil, 0, fmt.Errorf("load kernel spec: %w", err) } spec, module, err := findTargetInKernel(spec, typeName, &target) if errors.Is(err, btf.ErrNotFound) { return nil, 0, &internal.UnsupportedFeatureError{Name: featureName} } // See cilium/ebpf#894. Until we can disambiguate between equally-named kernel // symbols, we should explicitly refuse program loads. They will not reliably // do what the caller intended. if errors.Is(err, btf.ErrMultipleMatches) { return nil, 0, fmt.Errorf("attaching to ambiguous kernel symbol is not supported: %w", err) } if err != nil { return nil, 0, fmt.Errorf("find target for %s: %w", featureName, err) } id, err := spec.TypeID(target) return module, id, err } // findTargetInKernel attempts to find a named type in the current kernel. // // target will point at the found type after a successful call. Searches both // vmlinux and any loaded modules. // // Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound // if the type wasn't found at all. func findTargetInKernel(kernelSpec *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) { err := kernelSpec.TypeByName(typeName, target) if errors.Is(err, btf.ErrNotFound) { spec, module, err := findTargetInModule(kernelSpec, typeName, target) if err != nil { return nil, nil, fmt.Errorf("find target in modules: %w", err) } return spec, module, nil } if err != nil { return nil, nil, fmt.Errorf("find target in vmlinux: %w", err) } return kernelSpec, nil, err } // findTargetInModule attempts to find a named type in any loaded module. // // base must contain the kernel's types and is used to parse kmod BTF. Modules // are searched in the order they were loaded. // // Returns btf.ErrNotFound if the target can't be found in any module. func findTargetInModule(base *btf.Spec, typeName string, target *btf.Type) (*btf.Spec, *btf.Handle, error) { it := new(btf.HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { return nil, nil, fmt.Errorf("get info for BTF ID %d: %w", it.ID, err) } if !info.IsModule() { continue } spec, err := it.Handle.Spec(base) if err != nil { return nil, nil, fmt.Errorf("parse types for module %s: %w", info.Name, err) } err = spec.TypeByName(typeName, target) if errors.Is(err, btf.ErrNotFound) { continue } if err != nil { return nil, nil, fmt.Errorf("lookup type in module %s: %w", info.Name, err) } return spec, it.Take(), nil } if err := it.Err(); err != nil { return nil, nil, fmt.Errorf("iterate modules: %w", err) } return nil, nil, btf.ErrNotFound } // find an attach target type in a program. // // Returns errUnrecognizedAttachType. func findTargetInProgram(prog *Program, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) { type match struct { p ProgramType a AttachType } var typeName string switch (match{progType, attachType}) { case match{Extension, AttachNone}: typeName = name default: return 0, errUnrecognizedAttachType } btfHandle, err := prog.Handle() if err != nil { return 0, fmt.Errorf("load target BTF: %w", err) } defer btfHandle.Close() spec, err := btfHandle.Spec(nil) if err != nil { return 0, err } var targetFunc *btf.Func err = spec.TypeByName(typeName, &targetFunc) if err != nil { return 0, fmt.Errorf("find target %s: %w", typeName, err) } return spec.TypeID(targetFunc) } golang-github-cilium-ebpf-0.11.0/prog_test.go000066400000000000000000000573301456504511400210640ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "os" "path/filepath" "runtime" "strings" "syscall" "testing" "time" qt "github.com/frankban/quicktest" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestProgramRun(t *testing.T) { testutils.SkipOnOldKernel(t, "4.8", "XDP program") pat := []byte{0xDE, 0xAD, 0xBE, 0xEF} buf := internal.EmptyBPFContext // r1 : ctx_start // r1+4: ctx_end ins := asm.Instructions{ // r2 = *(r1+4) asm.LoadMem(asm.R2, asm.R1, 4, asm.Word), // r1 = *(r1+0) asm.LoadMem(asm.R1, asm.R1, 0, asm.Word), // r3 = r1 asm.Mov.Reg(asm.R3, asm.R1), // r3 += len(buf) asm.Add.Imm(asm.R3, int32(len(buf))), // if r3 > r2 goto +len(pat) asm.JGT.Reg(asm.R3, asm.R2, "out"), } for i, b := range pat { ins = append(ins, asm.StoreImm(asm.R1, int16(i), int64(b), asm.Byte)) } ins = append(ins, // return 42 asm.LoadImm(asm.R0, 42, asm.DWord).WithSymbol("out"), asm.Return(), ) t.Log(ins) prog, err := NewProgram(&ProgramSpec{ Name: "test", Type: XDP, Instructions: ins, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() p2, err := prog.Clone() if err != nil { t.Fatal("Can't clone program") } defer p2.Close() prog.Close() prog = p2 ret, out, err := prog.Test(buf) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 42 { t.Error("Expected return value to be 42, got", ret) } if !bytes.Equal(out[:len(pat)], pat) { t.Errorf("Expected %v, got %v", pat, out) } } func TestProgramRunWithOptions(t *testing.T) { testutils.SkipOnOldKernel(t, "5.15", "XDP ctx_in/ctx_out") ins := asm.Instructions{ // Return XDP_ABORTED asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } prog, err := NewProgram(&ProgramSpec{ Name: "test", Type: XDP, Instructions: ins, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() buf := internal.EmptyBPFContext xdp := sys.XdpMd{ Data: 0, DataEnd: uint32(len(buf)), } xdpOut := sys.XdpMd{} opts := RunOptions{ Data: buf, Context: xdp, ContextOut: &xdpOut, } ret, err := prog.Run(&opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 0 { t.Error("Expected return value to be 0, got", ret) } if xdp != xdpOut { t.Errorf("Expect xdp (%+v) == xdpOut (%+v)", xdp, xdpOut) } } func TestProgramRunEmptyData(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "sk_lookup BPF_PROG_RUN") ins := asm.Instructions{ // Return SK_DROP asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } prog, err := NewProgram(&ProgramSpec{ Name: "test", Type: SkLookup, AttachType: AttachSkLookup, Instructions: ins, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() opts := RunOptions{ Context: sys.SkLookup{ Family: syscall.AF_INET, }, } ret, err := prog.Run(&opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 0 { t.Error("Expected return value to be 0, got", ret) } } func TestProgramBenchmark(t *testing.T) { prog := mustSocketFilter(t) ret, duration, err := prog.Benchmark(internal.EmptyBPFContext, 1, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error from Benchmark:", err) } if ret != 2 { t.Error("Expected return value 2, got", ret) } if duration == 0 { t.Error("Expected non-zero duration") } } func TestProgramTestRunInterrupt(t *testing.T) { testutils.SkipOnOldKernel(t, "5.0", "EINTR from BPF_PROG_TEST_RUN") prog := mustSocketFilter(t) var ( tgid = unix.Getpid() tidChan = make(chan int, 1) exit = make(chan struct{}) errs = make(chan error, 1) timeout = time.After(5 * time.Second) ) defer close(exit) go func() { runtime.LockOSThread() defer func() { // Wait for the test to allow us to unlock the OS thread, to // ensure that we don't send SIGUSR1 to the wrong thread. <-exit runtime.UnlockOSThread() }() tidChan <- unix.Gettid() // Block this thread in the BPF syscall, so that we can // trigger EINTR by sending a signal. opts := RunOptions{ Data: internal.EmptyBPFContext, Repeat: math.MaxInt32, Reset: func() { // We don't know how long finishing the // test run would take, so flag that we've seen // an interruption and abort the goroutine. close(errs) runtime.Goexit() }, } _, _, err := prog.run(&opts) errs <- err }() tid := <-tidChan for { err := unix.Tgkill(tgid, tid, syscall.SIGUSR1) if err != nil { t.Fatal("Can't send signal to goroutine thread:", err) } select { case err, ok := <-errs: if !ok { return } testutils.SkipIfNotSupported(t, err) if err == nil { t.Fatal("testRun wasn't interrupted") } t.Fatal("testRun returned an error:", err) case <-timeout: t.Fatal("Timed out trying to interrupt the goroutine") default: } } } func TestProgramClose(t *testing.T) { prog := mustSocketFilter(t) if err := prog.Close(); err != nil { t.Fatal("Can't close program:", err) } } func TestProgramPin(t *testing.T) { prog := mustSocketFilter(t) c := qt.New(t) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } pinned := prog.IsPinned() c.Assert(pinned, qt.IsTrue) prog.Close() prog, err := LoadPinnedProgram(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer prog.Close() if prog.Type() != SocketFilter { t.Error("Expected pinned program to have type SocketFilter, but got", prog.Type()) } if haveObjName() == nil { if prog.name != "test" { t.Errorf("Expected program to have object name 'test', got '%s'", prog.name) } } else { if prog.name != "program" { t.Errorf("Expected program to have file name 'program', got '%s'", prog.name) } } if !prog.IsPinned() { t.Error("Expected IsPinned to be true") } } func TestProgramUnpin(t *testing.T) { prog := mustSocketFilter(t) c := qt.New(t) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } pinned := prog.IsPinned() c.Assert(pinned, qt.IsTrue) if err := prog.Unpin(); err != nil { t.Fatal("Failed to unpin program:", err) } if _, err := os.Stat(path); err == nil { t.Fatal("Pinned program path still exists after unpinning:", err) } } func TestProgramLoadPinnedWithFlags(t *testing.T) { // Introduced in commit 6e71b04a8224. testutils.SkipOnOldKernel(t, "4.14", "file_flags in BPF_OBJ_GET") prog := mustSocketFilter(t) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } prog.Close() _, err := LoadPinnedProgram(path, &LoadPinOptions{ Flags: math.MaxUint32, }) testutils.SkipIfNotSupported(t, err) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags don't trigger an error:", err) } } func TestProgramVerifierOutputOnError(t *testing.T) { _, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.Return(), }, License: "MIT", }) if err == nil { t.Fatal("Expected program to be invalid") } ve, ok := err.(*VerifierError) if !ok { t.Fatal("NewProgram does return an unwrapped VerifierError") } if !strings.Contains(ve.Error(), "R0 !read_ok") { t.Logf("%+v", ve) t.Error("Missing verifier log in error summary") } } func TestProgramKernelVersion(t *testing.T) { testutils.SkipOnOldKernel(t, "4.20", "KernelVersion") prog, err := NewProgram(&ProgramSpec{ Type: Kprobe, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, KernelVersion: 42, License: "MIT", }) if err != nil { t.Fatal("Could not load Kprobe program") } defer prog.Close() } func TestProgramVerifierOutput(t *testing.T) { prog, err := NewProgramWithOptions(socketFilterSpec, ProgramOptions{ LogLevel: LogLevelInstruction, }) if err != nil { t.Fatal(err) } defer prog.Close() if prog.VerifierLog == "" { t.Error("Expected VerifierLog to be present") } // Issue 64 _, err = NewProgramWithOptions(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.Mov.Reg(asm.R0, asm.R1), }, License: "MIT", }, ProgramOptions{ LogLevel: LogLevelInstruction, }) if err == nil { t.Fatal("Expected an error from invalid program") } var ve *internal.VerifierError if !errors.As(err, &ve) { t.Error("Error is not a VerifierError") } } // Test all scenarios where the VerifierError.Truncated flag is expected to be // true, marked with an x. LL means ProgramOption.LogLevel. // // | | Valid | Invalid | // |------|-------|---------| // | LL=0 | | x | // | LL>0 | x | x | func TestProgramVerifierLogTruncated(t *testing.T) { // Make the buffer intentionally small to coerce ENOSPC. // 128 bytes is the smallest the kernel will accept. logSize := 128 check := func(t *testing.T, err error) { if err == nil { t.Fatal("Expected an error") } var ve *internal.VerifierError if !errors.As(err, &ve) { t.Fatal("Error is not a VerifierError") } if !ve.Truncated { t.Errorf("VerifierError is not truncated: %+v", ve) } } // Generate a base program of sufficient size whose verifier log does not fit // a 128-byte buffer. This should always result in ENOSPC, setting the // VerifierError.Truncated flag. base := func() (out asm.Instructions) { for i := 0; i < 32; i++ { out = append(out, asm.Mov.Reg(asm.R0, asm.R1)) } return }() invalid := func() (out asm.Instructions) { out = base // Touch R10 (read-only frame pointer) to reliably force a verifier error. out = append(out, asm.Mov.Reg(asm.R10, asm.R0)) out = append(out, asm.Return()) return }() valid := func() (out asm.Instructions) { out = base out = append(out, asm.Return()) return }() // Start out with testing against the invalid program. spec := &ProgramSpec{ Type: SocketFilter, License: "MIT", Instructions: invalid, } // Set an undersized log buffer without explicitly requesting a verifier log // for an invalid program. _, err := NewProgramWithOptions(spec, ProgramOptions{LogSize: logSize}) check(t, err) // Explicitly request a verifier log for an invalid program. _, err = NewProgramWithOptions(spec, ProgramOptions{ LogSize: logSize, LogLevel: LogLevelInstruction, }) check(t, err) // Run tests against a valid program from here on out. spec.Instructions = valid // Don't request a verifier log, only set LogSize. Expect the valid program to // be created without errors. prog, err := NewProgramWithOptions(spec, ProgramOptions{ LogSize: logSize, }) if err != nil { t.Fatal(err) } prog.Close() // Explicitly request verifier log for a valid program. If a log is requested // and the buffer is too small, ENOSPC occurs even for valid programs. _, err = NewProgramWithOptions(spec, ProgramOptions{ LogSize: logSize, LogLevel: LogLevelInstruction, }) check(t, err) } func TestProgramWithUnsatisfiedMap(t *testing.T) { coll, err := LoadCollectionSpec("testdata/loader-el.elf") if err != nil { t.Fatal(err) } // The program will have at least one map reference. progSpec := coll.Programs["xdp_prog"] progSpec.ByteOrder = nil _, err = NewProgram(progSpec) testutils.SkipIfNotSupported(t, err) if !errors.Is(err, asm.ErrUnsatisfiedMapReference) { t.Fatal("Expected an error wrapping asm.ErrUnsatisfiedMapReference, got", err) } t.Log(err) } func TestProgramName(t *testing.T) { if err := haveObjName(); err != nil { t.Skip(err) } prog := mustSocketFilter(t) var info sys.ProgInfo if err := sys.ObjInfo(prog.fd, &info); err != nil { t.Fatal(err) } if name := unix.ByteSliceToString(info.Name[:]); name != "test" { t.Errorf("Name is not test, got '%s'", name) } } func TestSanitizeName(t *testing.T) { for input, want := range map[string]string{ "test": "test", "t-est": "test", "t_est": "t_est", "hörnchen": "hrnchen", } { if have := SanitizeName(input, -1); have != want { t.Errorf("Wanted '%s' got '%s'", want, have) } } } func TestProgramCloneNil(t *testing.T) { p, err := (*Program)(nil).Clone() if err != nil { t.Fatal(err) } if p != nil { t.Fatal("Cloning a nil Program doesn't return nil") } } func TestProgramMarshaling(t *testing.T) { const idx = uint32(0) arr := createProgramArray(t) defer arr.Close() prog := mustSocketFilter(t) if err := arr.Put(idx, prog); err != nil { t.Fatal("Can't put program:", err) } if err := arr.Lookup(idx, Program{}); err == nil { t.Fatal("Lookup accepts non-pointer Program") } var prog2 *Program defer prog2.Close() if err := arr.Lookup(idx, prog2); err == nil { t.Fatal("Get accepts *Program") } testutils.SkipOnOldKernel(t, "4.12", "lookup for ProgramArray") if err := arr.Lookup(idx, &prog2); err != nil { t.Fatal("Can't unmarshal program:", err) } defer prog2.Close() if prog2 == nil { t.Fatal("Unmarshalling set program to nil") } } func TestProgramFromFD(t *testing.T) { prog := mustSocketFilter(t) // If you're thinking about copying this, don't. Use // Clone() instead. prog2, err := NewProgramFromFD(dupFD(t, prog.FD())) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer prog2.Close() // Name and type are supposed to be copied from program info. if haveObjName() == nil && prog2.name != "test" { t.Errorf("Expected program to have name test, got '%s'", prog2.name) } if prog2.typ != SocketFilter { t.Errorf("Expected program to have type SocketFilter, got '%s'", prog2.typ) } } func TestHaveProgTestRun(t *testing.T) { testutils.CheckFeatureTest(t, haveProgRun) } func TestProgramGetNextID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_next_id") // Ensure there is at least one program loaded _ = mustSocketFilter(t) // As there can be multiple eBPF programs, we loop over all of them and // make sure, the IDs increase and the last call will return ErrNotExist last := ProgramID(0) for { next, err := ProgramGetNextID(last) if errors.Is(err, os.ErrNotExist) { if last == 0 { t.Fatal("Got ErrNotExist on the first iteration") } break } if err != nil { t.Fatal("Unexpected error:", err) } if next <= last { t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last) } last = next } } func TestNewProgramFromID(t *testing.T) { prog := mustSocketFilter(t) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Could not get program info:", err) } id, ok := info.ID() if !ok { t.Skip("Program ID not supported") } prog2, err := NewProgramFromID(id) if err != nil { t.Fatalf("Can't get FD for program ID %d: %v", id, err) } prog2.Close() // As there can be multiple programs, we use max(uint32) as ProgramID to trigger an expected error. _, err = NewProgramFromID(ProgramID(math.MaxUint32)) if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } } func TestProgramRejectIncorrectByteOrder(t *testing.T) { spec := socketFilterSpec.Copy() spec.ByteOrder = binary.BigEndian if internal.NativeEndian == binary.BigEndian { spec.ByteOrder = binary.LittleEndian } _, err := NewProgram(spec) if err == nil { t.Error("Incorrect ByteOrder should be rejected at load time") } } func TestProgramSpecTag(t *testing.T) { arr := createArray(t) defer arr.Close() spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, -1, asm.DWord), asm.LoadMapPtr(asm.R1, arr.FD()), asm.Mov.Imm32(asm.R0, 0), asm.Return(), }, License: "MIT", } prog, err := NewProgram(spec) if err != nil { t.Fatal(err) } defer prog.Close() info, err := prog.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } tag, err := spec.Tag() if err != nil { t.Fatal("Can't calculate tag:", err) } if tag != info.Tag { t.Errorf("Calculated tag %s doesn't match kernel tag %s", tag, info.Tag) } } func TestProgramAttachToKernel(t *testing.T) { // See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744 testutils.SkipOnOldKernel(t, "5.5", "attach_btf_id") haveTestmod := haveTestmod(t) tests := []struct { attachTo string programType ProgramType attachType AttachType flags uint32 }{ { attachTo: "task_getpgid", programType: LSM, attachType: AttachLSMMac, }, { attachTo: "inet_dgram_connect", programType: Tracing, attachType: AttachTraceFEntry, }, { attachTo: "inet_dgram_connect", programType: Tracing, attachType: AttachTraceFExit, }, { attachTo: "bpf_modify_return_test", programType: Tracing, attachType: AttachModifyReturn, }, { attachTo: "kfree_skb", programType: Tracing, attachType: AttachTraceRawTp, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceFEntry, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceFExit, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachModifyReturn, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceRawTp, }, } for _, test := range tests { name := fmt.Sprintf("%s:%s", test.attachType, test.attachTo) t.Run(name, func(t *testing.T) { if strings.HasPrefix(test.attachTo, "bpf_testmod_") && !haveTestmod { t.Skip("bpf_testmod not loaded") } prog, err := NewProgram(&ProgramSpec{ AttachTo: test.attachTo, AttachType: test.attachType, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", Type: test.programType, Flags: test.flags, }) if err != nil { t.Fatal("Can't load program:", err) } prog.Close() }) } } func TestProgramKernelTypes(t *testing.T) { if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) { t.Skip("/sys/kernel/btf/vmlinux not present") } btfSpec, err := btf.LoadSpec("/sys/kernel/btf/vmlinux") if err != nil { t.Fatal(err) } prog, err := NewProgramWithOptions(&ProgramSpec{ Type: Tracing, AttachType: AttachTraceIter, AttachTo: "bpf_map", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }, ProgramOptions{ KernelTypes: btfSpec, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("NewProgram with Target:", err) } prog.Close() } func TestProgramBindMap(t *testing.T) { testutils.SkipOnOldKernel(t, "5.10", "BPF_PROG_BIND_MAP") arr, err := NewMap(&MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Errorf("Failed to load map: %v", err) } defer arr.Close() prog := mustSocketFilter(t) // The attached map does not contain BTF information. So // the metadata part of the program will be empty. This // test just makes sure that we can bind a map to a program. if err := prog.BindMap(arr); err != nil { t.Errorf("Failed to bind map to program: %v", err) } } func TestProgramInstructions(t *testing.T) { name := "test_prog" spec := &ProgramSpec{ Type: SocketFilter, Name: name, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, -1, asm.DWord).WithSymbol(name), asm.Return(), }, License: "MIT", } prog, err := NewProgram(spec) if err != nil { t.Fatal(err) } defer prog.Close() pi, err := prog.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } insns, err := pi.Instructions() if err != nil { t.Fatal(err) } tag, err := spec.Tag() if err != nil { t.Fatal(err) } tagXlated, err := insns.Tag(internal.NativeEndian) if err != nil { t.Fatal(err) } if tag != tagXlated { t.Fatalf("tag %s differs from xlated instructions tag %s", tag, tagXlated) } } func createProgramArray(t *testing.T) *Map { t.Helper() arr, err := NewMap(&MapSpec{ Type: ProgramArray, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Fatal(err) } return arr } var socketFilterSpec = &ProgramSpec{ Name: "test", Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 2, asm.DWord), asm.Return(), }, License: "MIT", } func mustSocketFilter(tb testing.TB) *Program { tb.Helper() prog, err := NewProgram(socketFilterSpec) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return prog } // Print the full verifier log when loading a program fails. func ExampleVerifierError_retrieveFullLog() { _, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), // Missing Return }, License: "MIT", }) var ve *VerifierError if errors.As(err, &ve) { // Using %+v will print the whole verifier error, not just the last // few lines. fmt.Printf("Verifier error: %+v\n", ve) } } // VerifierLog understands a variety of formatting flags. func ExampleVerifierError() { err := internal.ErrorWithLog( "catastrophe", syscall.ENOSPC, []byte("first\nsecond\nthird"), false, ) fmt.Printf("With %%s: %s\n", err) err.Truncated = true fmt.Printf("With %%v and a truncated log: %v\n", err) fmt.Printf("All log lines: %+v\n", err) fmt.Printf("First line: %+1v\n", err) fmt.Printf("Last two lines: %-2v\n", err) // Output: With %s: catastrophe: no space left on device: third (2 line(s) omitted) // With %v and a truncated log: catastrophe: no space left on device: second: third (truncated, 1 line(s) omitted) // All log lines: catastrophe: no space left on device: // first // second // third // (truncated) // First line: catastrophe: no space left on device: // first // (2 line(s) omitted) // (truncated) // Last two lines: catastrophe: no space left on device: // (1 line(s) omitted) // second // third // (truncated) } // Use NewProgramWithOptions if you'd like to get the verifier output // for a program, or if you want to change the buffer size used when // generating error messages. func ExampleProgram_retrieveVerifierLog() { spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } prog, err := NewProgramWithOptions(spec, ProgramOptions{ LogLevel: LogLevelInstruction, LogSize: 1024, }) if err != nil { panic(err) } defer prog.Close() fmt.Println("The verifier output is:") fmt.Println(prog.VerifierLog) } // It's possible to read a program directly from a ProgramArray. func ExampleProgram_unmarshalFromMap() { progArray, err := LoadPinnedMap("/path/to/map", nil) if err != nil { panic(err) } defer progArray.Close() // Load a single program var prog *Program if err := progArray.Lookup(uint32(0), &prog); err != nil { panic(err) } defer prog.Close() fmt.Println("first prog:", prog) // Iterate all programs var ( key uint32 entries = progArray.Iterate() ) for entries.Next(&key, &prog) { fmt.Println(key, "is", prog) } if err := entries.Err(); err != nil { panic(err) } } func ExampleProgramSpec_Tag() { spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } prog, _ := NewProgram(spec) info, _ := prog.Info() tag, _ := spec.Tag() if info.Tag != tag { fmt.Printf("The tags don't match: %s != %s\n", info.Tag, tag) } else { fmt.Println("The programs are identical, tag is", tag) } } func dupFD(tb testing.TB, fd int) int { tb.Helper() dup, err := unix.FcntlInt(uintptr(fd), unix.F_DUPFD_CLOEXEC, 1) if err != nil { tb.Fatal("Can't dup fd:", err) } return dup } golang-github-cilium-ebpf-0.11.0/ringbuf/000077500000000000000000000000001456504511400201535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/ringbuf/doc.go000066400000000000000000000004231456504511400212460ustar00rootroot00000000000000// Package ringbuf allows interacting with Linux BPF ring buffer. // // BPF allows submitting custom events to a BPF ring buffer map set up // by userspace. This is very useful to push things like packet samples // from BPF to a daemon running in user space. package ringbuf golang-github-cilium-ebpf-0.11.0/ringbuf/reader.go000066400000000000000000000121051456504511400217430ustar00rootroot00000000000000package ringbuf import ( "encoding/binary" "errors" "fmt" "io" "os" "sync" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/epoll" "github.com/cilium/ebpf/internal/unix" ) var ( ErrClosed = os.ErrClosed errEOR = errors.New("end of ring") errDiscard = errors.New("sample discarded") errBusy = errors.New("sample not committed yet") ) var ringbufHeaderSize = binary.Size(ringbufHeader{}) // ringbufHeader from 'struct bpf_ringbuf_hdr' in kernel/bpf/ringbuf.c type ringbufHeader struct { Len uint32 PgOff uint32 } func (rh *ringbufHeader) isBusy() bool { return rh.Len&unix.BPF_RINGBUF_BUSY_BIT != 0 } func (rh *ringbufHeader) isDiscard() bool { return rh.Len&unix.BPF_RINGBUF_DISCARD_BIT != 0 } func (rh *ringbufHeader) dataLen() int { return int(rh.Len & ^uint32(unix.BPF_RINGBUF_BUSY_BIT|unix.BPF_RINGBUF_DISCARD_BIT)) } type Record struct { RawSample []byte } // Read a record from an event ring. // // buf must be at least ringbufHeaderSize bytes long. func readRecord(rd *ringbufEventRing, rec *Record, buf []byte) error { rd.loadConsumer() buf = buf[:ringbufHeaderSize] if _, err := io.ReadFull(rd, buf); err == io.EOF { return errEOR } else if err != nil { return fmt.Errorf("read event header: %w", err) } header := ringbufHeader{ internal.NativeEndian.Uint32(buf[0:4]), internal.NativeEndian.Uint32(buf[4:8]), } if header.isBusy() { // the next sample in the ring is not committed yet so we // exit without storing the reader/consumer position // and start again from the same position. return errBusy } /* read up to 8 byte alignment */ dataLenAligned := uint64(internal.Align(header.dataLen(), 8)) if header.isDiscard() { // when the record header indicates that the data should be // discarded, we skip it by just updating the consumer position // to the next record instead of normal Read() to avoid allocating data // and reading/copying from the ring (which normally keeps track of the // consumer position). rd.skipRead(dataLenAligned) rd.storeConsumer() return errDiscard } if cap(rec.RawSample) < int(dataLenAligned) { rec.RawSample = make([]byte, dataLenAligned) } else { rec.RawSample = rec.RawSample[:dataLenAligned] } if _, err := io.ReadFull(rd, rec.RawSample); err != nil { return fmt.Errorf("read sample: %w", err) } rd.storeConsumer() rec.RawSample = rec.RawSample[:header.dataLen()] return nil } // Reader allows reading bpf_ringbuf_output // from user space. type Reader struct { poller *epoll.Poller // mu protects read/write access to the Reader structure mu sync.Mutex ring *ringbufEventRing epollEvents []unix.EpollEvent header []byte haveData bool deadline time.Time } // NewReader creates a new BPF ringbuf reader. func NewReader(ringbufMap *ebpf.Map) (*Reader, error) { if ringbufMap.Type() != ebpf.RingBuf { return nil, fmt.Errorf("invalid Map type: %s", ringbufMap.Type()) } maxEntries := int(ringbufMap.MaxEntries()) if maxEntries == 0 || (maxEntries&(maxEntries-1)) != 0 { return nil, fmt.Errorf("ringbuffer map size %d is zero or not a power of two", maxEntries) } poller, err := epoll.New() if err != nil { return nil, err } if err := poller.Add(ringbufMap.FD(), 0); err != nil { poller.Close() return nil, err } ring, err := newRingBufEventRing(ringbufMap.FD(), maxEntries) if err != nil { poller.Close() return nil, fmt.Errorf("failed to create ringbuf ring: %w", err) } return &Reader{ poller: poller, ring: ring, epollEvents: make([]unix.EpollEvent, 1), header: make([]byte, ringbufHeaderSize), }, nil } // Close frees resources used by the reader. // // It interrupts calls to Read. func (r *Reader) Close() error { if err := r.poller.Close(); err != nil { if errors.Is(err, os.ErrClosed) { return nil } return err } // Acquire the lock. This ensures that Read isn't running. r.mu.Lock() defer r.mu.Unlock() if r.ring != nil { r.ring.Close() r.ring = nil } return nil } // SetDeadline controls how long Read and ReadInto will block waiting for samples. // // Passing a zero time.Time will remove the deadline. func (r *Reader) SetDeadline(t time.Time) { r.mu.Lock() defer r.mu.Unlock() r.deadline = t } // Read the next record from the BPF ringbuf. // // Returns os.ErrClosed if Close is called on the Reader, or os.ErrDeadlineExceeded // if a deadline was set. func (r *Reader) Read() (Record, error) { var rec Record return rec, r.ReadInto(&rec) } // ReadInto is like Read except that it allows reusing Record and associated buffers. func (r *Reader) ReadInto(rec *Record) error { r.mu.Lock() defer r.mu.Unlock() if r.ring == nil { return fmt.Errorf("ringbuffer: %w", ErrClosed) } for { if !r.haveData { _, err := r.poller.Wait(r.epollEvents[:cap(r.epollEvents)], r.deadline) if err != nil { return err } r.haveData = true } for { err := readRecord(r.ring, rec, r.header) if err == errBusy || err == errDiscard { continue } if err == errEOR { r.haveData = false break } return err } } } golang-github-cilium-ebpf-0.11.0/ringbuf/reader_test.go000066400000000000000000000157441456504511400230160ustar00rootroot00000000000000package ringbuf import ( "errors" "os" "syscall" "testing" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/fdtrace" "github.com/google/go-cmp/cmp" ) func TestMain(m *testing.M) { fdtrace.TestMain(m) } func TestRingbufReader(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") readerTests := []struct { name string messages []int want map[int][]byte }{ { name: "send one short sample", messages: []int{5}, want: map[int][]byte{ 5: {1, 2, 3, 4, 4}, }, }, { name: "send three short samples, the second is discarded", messages: []int{5, 10, 15}, want: map[int][]byte{ 5: {1, 2, 3, 4, 4}, 15: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2}, }, }, } for _, tt := range readerTests { t.Run(tt.name, func(t *testing.T) { prog, events := mustOutputSamplesProg(t, 0, tt.messages...) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if errno := syscall.Errno(-int32(ret)); errno != 0 { t.Fatal("Expected 0 as return value, got", errno) } raw := make(map[int][]byte) for len(raw) < len(tt.want) { record, err := rd.Read() if err != nil { t.Fatal("Can't read samples:", err) } raw[len(record.RawSample)] = record.RawSample } if diff := cmp.Diff(tt.want, raw); diff != "" { t.Errorf("Read samples mismatch (-want +got):\n%s", diff) } }) } } func outputSamplesProg(flags int32, sampleSizes ...int) (*ebpf.Program, *ebpf.Map, error) { events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.RingBuf, MaxEntries: 4096, }) if err != nil { return nil, nil, err } var maxSampleSize int for _, sampleSize := range sampleSizes { if sampleSize > maxSampleSize { maxSampleSize = sampleSize } } insns := asm.Instructions{ asm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord), asm.Mov.Reg(asm.R9, asm.R1), } bufDwords := (maxSampleSize / 8) + 1 for i := 0; i < bufDwords; i++ { insns = append(insns, asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord), ) } for sampleIdx, sampleSize := range sampleSizes { insns = append(insns, asm.LoadMapPtr(asm.R1, events.FD()), asm.Mov.Imm(asm.R2, int32(sampleSize)), asm.Mov.Imm(asm.R3, int32(0)), asm.FnRingbufReserve.Call(), asm.JEq.Imm(asm.R0, 0, "exit"), asm.Mov.Reg(asm.R5, asm.R0), ) for i := 0; i < sampleSize; i++ { insns = append(insns, asm.LoadMem(asm.R4, asm.RFP, int16(i+1)*-1, asm.Byte), asm.StoreMem(asm.R5, int16(i), asm.R4, asm.Byte), ) } // discard every even sample if sampleIdx&1 != 0 { insns = append(insns, asm.Mov.Reg(asm.R1, asm.R5), asm.Mov.Imm(asm.R2, flags), asm.FnRingbufDiscard.Call(), ) } else { insns = append(insns, asm.Mov.Reg(asm.R1, asm.R5), asm.Mov.Imm(asm.R2, flags), asm.FnRingbufSubmit.Call(), ) } } insns = append(insns, asm.Mov.Imm(asm.R0, int32(0)).WithSymbol("exit"), asm.Return(), ) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ License: "MIT", Type: ebpf.XDP, Instructions: insns, }) if err != nil { events.Close() return nil, nil, err } return prog, events, nil } func mustOutputSamplesProg(tb testing.TB, flags int32, sampleSizes ...int) (*ebpf.Program, *ebpf.Map) { tb.Helper() prog, events, err := outputSamplesProg(flags, sampleSizes...) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() events.Close() }) return prog, events } func TestReaderBlocking(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(t, 0, 5) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if errno := syscall.Errno(-int32(ret)); errno != 0 { t.Fatal("Expected 0 as return value, got", errno) } rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() if _, err := rd.Read(); err != nil { t.Fatal("Can't read first sample:", err) } errs := make(chan error, 1) go func() { _, err := rd.Read() errs <- err }() select { case err := <-errs: t.Fatal("Read returns error instead of blocking:", err) case <-time.After(100 * time.Millisecond): } // Close should interrupt blocking Read if err := rd.Close(); err != nil { t.Fatal(err) } select { case err := <-errs: if !errors.Is(err, ErrClosed) { t.Fatal("Expected os.ErrClosed from interrupted Read, got:", err) } case <-time.After(time.Second): t.Fatal("Close doesn't interrupt Read") } // And we should be able to call it multiple times if err := rd.Close(); err != nil { t.Fatal(err) } if _, err := rd.Read(); !errors.Is(err, ErrClosed) { t.Fatal("Second Read on a closed RingbufReader doesn't return ErrClosed") } } func TestReaderSetDeadline(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") _, events := mustOutputSamplesProg(t, 0, 5) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() rd.SetDeadline(time.Now().Add(-time.Second)) if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from first Read, got:", err) } if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from second Read, got:", err) } } func BenchmarkReader(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF ring buffer") readerBenchmarks := []struct { name string flags int32 }{ { name: "normal epoll with timeout -1", }, } for _, bm := range readerBenchmarks { b.Run(bm.name, func(b *testing.B) { prog, events := mustOutputSamplesProg(b, bm.flags, 80) rd, err := NewReader(events) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } _, err = rd.Read() if err != nil { b.Fatal("Can't read samples:", err) } } }) } } func BenchmarkReadInto(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(b, 0, 80) rd, err := NewReader(events) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ResetTimer() b.ReportAllocs() var rec Record for i := 0; i < b.N; i++ { ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } if err := rd.ReadInto(&rec); err != nil { b.Fatal("Can't read samples:", err) } } } golang-github-cilium-ebpf-0.11.0/ringbuf/ring.go000066400000000000000000000045461456504511400214520ustar00rootroot00000000000000package ringbuf import ( "fmt" "io" "os" "runtime" "sync/atomic" "unsafe" "github.com/cilium/ebpf/internal/unix" ) type ringbufEventRing struct { prod []byte cons []byte *ringReader } func newRingBufEventRing(mapFD, size int) (*ringbufEventRing, error) { cons, err := unix.Mmap(mapFD, 0, os.Getpagesize(), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) if err != nil { return nil, fmt.Errorf("can't mmap consumer page: %w", err) } prod, err := unix.Mmap(mapFD, (int64)(os.Getpagesize()), os.Getpagesize()+2*size, unix.PROT_READ, unix.MAP_SHARED) if err != nil { _ = unix.Munmap(cons) return nil, fmt.Errorf("can't mmap data pages: %w", err) } cons_pos := (*uint64)(unsafe.Pointer(&cons[0])) prod_pos := (*uint64)(unsafe.Pointer(&prod[0])) ring := &ringbufEventRing{ prod: prod, cons: cons, ringReader: newRingReader(cons_pos, prod_pos, prod[os.Getpagesize():]), } runtime.SetFinalizer(ring, (*ringbufEventRing).Close) return ring, nil } func (ring *ringbufEventRing) Close() { runtime.SetFinalizer(ring, nil) _ = unix.Munmap(ring.prod) _ = unix.Munmap(ring.cons) ring.prod = nil ring.cons = nil } type ringReader struct { // These point into mmap'ed memory and must be accessed atomically. prod_pos, cons_pos *uint64 cons uint64 mask uint64 ring []byte } func newRingReader(cons_ptr, prod_ptr *uint64, ring []byte) *ringReader { return &ringReader{ prod_pos: prod_ptr, cons_pos: cons_ptr, cons: atomic.LoadUint64(cons_ptr), // cap is always a power of two mask: uint64(cap(ring)/2 - 1), ring: ring, } } func (rr *ringReader) loadConsumer() { rr.cons = atomic.LoadUint64(rr.cons_pos) } func (rr *ringReader) storeConsumer() { atomic.StoreUint64(rr.cons_pos, rr.cons) } // clamp delta to 'end' if 'start+delta' is beyond 'end' func clamp(start, end, delta uint64) uint64 { if remainder := end - start; delta > remainder { return remainder } return delta } func (rr *ringReader) skipRead(skipBytes uint64) { rr.cons += clamp(rr.cons, atomic.LoadUint64(rr.prod_pos), skipBytes) } func (rr *ringReader) Read(p []byte) (int, error) { prod := atomic.LoadUint64(rr.prod_pos) n := clamp(rr.cons, prod, uint64(len(p))) start := rr.cons & rr.mask copy(p, rr.ring[start:start+n]) rr.cons += n if prod == rr.cons { return int(n), io.EOF } return int(n), nil } golang-github-cilium-ebpf-0.11.0/ringbuf/ring_test.go000066400000000000000000000024271456504511400225050ustar00rootroot00000000000000package ringbuf import ( "bytes" "io" "testing" ) func TestRingBufferReader(t *testing.T) { buf := make([]byte, 2) ring := makeRing(2, 0) n, err := ring.Read(buf) if err != io.EOF { t.Error("Expected io.EOF, got", err) } if n != 2 { t.Errorf("Expected to read 2 bytes, got %d", n) } if !bytes.Equal(buf, []byte{0, 1}) { t.Error("Expected [0, 1], got", buf) } n, err = ring.Read(buf) if err != io.EOF { t.Error("Expected io.EOF, got", err) } if n != 0 { t.Error("Expected to read 0 bytes, got", n) } buf = make([]byte, 4) ring = makeRing(4, 4) n, err = io.ReadFull(ring, buf) if err != nil { t.Error("Expected nil, got", err) } if n != 4 { t.Errorf("Expected to read 4 bytes, got %d", n) } if !bytes.Equal(buf, []byte{0, 1, 2, 3}) { t.Error("Expected [0, 1, 2, 3], got", buf) } n, err = ring.Read(buf) if err != io.EOF { t.Error("Expected io.EOF, got", err) } if n != 0 { t.Error("Expected to read 0 bytes, got", n) } } func makeRing(size, offset int) *ringReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } ring := make([]byte, 2*size) for i := range ring { ring[i] = byte(i) } consumer := uint64(offset) producer := uint64(len(ring)/2 + offset) return newRingReader(&consumer, &producer, ring) } golang-github-cilium-ebpf-0.11.0/rlimit/000077500000000000000000000000001456504511400200175ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/rlimit/rlimit.go000066400000000000000000000100161456504511400216440ustar00rootroot00000000000000// Package rlimit allows raising RLIMIT_MEMLOCK if necessary for the use of BPF. package rlimit import ( "errors" "fmt" "sync" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) var ( unsupportedMemcgAccounting = &internal.UnsupportedFeatureError{ MinimumVersion: internal.Version{5, 11, 0}, Name: "memcg-based accounting for BPF memory", } haveMemcgAccounting error rlimitMu sync.Mutex ) func init() { // We have to run this feature test at init, since it relies on changing // RLIMIT_MEMLOCK. Doing so is not safe in a concurrent program. Instead, // we rely on the initialization order guaranteed by the Go runtime to // execute the test in a safe environment: // // the invocation of init functions happens in a single goroutine, // sequentially, one package at a time. // // This is also the reason why RemoveMemlock is in its own package: // we only want to run the initializer if RemoveMemlock is called // from somewhere. haveMemcgAccounting = detectMemcgAccounting() } func detectMemcgAccounting() error { // Retrieve the original limit to prevent lowering Max, since // doing so is a permanent operation when running unprivileged. var oldLimit unix.Rlimit if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &oldLimit); err != nil { return fmt.Errorf("getting original memlock rlimit: %s", err) } // Drop the current limit to zero, maintaining the old Max value. // This is always permitted by the kernel for unprivileged users. // Retrieve a new copy of the old limit tuple to minimize the chances // of failing the restore operation below. zeroLimit := unix.Rlimit{Cur: 0, Max: oldLimit.Max} if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &zeroLimit, &oldLimit); err != nil { return fmt.Errorf("lowering memlock rlimit: %s", err) } attr := sys.MapCreateAttr{ MapType: 2, /* Array */ KeySize: 4, ValueSize: 4, MaxEntries: 1, } // Creating a map allocates shared (and locked) memory that counts against // the rlimit on pre-5.11 kernels, but against the memory cgroup budget on // kernels 5.11 and over. If this call succeeds with the process' memlock // rlimit set to 0, we can reasonably assume memcg accounting is supported. fd, mapErr := sys.MapCreate(&attr) // Restore old limits regardless of what happened. if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &oldLimit, nil); err != nil { return fmt.Errorf("restoring old memlock rlimit: %s", err) } // Map creation successful, memcg accounting supported. if mapErr == nil { fd.Close() return nil } // EPERM shows up when map creation would exceed the memory budget. if errors.Is(mapErr, unix.EPERM) { return unsupportedMemcgAccounting } // This shouldn't happen really. return fmt.Errorf("unexpected error detecting memory cgroup accounting: %s", mapErr) } // RemoveMemlock removes the limit on the amount of memory the current // process can lock into RAM, if necessary. // // This is not required to load eBPF resources on kernel versions 5.11+ // due to the introduction of cgroup-based memory accounting. On such kernels // the function is a no-op. // // Since the function may change global per-process limits it should be invoked // at program start up, in main() or init(). // // This function exists as a convenience and should only be used when // permanently raising RLIMIT_MEMLOCK to infinite is appropriate. Consider // invoking prlimit(2) directly with a more reasonable limit if desired. // // Requires CAP_SYS_RESOURCE on kernels < 5.11. func RemoveMemlock() error { if haveMemcgAccounting == nil { return nil } if !errors.Is(haveMemcgAccounting, unsupportedMemcgAccounting) { return haveMemcgAccounting } rlimitMu.Lock() defer rlimitMu.Unlock() // pid 0 affects the current process. Requires CAP_SYS_RESOURCE. newLimit := unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY} if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &newLimit, nil); err != nil { return fmt.Errorf("failed to set memlock rlimit: %w", err) } return nil } golang-github-cilium-ebpf-0.11.0/rlimit/rlimit_test.go000066400000000000000000000020141456504511400227020ustar00rootroot00000000000000package rlimit import ( "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestRemoveMemlock(t *testing.T) { var before unix.Rlimit qt.Assert(t, unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &before), qt.IsNil) err := RemoveMemlock() qt.Assert(t, err, qt.IsNil) var after unix.Rlimit qt.Assert(t, unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &after), qt.IsNil) // We can't use testutils here due to an import cycle. version, err := internal.KernelVersion() qt.Assert(t, err, qt.IsNil) if version.Less(unsupportedMemcgAccounting.MinimumVersion) { qt.Assert(t, after.Cur, qt.Equals, uint64(unix.RLIM_INFINITY), qt.Commentf("cur should be INFINITY")) qt.Assert(t, after.Max, qt.Equals, uint64(unix.RLIM_INFINITY), qt.Commentf("max should be INFINITY")) } else { qt.Assert(t, after.Cur, qt.Equals, before.Cur, qt.Commentf("cur should be unchanged")) qt.Assert(t, after.Max, qt.Equals, before.Max, qt.Commentf("max should be unchanged")) } } golang-github-cilium-ebpf-0.11.0/run-tests.sh000077500000000000000000000076401456504511400210310ustar00rootroot00000000000000#!/usr/bin/env bash # Test the current package under a different kernel. # Requires virtme and qemu to be installed. # Examples: # Run all tests on a 5.4 kernel # $ ./run-tests.sh 5.4 # Run a subset of tests: # $ ./run-tests.sh 5.4 ./link # Run using a local kernel image # $ ./run-tests.sh /path/to/bzImage set -euo pipefail script="$(realpath "$0")" readonly script # This script is a bit like a Matryoshka doll since it keeps re-executing itself # in various different contexts: # # 1. invoked by the user like run-tests.sh 5.4 # 2. invoked by go test like run-tests.sh --exec-vm # 3. invoked by init in the vm like run-tests.sh --exec-test # # This allows us to use all available CPU on the host machine to compile our # code, and then only use the VM to execute the test. This is because the VM # is usually slower at compiling than the host. if [[ "${1:-}" = "--exec-vm" ]]; then shift input="$1" shift # Use sudo if /dev/kvm isn't accessible by the current user. sudo="" if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then sudo="sudo" fi readonly sudo testdir="$(dirname "$1")" output="$(mktemp -d)" printf -v cmd "%q " "$@" if [[ "$(stat -c '%t:%T' -L /proc/$$/fd/0)" == "1:3" ]]; then # stdin is /dev/null, which doesn't play well with qemu. Use a fifo as a # blocking substitute. mkfifo "${output}/fake-stdin" # Open for reading and writing to avoid blocking. exec 0<> "${output}/fake-stdin" rm "${output}/fake-stdin" fi for ((i = 0; i < 3; i++)); do if ! $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \ --rwdir="${testdir}=${testdir}" \ --rodir=/run/input="${input}" \ --rwdir=/run/output="${output}" \ --script-sh "PATH=\"$PATH\" CI_MAX_KERNEL_VERSION="${CI_MAX_KERNEL_VERSION:-}" \"$script\" --exec-test $cmd" \ --kopt possible_cpus=2; then # need at least two CPUs for some tests exit 23 fi if [[ -e "${output}/status" ]]; then break fi if [[ -v CI ]]; then echo "Retrying test run due to qemu crash" continue fi exit 42 done rc=$(<"${output}/status") $sudo rm -r "$output" exit $rc elif [[ "${1:-}" = "--exec-test" ]]; then shift mount -t bpf bpf /sys/fs/bpf mount -t tracefs tracefs /sys/kernel/debug/tracing if [[ -d "/run/input/bpf" ]]; then export KERNEL_SELFTESTS="/run/input/bpf" fi if [[ -f "/run/input/bpf/bpf_testmod/bpf_testmod.ko" ]]; then insmod "/run/input/bpf/bpf_testmod/bpf_testmod.ko" fi dmesg --clear rc=0 "$@" || rc=$? dmesg echo $rc > "/run/output/status" exit $rc # this return code is "swallowed" by qemu fi if [[ -z "${1:-}" ]]; then echo "Expecting kernel version or path as first argument" exit 1 fi readonly input="$(mktemp -d)" readonly tmp_dir="${TMPDIR:-/tmp}" fetch() { echo Fetching "${1}" pushd "${tmp_dir}" > /dev/null curl --no-progress-meter -L -O --fail --etag-compare "${1}.etag" --etag-save "${1}.etag" "https://github.com/cilium/ci-kernels/raw/${BRANCH:-master}/${1}" local ret=$? popd > /dev/null return $ret } if [[ -f "${1}" ]]; then readonly kernel="${1}" cp "${1}" "${input}/bzImage" else # LINUX_VERSION_CODE test compares this to discovered value. export KERNEL_VERSION="${1}" readonly kernel="linux-${1}.bz" readonly selftests="linux-${1}-selftests-bpf.tgz" fetch "${kernel}" cp "${tmp_dir}/${kernel}" "${input}/bzImage" if fetch "${selftests}"; then echo "Decompressing selftests" mkdir "${input}/bpf" tar --strip-components=4 -xf "${tmp_dir}/${selftests}" -C "${input}/bpf" else echo "No selftests found, disabling" fi fi shift args=(-short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...) if (( $# > 0 )); then args=("$@") fi export GOFLAGS=-mod=readonly export CGO_ENABLED=0 echo Testing on "${kernel}" go test -exec "$script --exec-vm $input" "${args[@]}" echo "Test successful on ${kernel}" rm -r "${input}" golang-github-cilium-ebpf-0.11.0/syscalls.go000066400000000000000000000153301456504511400207050ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "os" "runtime" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) var ( // pre-allocating these here since they may // get called in hot code paths and cause // unnecessary memory allocations sysErrKeyNotExist = sys.Error(ErrKeyNotExist, unix.ENOENT) sysErrKeyExist = sys.Error(ErrKeyExist, unix.EEXIST) sysErrNotSupported = sys.Error(ErrNotSupported, sys.ENOTSUPP) ) // invalidBPFObjNameChar returns true if char may not appear in // a BPF object name. func invalidBPFObjNameChar(char rune) bool { dotAllowed := objNameAllowsDot() == nil switch { case char >= 'A' && char <= 'Z': return false case char >= 'a' && char <= 'z': return false case char >= '0' && char <= '9': return false case dotAllowed && char == '.': return false case char == '_': return false default: return true } } func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, error) { buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) if err := insns.Marshal(buf, internal.NativeEndian); err != nil { return nil, err } bytecode := buf.Bytes() return sys.ProgLoad(&sys.ProgLoadAttr{ ProgType: sys.ProgType(typ), License: sys.NewStringPointer(license), Insns: sys.NewSlicePointer(bytecode), InsnCnt: uint32(len(bytecode) / asm.InstructionSize), }) } var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error { _, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(ArrayOfMaps), KeySize: 4, ValueSize: 4, MaxEntries: 1, // Invalid file descriptor. InnerMapFd: ^uint32(0), }) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", "5.2", func() error { // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: unix.BPF_F_RDONLY_PROG, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", "5.5", func() error { // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: unix.BPF_F_MMAPABLE, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error { // This checks BPF_F_INNER_MAP, which appeared in 5.10. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: unix.BPF_F_INNER_MAP, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func() error { // This checks BPF_F_NO_PREALLOC, which appeared in 4.6. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Hash), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: unix.BPF_F_NO_PREALLOC, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) func wrapMapError(err error) error { if err == nil { return nil } if errors.Is(err, unix.ENOENT) { return sysErrKeyNotExist } if errors.Is(err, unix.EEXIST) { return sysErrKeyExist } if errors.Is(err, sys.ENOTSUPP) { return sysErrNotSupported } if errors.Is(err, unix.E2BIG) { return fmt.Errorf("key too big for map: %w", err) } return err } var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error { attr := sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: sys.NewObjName("feature_test"), } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", func() error { if err := haveObjName(); err != nil { return err } attr := sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: sys.NewObjName(".test"), } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error { var maxEntries uint32 = 2 attr := sys.MapCreateAttr{ MapType: sys.MapType(Hash), KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } defer fd.Close() keys := []uint32{1, 2} values := []uint32{3, 4} kp, _ := marshalPtr(keys, 8) vp, _ := marshalPtr(values, 8) err = sys.MapUpdateBatch(&sys.MapUpdateBatchAttr{ MapFd: fd.Uint(), Keys: kp, Values: vp, Count: maxEntries, }) if err != nil { return internal.ErrNotSupported } return nil }) var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5", func() error { insns := asm.Instructions{ asm.Mov.Reg(asm.R1, asm.R10), asm.Add.Imm(asm.R1, -8), asm.Mov.Imm(asm.R2, 8), asm.Mov.Imm(asm.R3, 0), asm.FnProbeReadKernel.Call(), asm.Return(), } fd, err := progLoad(insns, Kprobe, "GPL") if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func() error { insns := asm.Instructions{ asm.Call.Label("prog2").WithSymbol("prog1"), asm.Return(), asm.Mov.Imm(asm.R0, 0).WithSymbol("prog2"), asm.Return(), } fd, err := progLoad(insns, SocketFilter, "MIT") if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if err != nil { return err } _ = fd.Close() return nil }) var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func() error { prefix := internal.PlatformPrefix() if prefix == "" { return fmt.Errorf("unable to find the platform prefix for (%s)", runtime.GOARCH) } args := tracefs.ProbeArgs{ Type: tracefs.Kprobe, Symbol: prefix + "sys_bpf", Pid: -1, } var err error args.Group, err = tracefs.RandomGroup("ebpf_probe") if err != nil { return err } evt, err := tracefs.NewEvent(args) if errors.Is(err, os.ErrNotExist) { return internal.ErrNotSupported } if err != nil { return err } return evt.Close() }) golang-github-cilium-ebpf-0.11.0/syscalls_test.go000066400000000000000000000026031456504511400217430ustar00rootroot00000000000000package ebpf import ( "strings" "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestObjNameCharacters(t *testing.T) { for in, valid := range map[string]bool{ "test": true, "": true, "a-b": false, "yeah so": false, "dot.": objNameAllowsDot() == nil, "Capital": true, } { result := strings.IndexFunc(in, invalidBPFObjNameChar) == -1 if result != valid { t.Errorf("Name '%s' classified incorrectly", in) } } } func TestHaveBatchAPI(t *testing.T) { testutils.CheckFeatureTest(t, haveBatchAPI) } func TestHaveObjName(t *testing.T) { testutils.CheckFeatureTest(t, haveObjName) } func TestObjNameAllowsDot(t *testing.T) { testutils.CheckFeatureTest(t, objNameAllowsDot) } func TestHaveNestedMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveNestedMaps) } func TestHaveMapMutabilityModifiers(t *testing.T) { testutils.CheckFeatureTest(t, haveMapMutabilityModifiers) } func TestHaveMmapableMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveMmapableMaps) } func TestHaveInnerMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveInnerMaps) } func TestHaveProbeReadKernel(t *testing.T) { testutils.CheckFeatureTest(t, haveProbeReadKernel) } func TestHaveBPFToBPFCalls(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFToBPFCalls) } func TestHaveSyscallWrapper(t *testing.T) { testutils.CheckFeatureTest(t, haveSyscallWrapper) } golang-github-cilium-ebpf-0.11.0/testdata/000077500000000000000000000000001456504511400203305ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/testdata/auxv.bin000066400000000000000000000005001456504511400220000ustar00rootroot00000000000000d@ U8 PZz U e e ggYX{7_{7iX{7!P~7golang-github-cilium-ebpf-0.11.0/testdata/auxv_no_vdso.bin000066400000000000000000000004601456504511400235340ustar00rootroot00000000000000d@ U8 PZz U e e ggYX{7_{7iX{7golang-github-cilium-ebpf-0.11.0/testdata/btf_map_init-eb.elf000066400000000000000000000053301456504511400240400ustar00rootroot00000000000000ELFX@@* 0   "     /4@8>J Q  />@48a  />@48Jk z   0 0int__ARRAY_SIZE_TYPE__uint32_tunsigned inttypekeyvaluemax_entriesvaluesprog_array_initinner_mapouter_map_inittail_1socket/tail./testdata/btf_map_init.c return 42;ctxtail_mainsocket/main tail_call(ctx, &prog_array_init, 1); return 0;.maps $$Dh x G00=0 $P0(x,<Phx .text.rel.BTF.extprog_array_initouter_map_init.rel.mapsinner_maptail_main.relsocket/mainsocket/tail.llvm_addrsig.strtab.symtab.rel.BTFtail_1{@a@UP0Q @ 73 @  @80   @hP moLHgolang-github-cilium-ebpf-0.11.0/testdata/btf_map_init-el.elf000066400000000000000000000053301456504511400240520ustar00rootroot00000000000000ELFX@@*   "      /4@8>J Q  />@48a  />@48Jk z   0 0int__ARRAY_SIZE_TYPE__uint32_tunsigned inttypekeyvaluemax_entriesvaluesprog_array_initinner_mapouter_map_inittail_1socket/tail./testdata/btf_map_init.c return 42;ctxtail_mainsocket/main tail_call(ctx, &prog_array_init, 1); return 0;.maps $$Dh x G00=0 $P0(x,<Phx .text.rel.BTF.extprog_array_initouter_map_init.rel.mapsinner_maptail_main.relsocket/mainsocket/tail.llvm_addrsig.strtab.symtab.rel.BTFtail_1{@a@UP0Q @ 73 @  @80   @hP mLoHgolang-github-cilium-ebpf-0.11.0/testdata/btf_map_init.c000066400000000000000000000025521456504511400231330ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" #if __clang_major__ >= 9 int __section("socket/tail") tail_1() { return 42; } // Tail call map (program array) initialized with program pointers. struct { __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __type(key, uint32_t); __type(value, uint32_t); __uint(max_entries, 2); __array(values, int()); } prog_array_init __section(".maps") = { .values = { // Skip index 0 to exercise empty array slots. [1] = &tail_1, }, }; int __section("socket/main") tail_main(void *ctx) { // If prog_array_init is correctly populated, the tail call // will succeed and the program will continue in tail_1 and // not return here. tail_call(ctx, &prog_array_init, 1); return 0; } // Inner map with a single possible entry. struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, uint32_t); __type(value, uint32_t); } inner_map __section(".maps"); // Outer map carrying a reference to the inner map. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(max_entries, 2); __type(key, uint32_t); __type(value, uint32_t); __array(values, typeof(inner_map)); } outer_map_init __section(".maps") = { .values = { // Skip index 0 to exercise empty array slots. [1] = &inner_map, }, }; #else #error This file has to be compiled with clang >= 9 #endif golang-github-cilium-ebpf-0.11.0/testdata/common.h000066400000000000000000000031721456504511400217740ustar00rootroot00000000000000#pragma once typedef _Bool bool; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long uint64_t; enum libbpf_tristate { TRI_NO = 0, TRI_YES = 1, TRI_MODULE = 2, }; #define __section(NAME) __attribute__((section(NAME), used)) #define __uint(name, val) int(*name)[val] #define __type(name, val) typeof(val) *name #define __array(name, val) typeof(val) *name[] #define __kconfig __attribute__((section(".kconfig"))) #define __ksym __attribute__((section(".ksyms"))) #define BPF_MAP_TYPE_HASH (1) #define BPF_MAP_TYPE_ARRAY (2) #define BPF_MAP_TYPE_PROG_ARRAY (3) #define BPF_MAP_TYPE_PERF_EVENT_ARRAY (4) #define BPF_MAP_TYPE_ARRAY_OF_MAPS (12) #define BPF_MAP_TYPE_HASH_OF_MAPS (13) #define BPF_F_NO_PREALLOC (1U << 0) #define BPF_F_CURRENT_CPU (0xffffffffULL) /* From tools/lib/bpf/libbpf.h */ struct bpf_map_def { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; unsigned int map_flags; }; static void *(*map_lookup_elem)(const void *map, const void *key) = (void *)1; static long (*map_update_elem)(const void *map, const void *key, const void *value, uint64_t flags) = (void *)2; static long (*trace_printk)(const char *fmt, uint32_t fmt_size, ...) = (void *)6; static long (*tail_call)(void *ctx, void *prog_array_map, uint32_t index) = (void *)12; static int (*perf_event_output)(const void *ctx, const void *map, uint64_t index, const void *data, uint64_t size) = (void *)25; static uint32_t (*get_smp_processor_id)(void) = (void *)8; static long (*for_each_map_elem)(const void *map, void *callback_fn, void *callback_ctx, uint64_t flags) = (void *)164; golang-github-cilium-ebpf-0.11.0/testdata/docker/000077500000000000000000000000001456504511400215775ustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/testdata/docker/Dockerfile000066400000000000000000000014431456504511400235730ustar00rootroot00000000000000# This Dockerfile generates a build environment for generating ELFs # of testdata programs. Run `make build` in this directory to build it. FROM golang:buster COPY llvm-snapshot.gpg.key . RUN apt-get update && \ apt-get -y --no-install-recommends install ca-certificates gnupg && \ apt-key add llvm-snapshot.gpg.key && \ rm llvm-snapshot.gpg.key && \ apt-get remove -y gnupg && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* COPY llvm.list /etc/apt/sources.list.d RUN apt-get update && \ apt-get -y --no-install-recommends install \ make git \ clang-format \ clang-7 llvm-7 \ clang-9 llvm-9 \ clang-14 llvm-14 && \ rm -rf /var/lib/apt/lists/* RUN GOBIN=/usr/local/bin go install golang.org/x/tools/cmd/stringer@latest && rm -rf /go/pkg golang-github-cilium-ebpf-0.11.0/testdata/docker/IMAGE000066400000000000000000000000341456504511400223410ustar00rootroot00000000000000ghcr.io/cilium/ebpf-builder golang-github-cilium-ebpf-0.11.0/testdata/docker/Makefile000066400000000000000000000006361456504511400232440ustar00rootroot00000000000000# Makefile to build and push the `cilium/ebpf` llvm builder Docker image. CONTAINER_ENGINE ?= docker IMAGE := $(shell cat IMAGE) EPOCH := $(shell date +'%s') ifndef IMAGE $(error IMAGE file not present in Makefile directory) endif .PHONY: build push build: ${CONTAINER_ENGINE} build --no-cache . -t "$(IMAGE):$(EPOCH)" echo $(EPOCH) > VERSION push: ${CONTAINER_ENGINE} push "$(IMAGE):$(shell cat VERSION)" golang-github-cilium-ebpf-0.11.0/testdata/docker/README.md000066400000000000000000000022271456504511400230610ustar00rootroot00000000000000# `cilium/ebpf` LLVM Builder Image This is a simple Docker image to provide reproducible eBPF ELF builds across contributors' workstations. This standardizes on a single environment used to regenerate e.g. testdata ELFs and does not depend on the toolchain installed on the host machine. Additionally, it reduces drift in the bytecode committed to the repository over time as the same exact clang + llc version is used throughout the development lifecycle. Only when upgrading or rebuilding the Docker image would changes in .elf files be expected (assuming the .c files are untouched). ## Building Building the image requires Docker. Run the build with: `make build` This updates the `VERSION` file. Commit it and submit a PR upstream. ### Regeneration Testdata on non-x86 platforms Before running `make`, ensure [Docker buildx](https://docs.docker.com/buildx/working-with-buildx/) is enabled. Additionally `QEMU user` and `binfmt` should be installed. On a Debian based distribution the command to add them is `apt install qemu-user-static binfmt-support`. ## Pushing After building, push the image to the Docker registry specified in `IMAGE` with: `make push` golang-github-cilium-ebpf-0.11.0/testdata/docker/VERSION000066400000000000000000000000131456504511400226410ustar00rootroot000000000000001666886595 golang-github-cilium-ebpf-0.11.0/testdata/docker/llvm-snapshot.gpg.key000066400000000000000000000061111456504511400256730ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2 B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1 G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/ ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq 7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz dHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE FgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC 9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR udC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX wpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn l0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv gMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W R0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg hB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx K+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya KJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B MWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7 BxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g zOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc bvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC DshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw F1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta RES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/ 21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV ymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+ M9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa xenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ d9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/ fN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X OEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB pbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML PdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL wvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd oVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l tVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG 5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP LSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov 1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3 krCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN bXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw== =j+4q -----END PGP PUBLIC KEY BLOCK----- golang-github-cilium-ebpf-0.11.0/testdata/docker/llvm.list000066400000000000000000000010351456504511400234450ustar00rootroot00000000000000# Taken from https://apt.llvm.org. deb http://apt.llvm.org/buster/ llvm-toolchain-buster main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster main deb http://apt.llvm.org/buster/ llvm-toolchain-buster-7 main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-7 main deb http://apt.llvm.org/buster/ llvm-toolchain-buster-9 main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-9 main deb http://apt.llvm.org/buster/ llvm-toolchain-buster-14 main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-14 main golang-github-cilium-ebpf-0.11.0/testdata/freplace-eb.elf000066400000000000000000000044001456504511400231630ustar00rootroot00000000000000ELF@@ ca MIT;   w@      $ )3intsubprog.text./testdata/freplace.c__attribute__((noinline)) int subprog() { volatile int ret = 0; return ret;bpf_argsargsuint64_tunsigned long__ARRAY_SIZE_TYPE__ctxsched_process_execraw_tracepoint/sched_process_exec return subprog();replacementfreplace/subprog return 0;char__licenselicense 44|    ),S0j4D DT) \? (,<L`p.text.rel.BTF.extreplacementfreplace/subprog.llvm_addrsig__license.relraw_tracepoint/sched_process_exec.strtab.symtab.rel.BTFo3@ M`I @  pAk @   @ 1oL0wgolang-github-cilium-ebpf-0.11.0/testdata/freplace-el.elf000066400000000000000000000044001456504511400231750ustar00rootroot00000000000000ELF@@ caMIT;   w@     $ )3intsubprog.text./testdata/freplace.c__attribute__((noinline)) int subprog() { volatile int ret = 0; return ret;bpf_argsargsuint64_tunsigned long__ARRAY_SIZE_TYPE__ctxsched_process_execraw_tracepoint/sched_process_exec return subprog();replacementfreplace/subprog return 0;char__licenselicense 44|    ),S0j4 DDT) \? (,<L`p.text.rel.BTF.extreplacementfreplace/subprog.llvm_addrsig__license.relraw_tracepoint/sched_process_exec.strtab.symtab.rel.BTFo3@ M`I @  pAk @   @ 1Lo0wgolang-github-cilium-ebpf-0.11.0/testdata/freplace.c000066400000000000000000000006331456504511400222570ustar00rootroot00000000000000// /* This file excercises freplace. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_args { uint64_t args[0]; }; __attribute__((noinline)) int subprog() { volatile int ret = 0; return ret; } __section("raw_tracepoint/sched_process_exec") int sched_process_exec(struct bpf_args *ctx) { return subprog(); } __section("freplace/subprog") int replacement() { return 0; } golang-github-cilium-ebpf-0.11.0/testdata/fwd_decl-eb.elf000066400000000000000000000022401456504511400231510ustar00rootroot00000000000000ELF @@ ((:   intcall_fwdsocket./testdata/fwd_decl.c return fwd(); ,@+$ +$-2 ,@P .text.rel.BTF.ext.relsocket.llvm_addrsigcall_fwd.strtab.symtab.BTF6K@@ @ FPz ` @0 oL>0`golang-github-cilium-ebpf-0.11.0/testdata/fwd_decl-el.elf000066400000000000000000000022401456504511400231630ustar00rootroot00000000000000ELF @@ ((:   intcall_fwdsocket./testdata/fwd_decl.c return fwd(); ,@+ $+$-2 ,@P .text.rel.BTF.ext.relsocket.llvm_addrsigcall_fwd.strtab.symtab.BTF6K@@ @ FPz ` @0 Lo>0`golang-github-cilium-ebpf-0.11.0/testdata/fwd_decl.c000066400000000000000000000003261456504511400222440ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" // Forward function declaration, never implemented. int fwd(); __section("socket") int call_fwd() { return fwd(); } golang-github-cilium-ebpf-0.11.0/testdata/invalid-kfunc-eb.elf000066400000000000000000000032501456504511400241360ustar00rootroot00000000000000ELF@@ Dual MIT/GPL    _    intcall_kfunctc./testdata/invalid-kfunc.c bpf_kfunc_call_test_mem_len_pass1(); return 1;bpf_kfunc_call_test_mem_len_pass1char__ARRAY_SIZE_TYPE____license.ksymslicense ,@.$T(3W"  ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfunc.strtab.symtab.rel.BTFbpf_kfunc_call_test_mem_len_pass1>*y@0@, @ $X RhN @  ` @0 oL(FPxgolang-github-cilium-ebpf-0.11.0/testdata/invalid-kfunc-el.elf000066400000000000000000000032501456504511400241500ustar00rootroot00000000000000ELF@@ Dual MIT/GPL    _    intcall_kfunctc./testdata/invalid-kfunc.c bpf_kfunc_call_test_mem_len_pass1(); return 1;bpf_kfunc_call_test_mem_len_pass1char__ARRAY_SIZE_TYPE____license.ksymslicense ,@.$T(3W" ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfunc.strtab.symtab.rel.BTFbpf_kfunc_call_test_mem_len_pass1>*y@0@, @ $X RhN @  ` @0 Lo(FPxgolang-github-cilium-ebpf-0.11.0/testdata/invalid-kfunc.c000066400000000000000000000004271456504511400232310ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; // This function declaration is incorrect on purpose. extern void bpf_kfunc_call_test_mem_len_pass1(void) __ksym; __section("tc") int call_kfunc() { bpf_kfunc_call_test_mem_len_pass1(); return 1; } golang-github-cilium-ebpf-0.11.0/testdata/invalid_btf_map_init-eb.elf000066400000000000000000000021401456504511400255420ustar00rootroot00000000000000ELF`@@p  "  / 8@ FK@OUa j int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypekeyvaluemax_entrieshash_map.maps  .text.mapshash_map.llvm_addrsig.strtab.symtab.rel.BTF$!=@@ 8`4 @oL ,0golang-github-cilium-ebpf-0.11.0/testdata/invalid_btf_map_init-el.elf000066400000000000000000000021401456504511400255540ustar00rootroot00000000000000ELF`@@p  "  / 8@ FK@OUa j int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypekeyvaluemax_entrieshash_map.maps  .text.mapshash_map.llvm_addrsig.strtab.symtab.rel.BTF$!=@@ 8`4 @Lo ,0golang-github-cilium-ebpf-0.11.0/testdata/invalid_btf_map_init.c000066400000000000000000000006431456504511400246400ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" #if __clang_major__ >= 9 struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); } hash_map __section(".maps") = { /* This forces a non-zero byte into the .maps section. */ .key = (void *)1, }; #else #error This file has to be compiled with clang >= 9 #endif golang-github-cilium-ebpf-0.11.0/testdata/invalid_map-eb.elf000066400000000000000000000024301456504511400236660ustar00rootroot00000000000000ELF@@ MIT $(.:? H@S`_i v char__ARRAY_SIZE_TYPE____licensedefdummybpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intuint32_tinvalid_maplicensemaps&   .textmapsinvalid_map.llvm_addrsig__license.strtab.symtab.rel.BTF0I@(@DD\@ @h oL8 Hgolang-github-cilium-ebpf-0.11.0/testdata/invalid_map-el.elf000066400000000000000000000024301456504511400237000ustar00rootroot00000000000000ELF@@ MIT $(.:? H@S`_i v char__ARRAY_SIZE_TYPE____licensedefdummybpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intuint32_tinvalid_maplicensemaps&   .textmapsinvalid_map.llvm_addrsig__license.strtab.symtab.rel.BTF0I@(@DD\@ @h Lo8 Hgolang-github-cilium-ebpf-0.11.0/testdata/invalid_map.c000066400000000000000000000005551456504511400227640ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" char __license[] __section("license") = "MIT"; struct { struct bpf_map_def def; uint32_t dummy; } invalid_map __section("maps") = { .def = { .type = BPF_MAP_TYPE_HASH, .key_size = 4, .value_size = 2, .max_entries = 1, }, .dummy = 1, }; golang-github-cilium-ebpf-0.11.0/testdata/invalid_map_static-eb.elf000066400000000000000000000040001456504511400252300ustar00rootroot00000000000000ELF@@ c* U    @` intxdp_progxdp./testdata/invalid_map_static.c__section("xdp") int xdp_prog() { uint32_t key = 0; void *p = map_lookup_elem(&hash_map, &key); return !!p;bpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intdummyhash_mapmaps l2XT\  g`Hd Xd'`X0` ,@P`pdummy.text.rel.BTF.extmaps.relxdphash_mapxdp_prog.llvm_addrsig.strtab.symtab.rel.BTFLBB0_2Gg@#@` @ ([W @  @ p 9oLOHgolang-github-cilium-ebpf-0.11.0/testdata/invalid_map_static-el.elf000066400000000000000000000040001456504511400252420ustar00rootroot00000000000000ELF@@ cU    @` intxdp_progxdp./testdata/invalid_map_static.c__section("xdp") int xdp_prog() { uint32_t key = 0; void *p = map_lookup_elem(&hash_map, &key); return !!p;bpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intdummyhash_mapmaps l2XT \ g`H dXd'`X0` ,@P`pdummy.text.rel.BTF.extmaps.relxdphash_mapxdp_prog.llvm_addrsig.strtab.symtab.rel.BTFLBB0_2Gg@#@` @ ([W @  @ p 9LoOHgolang-github-cilium-ebpf-0.11.0/testdata/invalid_map_static.c000066400000000000000000000012411456504511400243240ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" struct bpf_map_def dummy __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = 0, }; /* The static qualifier leads to clang not emitting a symbol. */ static struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = 0, }; __section("xdp") int xdp_prog() { uint32_t key = 0; void *p = map_lookup_elem(&hash_map, &key); return !!p; } golang-github-cilium-ebpf-0.11.0/testdata/iproute2_map_compat-eb.elf000066400000000000000000000020401456504511400253510ustar00rootroot00000000000000ELF @@n $  @&`/58@IS `i$bpf_elf_maptypesize_keysize_valuemax_elemflagsidpinninginner_idinner_idxunsigned inthash_mapmaps $.textmapshash_map.llvm_addrsig.strtab.symtab.rel.BTF#<@@$7d63 @oL+0golang-github-cilium-ebpf-0.11.0/testdata/iproute2_map_compat-el.elf000066400000000000000000000020401456504511400253630ustar00rootroot00000000000000ELF @@n $  @&`/58@IS `i$bpf_elf_maptypesize_keysize_valuemax_elemflagsidpinninginner_idinner_idxunsigned inthash_mapmaps $.textmapshash_map.llvm_addrsig.strtab.symtab.rel.BTF#<@@$7d63 @Lo+0golang-github-cilium-ebpf-0.11.0/testdata/iproute2_map_compat.c000066400000000000000000000014131456504511400244440ustar00rootroot00000000000000/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" #define PIN_GLOBAL_NS 2 // bpf_elf_map is a custom BPF map definition used by iproute2. // It contains the id, pinning, inner_id and inner_idx fields // in addition to the ones in struct bpf_map_def which is commonly // used in the kernel and libbpf. struct bpf_elf_map { unsigned int type; unsigned int size_key; unsigned int size_value; unsigned int max_elem; unsigned int flags; unsigned int id; unsigned int pinning; unsigned int inner_id; unsigned int inner_idx; }; struct bpf_elf_map hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .size_key = sizeof(uint32_t), .size_value = sizeof(uint64_t), .max_elem = 2, .pinning = PIN_GLOBAL_NS, }; golang-github-cilium-ebpf-0.11.0/testdata/kconfig-eb.elf000066400000000000000000000037601456504511400230320ustar00rootroot00000000000000ELF@@ aaMIT    N     intkernel_versionsocket./testdata/kconfig.c return LINUX_KERNEL_VERSION;syscall_wrapper return LINUX_HAS_SYSCALL_WRAPPER;char__ARRAY_SIZE_TYPE____licenseLINUX_KERNEL_VERSIONLINUX_HAS_SYSCALL_WRAPPER.kconfiglicense Lh 0$ 0$ ^4 0^4/  fL ,4HXhx .text.rel.BTF.ext.relsocketsyscall_wrapperkernel_version.llvm_addrsig__license.strtab.symtabLINUX_HAS_SYSCALL_WRAPPERLINUX_KERNEL_VERSION.rel.BTFVK@@@ @ N @0  d @` >oLH^golang-github-cilium-ebpf-0.11.0/testdata/kconfig-el.elf000066400000000000000000000037601456504511400230440ustar00rootroot00000000000000ELF@@ aaMIT    N     intkernel_versionsocket./testdata/kconfig.c return LINUX_KERNEL_VERSION;syscall_wrapper return LINUX_HAS_SYSCALL_WRAPPER;char__ARRAY_SIZE_TYPE____licenseLINUX_KERNEL_VERSIONLINUX_HAS_SYSCALL_WRAPPER.kconfiglicense Lh 0 $0$ ^ 40^4/  fL ,4HXhx .text.rel.BTF.ext.relsocketsyscall_wrapperkernel_version.llvm_addrsig__license.strtab.symtabLINUX_HAS_SYSCALL_WRAPPERLINUX_KERNEL_VERSION.rel.BTFVK@@@ @ N @0  d @` >LoH^golang-github-cilium-ebpf-0.11.0/testdata/kconfig.c000066400000000000000000000004761456504511400221230ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "MIT"; extern int LINUX_KERNEL_VERSION __kconfig; extern int LINUX_HAS_SYSCALL_WRAPPER __kconfig; __section("socket") int kernel_version() { return LINUX_KERNEL_VERSION; } __section("socket") int syscall_wrapper() { return LINUX_HAS_SYSCALL_WRAPPER; } golang-github-cilium-ebpf-0.11.0/testdata/kconfig_config-eb.elf000066400000000000000000000046001456504511400243510ustar00rootroot00000000000000ELF@@@ ag  {c* :0@GPL-2.0   "  / 8@ FK@W[ a  k  int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypemax_entrieskeyvaluearray_mapkconfigsocket./testdata/kconfig_config.c val = CONFIG_HZ; i = 0; bpf_map_update_elem(&array_map, &i, &val, 0); return 0;char__licenseCONFIG_HZ.kconfig.mapslicense lsszT(zT8zPHzT`zXz`=_% E`,@P`p  .text.rel.BTF.ext.relsocket.mapsarray_map.llvm_addrsigkconfig__license.strtab.symtabCONFIG_HZ.rel.BTFOr@@ @ G mi @(0   @Xp /oLWxgolang-github-cilium-ebpf-0.11.0/testdata/kconfig_config-el.elf000066400000000000000000000046001456504511400243630ustar00rootroot00000000000000ELF@@@ ag  {cGPL-2.0   "  / 8@ FK@W[ a  k  int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypemax_entrieskeyvaluearray_mapkconfigsocket./testdata/kconfig_config.c val = CONFIG_HZ; i = 0; bpf_map_update_elem(&array_map, &i, &val, 0); return 0;char__licenseCONFIG_HZ.kconfig.mapslicense lsszT(zT8zPHzT`zXz`=_% E`,@P`p  .text.rel.BTF.ext.relsocket.mapsarray_map.llvm_addrsigkconfig__license.strtab.symtabCONFIG_HZ.rel.BTFOr@@ @ G mi @(0   @Xp /LoWxgolang-github-cilium-ebpf-0.11.0/testdata/kconfig_config.c000066400000000000000000000010141456504511400234350ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "GPL-2.0"; extern int CONFIG_HZ __kconfig; struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, uint32_t); __type(value, uint64_t); } array_map __section(".maps"); static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, uint64_t flags) = (void *)2; __section("socket") int kconfig() { uint32_t i; uint64_t val; i = 0; val = CONFIG_HZ; bpf_map_update_elem(&array_map, &i, &val, 0); return 0; } golang-github-cilium-ebpf-0.11.0/testdata/kfunc-eb.elf000066400000000000000000000033301456504511400225110ustar00rootroot00000000000000ELF@@  Dual MIT/GPL    c    intcall_kfunctc./testdata/kfunc.c bpf_kfunc_call_test_mem_len_pass1((void *)0, 0); return 1;bpf_kfunc_call_test_mem_len_pass1char__ARRAY_SIZE_TYPE____license.ksymslicense ,@& X$3(W"  ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfunc.strtab.symtab.rel.BTFbpf_kfunc_call_test_mem_len_pass1>Zy@0@(, @ $h RxN @  ` @(0 oLXFxgolang-github-cilium-ebpf-0.11.0/testdata/kfunc-el.elf000066400000000000000000000033301456504511400225230ustar00rootroot00000000000000ELF@@ Dual MIT/GPL    c    intcall_kfunctc./testdata/kfunc.c bpf_kfunc_call_test_mem_len_pass1((void *)0, 0); return 1;bpf_kfunc_call_test_mem_len_pass1char__ARRAY_SIZE_TYPE____license.ksymslicense ,@& X$3(W"  ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfunc.strtab.symtab.rel.BTFbpf_kfunc_call_test_mem_len_pass1>Zy@0@(, @ $h RxN @  ` @(0 LoXFxgolang-github-cilium-ebpf-0.11.0/testdata/kfunc-kmod-eb.elf000066400000000000000000000032401456504511400234410ustar00rootroot00000000000000ELF@@ Dual MIT/GPL    V q v  intcall_kfunctc./testdata/kfunc-kmod.c bpf_testmod_test_mod_kfunc(0); return 1;bpf_testmod_test_mod_kfuncchar__ARRAY_SIZE_TYPE____license.ksymslicense ,@+ K$3 >"  ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfuncbpf_testmod_test_mod_kfunc.strtab.symtab.rel.BTFY*r@0@ , @ $` mp{i @  ` @0 oL(aPxgolang-github-cilium-ebpf-0.11.0/testdata/kfunc-kmod-el.elf000066400000000000000000000032401456504511400234530ustar00rootroot00000000000000ELF@@ Dual MIT/GPL    V q v  intcall_kfunctc./testdata/kfunc-kmod.c bpf_testmod_test_mod_kfunc(0); return 1;bpf_testmod_test_mod_kfuncchar__ARRAY_SIZE_TYPE____license.ksymslicense ,@+ K$3 >"  ,@P .text.rel.BTF.ext.llvm_addrsig__license.reltccall_kfuncbpf_testmod_test_mod_kfunc.strtab.symtab.rel.BTFY*r@0@ , @ $` mp{i @  ` @0 Lo(aPxgolang-github-cilium-ebpf-0.11.0/testdata/kfunc-kmod.c000066400000000000000000000003231456504511400225300ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; extern void bpf_testmod_test_mod_kfunc(int) __ksym; __section("tc") int call_kfunc() { bpf_testmod_test_mod_kfunc(0); return 1; } golang-github-cilium-ebpf-0.11.0/testdata/kfunc.c000066400000000000000000000003731456504511400216050ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; extern void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len) __ksym; __section("tc") int call_kfunc() { bpf_kfunc_call_test_mem_len_pass1((void *)0, 0); return 1; } golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-14-eb.elf000066400000000000000000000243101456504511400241560ustar00rootroot00000000000000ELF!@@ap   pa`aaa ]a ]a ]a ca* a* a*U g !yMIT \\  "  / 8@(FK@OUak  Ft @}U(FK@OU  FK@OUFt @U  FU@KOFt @U!"%F U$@&  ( t *  , = .  0 ~ 2  4  6  8':,;6Ft }@U`aB= @ T?Y@^@ E DcDh? J mIq?}IBC ( (#'AFHKLMG<>int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypekeyvaluemax_entriesmap_flagshash_mapkey_sizevalue_sizehash_map2pinningbtf_pininner_map_tvaluesbtf_outer_mapbtf_outer_map_anonperf_event_arrayargstatic_fnstatic./testdata/loader.cstatic int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1;global_fn2.textint __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2;global_fn3otherint __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1;global_fnint __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg);xdp_progxdp map_lookup_elem(&hash_map, (void *)&key1); map_lookup_elem(&hash_map2, (void *)&key2); map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2;no_relocationsocket return 0;asm_relocationsocket/2 asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const;data_sectionssocket/3__section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) if (neg != -2) if (static_uneg != (unsigned int)-3) if (static_neg != -4)}anon_constsocket/4__section("socket/4") int anon_const() { volatile int ctx = 0; if (ctx == values[i]) { return values[i];char__licensebpf_map_defarray_of_hash_mapkey3key1key2arg2unegnegstatic_unegstatic_neg.bss.data.maps.rodata.rodata.testlicensemaps T)+/-13579|c c  G  0HP,`*h, , (P!xN N N N N +N )N  8<  `6d06d@UphUpxe|e| (0HPX   N  y XU n`(( i <S   )  }H(op  8 X (8P`x  !#@$x`d|()*+# $$<T&l',<DTdt    0@P`p 0@P`x          ( 8 H `p#%)*,/01(&' -.2345perf_event_array.rel.text.rel.BTF.extanon_const.rodata.testsocket.bss.mapsdata_sectionsother.relxdpbtf_outer_maparray_of_hash_mapbtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__licensestatic.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5LBB8_4.relsocket/4key3global_fn3LBB8_3.relsocket/3key2hash_map2global_fn2arg2.rodata.cst32.relsocket/2key1@x @0akg @A @  @@ `\ @ hNl.MHH6H4LP  Ip /E @  @oL& golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-14-el.elf000066400000000000000000000243101456504511400241700ustar00rootroot00000000000000ELF!@@axaaqaq`aa]!a]!a]!a!caaaUgy MIT \\  "  / 8@(FK@OUak  Ft @}U(FK@OU  FK@OUFt @U  FU@KOFt @U!"%F U$@&  ( t *  , = .  0 ~ 2  4  6  8':,;6Ft }@U`aB= @ T?Y@^@ E DcDh? J mIq?}IBC ( (#'AFHKLMG<>int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tunsigned longtypekeyvaluemax_entriesmap_flagshash_mapkey_sizevalue_sizehash_map2pinningbtf_pininner_map_tvaluesbtf_outer_mapbtf_outer_map_anonperf_event_arrayargstatic_fnstatic./testdata/loader.cstatic int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1;global_fn2.textint __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2;global_fn3otherint __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1;global_fnint __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg);xdp_progxdp map_lookup_elem(&hash_map, (void *)&key1); map_lookup_elem(&hash_map2, (void *)&key2); map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2;no_relocationsocket return 0;asm_relocationsocket/2 asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const;data_sectionssocket/3__section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) if (neg != -2) if (static_uneg != (unsigned int)-3) if (static_neg != -4)}anon_constsocket/4__section("socket/4") int anon_const() { volatile int ctx = 0; if (ctx == values[i]) { return values[i];char__licensebpf_map_defarray_of_hash_mapkey3key1key2arg2unegnegstatic_unegstatic_neg.bss.data.maps.rodata.rodata.testlicensemaps T)+/-13579|c c  G  0HP,`*h, , (P!xN N N N N+ N) N  8< `6d06d@UphUpxe|e| (0HPX   N  y XU n`(( i <S   ) }H(op 8 X (8P`x  !#@$x`d|()*+# $$<T&l',<DTdt    0@P`p 0@P`x          ( 8 H `p#%)*,/01(&' -.2345perf_event_array.rel.text.rel.BTF.extanon_const.rodata.testsocket.bss.mapsdata_sectionsother.relxdpbtf_outer_maparray_of_hash_mapbtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__licensestatic.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5LBB8_4.relsocket/4key3global_fn3LBB8_3.relsocket/3key2hash_map2global_fn2arg2.rodata.cst32.relsocket/2key1@x @0akg @A @  @@ `\ @ hNl.MHH6H4LP  Ip/ E @  @Lo& golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-7-eb.elf000066400000000000000000000061401456504511400241010ustar00rootroot00000000000000ELF@@apccc* * * ```ca* a*U g !ya* MIT 9 8 ` h    O<o 3 ~ Xa(  X @h  @*AFD=>;:BCE<perf_event_array.rel.textanon_constsocketmapsdata_sectionsother.relxdparray_of_hash_mapno_relocationasm_relocationglobal_fnstatic_fnxdp_prog.llvm_addrsig__licensestatic.strtab.symtabMY_CONSTLBB8_4.relsocket/4global_fn3LBB8_3socket/3hash_map2global_fn2LBB8_2.rodata.cst32.relsocket/2.Lconstinit.1TG@x  AKG P'0, (  8 .P  oLH golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-7-el.elf000066400000000000000000000061401456504511400241130ustar00rootroot00000000000000ELF@@axaccc`caaUgy aMIT 9 8 ` h    O<o 3 ~ Xa( X @h  @*AFD=>;:BCE<perf_event_array.rel.textanon_constsocketmapsdata_sectionsother.relxdparray_of_hash_mapno_relocationasm_relocationglobal_fnstatic_fnxdp_prog.llvm_addrsig__licensestatic.strtab.symtabMY_CONSTLBB8_4.relsocket/4global_fn3LBB8_3socket/3hash_map2global_fn2LBB8_2.rodata.cst32.relsocket/2.Lconstinit.1TG@x  AKG P'0, (  8 .P  LoH golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-9-eb.elf000066400000000000000000000241601456504511400241050ustar00rootroot00000000000000ELF!0@@ap   pa`aaa ]a ]a ]a ca* a*U g !ya* MIT (@ & * >G  T ]@o x@ (@ x@  @ x@"! @  %@&$  ( x *  , A .  0  2  4  6  8+:0;:x @`F= @ X@]@ D CbC H g?sG~AB ( (#'EIJF<>typekeyvaluemax_entriesmap_flagsint__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inthash_mapkey_sizevalue_sizehash_map2pinningbtf_pinvaluesinner_map_tbtf_outer_mapbtf_outer_map_anonperf_event_arrayargstatic_fnstatic./testdata/loader.cstatic int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1;global_fn2.textint __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2;global_fn3otherint __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1;global_fnint __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg);xdp_progxdp map_lookup_elem(&hash_map, (void *)&key1); map_lookup_elem(&hash_map2, (void *)&key2); map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2;no_relocationsocket return 0;asm_relocationsocket/2 asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const;data_sectionssocket/3__section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) if (neg != -2) if (static_uneg != (unsigned int)-3) if (static_neg != -4)}anon_constsocket/4__section("socket/4") int anon_const() { volatile int ctx = 0; if (ctx == values[i]) { return values[i];char__licensebpf_map_defarray_of_hash_mapkey1key2arg2static_unegstatic_neg.bss.data.maps.rodata.rodata.testlicensemaps(T)+/-13579|g g  K  0HP,`*h0 0 (P%xR R R R R +R )R  8< `:d0:d@YphYpxi|i| (08 `x N  8y `U h   < ) } opH(S `n(( i    X$ %(&8P&`'x " +@(x@4Ld%p&| *    4D L \l|   ( 8 H X h x  (8HXh 0@Phx>]ebVZTSU`^<_=:;da@?YWXcperf_event_array.rel.text.rel.BTF.extanon_const.rodata.testsocket.bss.mapsdata_sectionsother.relxdpbtf_outer_maparray_of_hash_mapbtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__licensestatic.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5LBB8_4.relsocket/4key3global_fn3LBB8_3.relsocket/3key2hash_map2global_fn2arg2LBB8_2.rodata.cst32.relsocket/2key1.Lconstinit.1H@x  akg A P  `@ `\  pNt.MHP6P4TX  Ix E  I oL0&X8golang-github-cilium-ebpf-0.11.0/testdata/loader-clang-9-el.elf000066400000000000000000000241601456504511400241170ustar00rootroot00000000000000ELF0!@@axaaqaq`aa]!a]!a]!a!caaUgy aMIT (@ & * >G  T ]@o x@ (@ x@  @ x@"! @  %@&$  ( x *  , A .  0  2  4  6  8+:0;:x @`F= @ X@]@ D CbC H g?sG~AB ( (#'EIJF<>typekeyvaluemax_entriesmap_flagsint__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inthash_mapkey_sizevalue_sizehash_map2pinningbtf_pinvaluesinner_map_tbtf_outer_mapbtf_outer_map_anonperf_event_arrayargstatic_fnstatic./testdata/loader.cstatic int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1;global_fn2.textint __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2;global_fn3otherint __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1;global_fnint __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg);xdp_progxdp map_lookup_elem(&hash_map, (void *)&key1); map_lookup_elem(&hash_map2, (void *)&key2); map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2;no_relocationsocket return 0;asm_relocationsocket/2 asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const;data_sectionssocket/3__section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) if (neg != -2) if (static_uneg != (unsigned int)-3) if (static_neg != -4)}anon_constsocket/4__section("socket/4") int anon_const() { volatile int ctx = 0; if (ctx == values[i]) { return values[i];char__licensebpf_map_defarray_of_hash_mapkey1key2arg2static_unegstatic_neg.bss.data.maps.rodata.rodata.testlicensemaps(T)+/-13579|g g  K  0HP,`*h0 0 (P%xR R R R R+ R) R  8< `:d0:d@YphYpxi|i| (08 `x N  8y `U h   < ) } opH(S `n(( i   X $%(&8P&`'x  "+@(x@4Ld%p&| *    4D L \l|   ( 8 H X h x  (8HXh 0@Phx>]ebVZTSU`^<_=:;da@?YWXcperf_event_array.rel.text.rel.BTF.extanon_const.rodata.testsocket.bss.mapsdata_sectionsother.relxdpbtf_outer_maparray_of_hash_mapbtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__licensestatic.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5LBB8_4.relsocket/4key3global_fn3LBB8_3.relsocket/3key2hash_map2global_fn2arg2LBB8_2.rodata.cst32.relsocket/2key1.Lconstinit.1H@x  akg A P  `@ `\  pNt.MHP6P4TX  Ix E  I Lo0&X8golang-github-cilium-ebpf-0.11.0/testdata/loader-eb.elf000077700000000000000000000000001456504511400264732loader-clang-14-eb.elfustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/testdata/loader-el.elf000077700000000000000000000000001456504511400265172loader-clang-14-el.elfustar00rootroot00000000000000golang-github-cilium-ebpf-0.11.0/testdata/loader.c000066400000000000000000000114571456504511400217520ustar00rootroot00000000000000/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; #if __clang_major__ >= 9 // Clang < 9 doesn't emit the necessary BTF for this to work. struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); __uint(map_flags, BPF_F_NO_PREALLOC); } hash_map __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(uint32_t)); __uint(value_size, sizeof(uint64_t)); __uint(max_entries, 2); } hash_map2 __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); __uint(pinning, 1 /* LIBBPF_PIN_BY_NAME */); } btf_pin __section(".maps"); // Named map type definition, without structure variable declaration. struct inner_map_t { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, int); __uint(max_entries, 1); }; // Anonymous map type definition with structure variable declaration. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(key_size, sizeof(uint32_t)); __uint(max_entries, 1); __array(values, struct inner_map_t); } btf_outer_map __section(".maps"); // Array of maps with anonymous inner struct. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(key_size, sizeof(uint32_t)); __uint(max_entries, 1); __array( values, struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1); __type(key, uint32_t); __type(value, uint32_t); }); } btf_outer_map_anon __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(max_entries, 4096); } perf_event_array __section(".maps"); #else struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = BPF_F_NO_PREALLOC, }; struct bpf_map_def hash_map2 __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 2, }; struct bpf_map_def perf_event_array __section("maps") = { .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .max_entries = 4096, }; #endif struct bpf_map_def array_of_hash_map __section("maps") = { .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, .key_size = sizeof(uint32_t), .max_entries = 2, }; static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1; } int __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2; } int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1; } int __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg); } #if __clang_major__ >= 9 static volatile unsigned int key1 = 0; // .bss static volatile unsigned int key2 = 1; // .data volatile const unsigned int key3 = 2; // .rodata static volatile const uint32_t arg; // .rodata, populated by loader // custom .rodata section, populated by loader static volatile const uint32_t arg2 __section(".rodata.test"); #endif __section("xdp") int xdp_prog() { #if __clang_major__ < 9 unsigned int key1 = 0; unsigned int key2 = 1; unsigned int key3 = 2; uint32_t arg = 1; uint32_t arg2 = 2; #endif map_lookup_elem(&hash_map, (void *)&key1); map_lookup_elem(&hash_map2, (void *)&key2); map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2; } // This function has no relocations, and is thus parsed differently. __section("socket") int no_relocation() { return 0; } // Make sure we allow relocations generated by inline assembly. __section("socket/2") int asm_relocation() { int my_const; asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const; } #if __clang_major__ >= 9 volatile const unsigned int uneg = -1; volatile const int neg = -2; static volatile const unsigned int static_uneg = -3; static volatile const int static_neg = -4; __section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) return __LINE__; if (neg != -2) return __LINE__; if (static_uneg != (unsigned int)-3) return __LINE__; if (static_neg != -4) return __LINE__; return 0; } #else __section("socket/3") int data_sections() { return 0; } #endif /* * Up until LLVM 14, this program results in an .rodata.cst32 section * that is accessed by 'return values[i]'. For this section, no BTF is * emitted. 'values' cannot be rewritten, since there is no BTF info * describing the data section. */ __section("socket/4") int anon_const() { volatile int ctx = 0; // 32 bytes wide results in a .rodata.cst32 section. #define values \ (uint64_t[]) { \ 0x0, 0x1, 0x2, 0x3 \ } int i; for (i = 0; i < 3; i++) { if (ctx == values[i]) { return values[i]; } } return 0; } golang-github-cilium-ebpf-0.11.0/testdata/manyprogs-eb.elf000066400000000000000000001172201456504511400234260ustar00rootroot00000000000000ELF@@Gp{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@p{#j` 0qy:1 qy:1j` q&U * :0@Dual MIT/GPL88   "0 3= B   "  V        &  Z        0 ! g #  %  '  ) C + z -  /  1  3 V 5  7  9  ; 2 = i ?  A  CEG  'F1= B K @V `b lIwH Jintkprobe_execve0kprobe/sys_execvea0./testdata/manyprogs.cDEFINE_PROBE(0); uint64_t initval = 1, *valp; struct task_struct *task = (struct task_struct *)bpf_get_current_task();task_structnsproxy0:0 uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);mnt_nsmnt_namespacensns_commoninumunsigned int0:0:0 valp = bpf_map_lookup_elem(&kprobe_map, &mntns); if (!valp) { bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0); __sync_fetch_and_add(valp, 1);kprobe_execve1kprobe/sys_execvea1DEFINE_PROBE(1);kprobe_execve2kprobe/sys_execvea2DEFINE_PROBE(2);kprobe_execve3kprobe/sys_execvea3DEFINE_PROBE(3);kprobe_execve4kprobe/sys_execvea4DEFINE_PROBE(4);kprobe_execve5kprobe/sys_execvea5DEFINE_PROBE(5);kprobe_execve6kprobe/sys_execvea6DEFINE_PROBE(6);kprobe_execve7kprobe/sys_execvea7DEFINE_PROBE(7);kprobe_execve8kprobe/sys_execvea8DEFINE_PROBE(8);kprobe_execve9kprobe/sys_execvea9DEFINE_PROBE(9);kprobe_execve10kprobe/sys_execvea10DEFINE_PROBE(10);kprobe_execve11kprobe/sys_execvea11DEFINE_PROBE(11);kprobe_execve12kprobe/sys_execvea12DEFINE_PROBE(12);kprobe_execve13kprobe/sys_execvea13DEFINE_PROBE(13);kprobe_execve14kprobe/sys_execvea14DEFINE_PROBE(14);kprobe_execve15kprobe/sys_execvea15DEFINE_PROBE(15);kprobe_execve16kprobe/sys_execvea16DEFINE_PROBE(16);kprobe_execve17kprobe/sys_execvea17DEFINE_PROBE(17);kprobe_execve18kprobe/sys_execvea18DEFINE_PROBE(18);kprobe_execve19kprobe/sys_execvea19DEFINE_PROBE(19);kprobe_execve20kprobe/sys_execvea20DEFINE_PROBE(20);kprobe_execve21kprobe/sys_execvea21DEFINE_PROBE(21);kprobe_execve22kprobe/sys_execvea22DEFINE_PROBE(22);kprobe_execve23kprobe/sys_execvea23DEFINE_PROBE(23);kprobe_execve24kprobe/sys_execvea24DEFINE_PROBE(24);kprobe_execve25kprobe/sys_execvea25DEFINE_PROBE(25);kprobe_execve26kprobe/sys_execvea26DEFINE_PROBE(26);kprobe_execve27kprobe/sys_execvea27DEFINE_PROBE(27);kprobe_execve28kprobe/sys_execvea28DEFINE_PROBE(28);kprobe_execve29kprobe/sys_execvea29DEFINE_PROBE(29);char__ARRAY_SIZE_TYPE____licensebpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagskprobe_maplicensemaps T8 1e5i  @"w$&(*S,.02/4f68: <B>y@BD (?(P (n30(8((((U (((8(@(? ((P (n30(8((((U (((8(@(1 (E(P (n30(8((((U (((8(@(Ee (y(P (n30(8((((U (((8(@(y ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@(5 (I(P (n30(8((((U (((8(@(Ii (}(P (n30(8((((U (((8(@(} ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@(@ (U(P (n30(8((((U (((8(@(Uw ((P (n30(8((((U (((8(@( ( (P (n30(8((((U (((8(@(  ($(P (n30(8((((U (((8(@($ (1((P (n30(8((((U (((8(@(1(S (h,(P (n30(8((((U (((8(@(h, (0(P (n30(8((((U (((8(@(0 (4(P (n30(8((((U (((8(@(4 ( <(P (n30(8((((U (((8(@( </ (D@(P (n30(8((((U (((8(@(D@f ({D(P (n30(8((((U (((8(@({D (H(P (n30(8((((U (((8(@(H (L(P (n30(8((((U (((8(@(L ( P(P (n30(8((((U (((8(@( PB (WT(P (n30(8((((U (((8(@(WTy (X(P (n30(8((((U (((8(@(X (\(P (n30(8((((U (((8(@(\ (`(P (n30(8((((U (((8(@(`XOXO1XOeXOXOXOXO5XOiXOXOXO XO@XOwXOXOXOXOSXOXOXOXO/XOfXOXOXO XOBXOyXOXOXO8!@i8 @R8@ ; 8 @ $ 8 @  8 @8@8@8i@8R@80@x8@a8@J8@38@!!8!@##8#@%%8%@''8x'@))8a)@++8(+@-p-8-@/Y/8/@1B181@3+383@5585@7787@9989@;;8p;@==8Y=@bP@PP+ P P9 PPGPPUPP9PPaPP!P#P%P'P)P+P-P/P81P3P`5P7Pn9P;P|=P2? \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\0zH\,<L\ l |"%( +.,1<4L7\:l=|@CFILORUX 0@P`p(8HXhx 0@P`p         ( 8 H X p            0 HXhx 0@P`p(8HXhx    0 @ P ` p           ( 8 H X h            0 @ X" h" x" " " " " " " " " " " 0% @% P% `% p% % % % % % % % % ( ( (( 8( H( X( h( x( ( ( ( ( ( + +++ +0+@+P+`+p++++.......(.8.H.X.h.x.111111111 101@1P1h4x44444444444(4@7P7`7p7777777777:(:8:H:X:h:x:::::::=== =0=@=P=`=p=====@@@@@@(@8@H@X@h@x@@CCCCCCCC C0C@CPC`CxFFFFFFFFFFF(F8FPI`IpIIIIIIIIIII(L8LHLXLhLxLLLLLLLLOO O0O@OPO`OpOOOOOORRRRR(R8RHRXRhRxRRRUUUUUUU U0U@UPU`UpUXXXXXXXXXX(X8XHXdt  , D T d | $4D\l|"""%%$%<(L(\(t+++...1114,4<4T7d7t7:::===@ @ @ 4C DC TC lF |F F I I I L L L!O!$O!4O!LR!\R!lR!U!U!U!X!X!Xeghijklmnopqrstuvwxyz{|}~f.text.rel.BTF.extmapskprobe_map.llvm_addrsig__license.strtab.symtab.rel.BTFkprobe_execve9.relkprobe/sys_execvea9kprobe_execve29.relkprobe/sys_execvea29kprobe_execve19.relkprobe/sys_execvea19kprobe_execve8.relkprobe/sys_execvea8kprobe_execve28.relkprobe/sys_execvea28kprobe_execve18.relkprobe/sys_execvea18kprobe_execve7.relkprobe/sys_execvea7kprobe_execve27.relkprobe/sys_execvea27kprobe_execve17.relkprobe/sys_execvea17kprobe_execve6.relkprobe/sys_execvea6kprobe_execve26.relkprobe/sys_execvea26kprobe_execve16.relkprobe/sys_execvea16kprobe_execve5.relkprobe/sys_execvea5kprobe_execve25.relkprobe/sys_execvea25kprobe_execve15.relkprobe/sys_execvea15kprobe_execve4.relkprobe/sys_execvea4kprobe_execve24.relkprobe/sys_execvea24kprobe_execve14.relkprobe/sys_execvea14kprobe_execve3.relkprobe/sys_execvea3LBB9_3LBB29_3LBB19_3LBB8_3LBB28_3LBB18_3LBB7_3LBB27_3LBB17_3LBB6_3LBB26_3LBB16_3LBB5_3LBB25_3LBB15_3LBB4_3LBB24_3LBB14_3LBB3_3LBB23_3LBB13_3LBB2_3LBB22_3LBB12_3LBB1_3LBB21_3LBB11_3LBB0_3LBB20_3LBB10_3kprobe_execve23.relkprobe/sys_execvea23kprobe_execve13.relkprobe/sys_execvea13kprobe_execve2.relkprobe/sys_execvea2LBB9_2LBB29_2LBB19_2LBB8_2LBB28_2LBB18_2LBB7_2LBB27_2LBB17_2LBB6_2LBB26_2LBB16_2LBB5_2LBB25_2LBB15_2LBB4_2LBB24_2LBB14_2LBB3_2LBB23_2LBB13_2LBB2_2LBB22_2LBB12_2LBB1_2LBB21_2LBB11_2LBB0_2LBB20_2LBB10_2kprobe_execve22.relkprobe/sys_execvea22kprobe_execve12.relkprobe/sys_execvea12kprobe_execve1.relkprobe/sys_execvea1kprobe_execve21.relkprobe/sys_execvea21kprobe_execve11.relkprobe/sys_execvea11kprobe_execve0.relkprobe/sys_execvea0kprobe_execve20.relkprobe/sys_execvea20kprobe_execve10.relkprobe/sys_execvea10<@u@Pq @b FP @b0 FP @bP F>0P: @bp F P @b F LPH @b F  P @b FZ pPV @b F P @c Fh Pd @c0 F `P @cP FMPI @cp FP @c FuPPq @c FP @c FP @c F!$@P @d F#P @d0 F%2P. @dP F'0P @dp F)P @d F+$P @d F- P @d F/LpPH @d F1P @e F3t!Pp @e0 F5"`P @eP F7#P~ @ep F9 %P @e F;&PP @e F=4' 'P' L @e FA 4! @eFC$oL%DV [golang-github-cilium-ebpf-0.11.0/testdata/manyprogs-el.elf000066400000000000000000001172201456504511400234400ustar00rootroot00000000000000ELFЌ@@G{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU p{z#aqyaqyaqbU pDual MIT/GPL88   "0 3= B  " V        &  Z        0 ! g #  %  '   ) C + z -  /  1  3 V 5  7  9  ; 2 = i ?  A  CEG  'F1= B K @V `b lIwH Jintkprobe_execve0kprobe/sys_execvea0./testdata/manyprogs.cDEFINE_PROBE(0); uint64_t initval = 1, *valp; struct task_struct *task = (struct task_struct *)bpf_get_current_task();task_structnsproxy0:0 uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);mnt_nsmnt_namespacensns_commoninumunsigned int0:0:0 valp = bpf_map_lookup_elem(&kprobe_map, &mntns); if (!valp) { bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0); __sync_fetch_and_add(valp, 1);kprobe_execve1kprobe/sys_execvea1DEFINE_PROBE(1);kprobe_execve2kprobe/sys_execvea2DEFINE_PROBE(2);kprobe_execve3kprobe/sys_execvea3DEFINE_PROBE(3);kprobe_execve4kprobe/sys_execvea4DEFINE_PROBE(4);kprobe_execve5kprobe/sys_execvea5DEFINE_PROBE(5);kprobe_execve6kprobe/sys_execvea6DEFINE_PROBE(6);kprobe_execve7kprobe/sys_execvea7DEFINE_PROBE(7);kprobe_execve8kprobe/sys_execvea8DEFINE_PROBE(8);kprobe_execve9kprobe/sys_execvea9DEFINE_PROBE(9);kprobe_execve10kprobe/sys_execvea10DEFINE_PROBE(10);kprobe_execve11kprobe/sys_execvea11DEFINE_PROBE(11);kprobe_execve12kprobe/sys_execvea12DEFINE_PROBE(12);kprobe_execve13kprobe/sys_execvea13DEFINE_PROBE(13);kprobe_execve14kprobe/sys_execvea14DEFINE_PROBE(14);kprobe_execve15kprobe/sys_execvea15DEFINE_PROBE(15);kprobe_execve16kprobe/sys_execvea16DEFINE_PROBE(16);kprobe_execve17kprobe/sys_execvea17DEFINE_PROBE(17);kprobe_execve18kprobe/sys_execvea18DEFINE_PROBE(18);kprobe_execve19kprobe/sys_execvea19DEFINE_PROBE(19);kprobe_execve20kprobe/sys_execvea20DEFINE_PROBE(20);kprobe_execve21kprobe/sys_execvea21DEFINE_PROBE(21);kprobe_execve22kprobe/sys_execvea22DEFINE_PROBE(22);kprobe_execve23kprobe/sys_execvea23DEFINE_PROBE(23);kprobe_execve24kprobe/sys_execvea24DEFINE_PROBE(24);kprobe_execve25kprobe/sys_execvea25DEFINE_PROBE(25);kprobe_execve26kprobe/sys_execvea26DEFINE_PROBE(26);kprobe_execve27kprobe/sys_execvea27DEFINE_PROBE(27);kprobe_execve28kprobe/sys_execvea28DEFINE_PROBE(28);kprobe_execve29kprobe/sys_execvea29DEFINE_PROBE(29);char__ARRAY_SIZE_TYPE____licensebpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagskprobe_maplicensemaps T8 1e5i  @"w$&(*S,.02/4f68: <B>y@BD (?(P (n30(8((((U (((8(@(? ((P (n30(8((((U (((8(@(1 (E(P (n30(8((((U (((8(@(Ee (y(P (n30(8((((U (((8(@(y ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@(5 (I(P (n30(8((((U (((8(@(Ii (}(P (n30(8((((U (((8(@(} ((P (n30(8((((U (((8(@( ((P (n30(8((((U (((8(@(  ((P (n30(8((((U (((8(@(@ (U(P (n30(8((((U (((8(@(Uw ((P (n30(8((((U (((8(@( ( (P (n30(8((((U (((8(@(  ($(P (n30(8((((U (((8(@($ (1((P (n30(8((((U (((8(@(1(S (h,(P (n30(8((((U (((8(@(h, (0(P (n30(8((((U (((8(@(0 (4(P (n30(8((((U (((8(@(4 ( <(P (n30(8((((U (((8(@( </ (D@(P (n30(8((((U (((8(@(D@f ({D(P (n30(8((((U (((8(@({D (H(P (n30(8((((U (((8(@(H (L(P (n30(8((((U (((8(@(L  ( P(P (n30(8((((U (((8(@( PB (WT(P (n30(8((((U (((8(@(WTy (X(P (n30(8((((U (((8(@(X (\(P (n30(8((((U (((8(@(\ (`(P (n30(8((((U (((8(@(`XOXO1XOeXOXOXOXO5XOiXOXOXO XO@XOwXOXOXOXOSXOXOXOXO/XOfXOXOXO XOBXOyXOXOXO8!@i8 @R8@ ; 8 @ $ 8 @  8 @8@8@8i@8R@80@x8@a8@J8@38@!!8!@##8#@%%8%@''8x'@))8a)@++8(+@-p-8-@/Y/8/@1B181@3+383@5585@7787@9989@;;8p;@==8Y=@bP@PP+ P P9 PPGPPUPP9PPaPP!P#P%P'P)P+P-P/P81P3P`5P7Pn9P;P|=P2? \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\0zH\,<L\ l |"%( +.,1<4L7\:l=|@CFILORUX 0@P`p(8HXhx 0@P`p         ( 8 H X p            0 HXhx 0@P`p(8HXhx   0 @ P ` p           ( 8 H X h            0 @ X "h "x " " " " " " " " " " "0 %@ %P %` %p % % % % % % % % % ( (( (8 (H (X (h (x ( ( ( ( ( ( + +++ +0+@+P+`+p++++.......(.8.H.X.h.x.111111111 101@1P1h4x44444444444(4@7P7`7p7777777777:(:8:H:X:h:x:::::::=== =0=@=P=`=p=====@@@@@@(@8@H@X@h@x@@CCCCCCCC C0C@CPC`CxFFFFFFFFFFF(F8FPI`IpIIIIIIIIIII(L8LHLXLhLxLLLLLLLLOO O0O@OPO`OpOOOOOORRRRR(R8RHRXRhRxRRRUUUUUUU U0U@UPU`UpUXXXXXXXXXX(X8XHXdt   , D T d | $4D\l|"""%%$%<(L(\(t+++...1114,4<4T7d7t7:::===@ @ @4 CD CT Cl F| F F I I I L L L!O$!O4!OL!R\!Rl!R!U!U!U!X!X!Xeghijklmnopqrstuvwxyz{|}~f.text.rel.BTF.extmapskprobe_map.llvm_addrsig__license.strtab.symtab.rel.BTFkprobe_execve9.relkprobe/sys_execvea9kprobe_execve29.relkprobe/sys_execvea29kprobe_execve19.relkprobe/sys_execvea19kprobe_execve8.relkprobe/sys_execvea8kprobe_execve28.relkprobe/sys_execvea28kprobe_execve18.relkprobe/sys_execvea18kprobe_execve7.relkprobe/sys_execvea7kprobe_execve27.relkprobe/sys_execvea27kprobe_execve17.relkprobe/sys_execvea17kprobe_execve6.relkprobe/sys_execvea6kprobe_execve26.relkprobe/sys_execvea26kprobe_execve16.relkprobe/sys_execvea16kprobe_execve5.relkprobe/sys_execvea5kprobe_execve25.relkprobe/sys_execvea25kprobe_execve15.relkprobe/sys_execvea15kprobe_execve4.relkprobe/sys_execvea4kprobe_execve24.relkprobe/sys_execvea24kprobe_execve14.relkprobe/sys_execvea14kprobe_execve3.relkprobe/sys_execvea3LBB9_3LBB29_3LBB19_3LBB8_3LBB28_3LBB18_3LBB7_3LBB27_3LBB17_3LBB6_3LBB26_3LBB16_3LBB5_3LBB25_3LBB15_3LBB4_3LBB24_3LBB14_3LBB3_3LBB23_3LBB13_3LBB2_3LBB22_3LBB12_3LBB1_3LBB21_3LBB11_3LBB0_3LBB20_3LBB10_3kprobe_execve23.relkprobe/sys_execvea23kprobe_execve13.relkprobe/sys_execvea13kprobe_execve2.relkprobe/sys_execvea2LBB9_2LBB29_2LBB19_2LBB8_2LBB28_2LBB18_2LBB7_2LBB27_2LBB17_2LBB6_2LBB26_2LBB16_2LBB5_2LBB25_2LBB15_2LBB4_2LBB24_2LBB14_2LBB3_2LBB23_2LBB13_2LBB2_2LBB22_2LBB12_2LBB1_2LBB21_2LBB11_2LBB0_2LBB20_2LBB10_2kprobe_execve22.relkprobe/sys_execvea22kprobe_execve12.relkprobe/sys_execvea12kprobe_execve1.relkprobe/sys_execvea1kprobe_execve21.relkprobe/sys_execvea21kprobe_execve11.relkprobe/sys_execvea11kprobe_execve0.relkprobe/sys_execvea0kprobe_execve20.relkprobe/sys_execvea20kprobe_execve10.relkprobe/sys_execvea10<@u@Pq @b FP @0b FP @Pb F>0P: @pb F P @b F LPH @b F  P @b FZp PV @b F P @c Fh Pd @0c F` P @Pc FMPI @pc FP @c FuPPq @c FP @c FP @c F!$@P  @d F#P @0d F%2P. @Pd F'0P @pd F)P @d F+$P  @d F- P @d F/LpPH @d F1P @e F3t!Pp @0e F5`"P @Pe F7#P~ @pe F9 %P @e F;P&P @e F=4' 'P' L @e FA 4! @eFC$LoЅ%DV [golang-github-cilium-ebpf-0.11.0/testdata/manyprogs.c000066400000000000000000000041501456504511400225130ustar00rootroot00000000000000/* This file is used for benchmarking NewCollection(). */ #include "../btf/testdata/bpf_core_read.h" #include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; struct bpf_map_def __section("maps") kprobe_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 128, }; static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *)1; static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, uint64_t flags) = (void *)2; static void *(*bpf_get_current_task)() = (void *)35; static long (*bpf_probe_read_kernel)(void *dst, uint32_t size, const void *unsafe_ptr) = (void *)113; #pragma clang attribute push(__attribute__((preserve_access_index)), apply_to = record) struct ns_common { unsigned int inum; }; struct mnt_namespace { struct ns_common ns; }; struct nsproxy { struct mnt_namespace *mnt_ns; }; struct task_struct { struct nsproxy *nsproxy; }; #pragma clang attribute pop static inline int impl() { uint64_t initval = 1, *valp; struct task_struct *task = (struct task_struct *)bpf_get_current_task(); uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum); valp = bpf_map_lookup_elem(&kprobe_map, &mntns); if (!valp) { bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0); return 0; } __sync_fetch_and_add(valp, 1); return 0; } #define DEFINE_PROBE(i) \ __section("kprobe/sys_execvea" #i) int kprobe_execve##i() { \ return impl(); \ } DEFINE_PROBE(0); DEFINE_PROBE(1); DEFINE_PROBE(2); DEFINE_PROBE(3); DEFINE_PROBE(4); DEFINE_PROBE(5); DEFINE_PROBE(6); DEFINE_PROBE(7); DEFINE_PROBE(8); DEFINE_PROBE(9); DEFINE_PROBE(10); DEFINE_PROBE(11); DEFINE_PROBE(12); DEFINE_PROBE(13); DEFINE_PROBE(14); DEFINE_PROBE(15); DEFINE_PROBE(16); DEFINE_PROBE(17); DEFINE_PROBE(18); DEFINE_PROBE(19); DEFINE_PROBE(20); DEFINE_PROBE(21); DEFINE_PROBE(22); DEFINE_PROBE(23); DEFINE_PROBE(24); DEFINE_PROBE(25); DEFINE_PROBE(26); DEFINE_PROBE(27); DEFINE_PROBE(28); DEFINE_PROBE(29); golang-github-cilium-ebpf-0.11.0/testdata/map_spin_lock-eb.elf000066400000000000000000000023001456504511400242150ustar00rootroot00000000000000ELF@@<<  "  /9= BP  TY@]c o } int__ARRAY_SIZE_TYPE__uint32_tunsigned inthash_elemcntlockbpf_spin_lockvaltypekeyvaluemax_entriesspin_lock_map.maps  L.text.mapsspin_lock_map.llvm_addrsig.strtab.symtab.rel.BTF)yB@@ =`9 @hoLx180golang-github-cilium-ebpf-0.11.0/testdata/map_spin_lock-el.elf000066400000000000000000000023001456504511400242270ustar00rootroot00000000000000ELF@@<<  "  /9= BP  TY@]c o } int__ARRAY_SIZE_TYPE__uint32_tunsigned inthash_elemcntlockbpf_spin_lockvaltypekeyvaluemax_entriesspin_lock_map.maps  L.text.mapsspin_lock_map.llvm_addrsig.strtab.symtab.rel.BTF)yB@@ =`9 @hLox180golang-github-cilium-ebpf-0.11.0/testdata/map_spin_lock.c000066400000000000000000000006241456504511400233140ustar00rootroot00000000000000/* This file excercises bpf_spin_lock. */ #include "common.h" struct bpf_spin_lock { uint32_t val; }; struct hash_elem { int cnt; struct bpf_spin_lock lock; }; #if __clang_major__ >= 9 struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, struct hash_elem); __uint(max_entries, 2); } spin_lock_map __section(".maps"); #else #error This file required clang >= 9 #endif golang-github-cilium-ebpf-0.11.0/testdata/raw_tracepoint-eb.elf000066400000000000000000000030401456504511400244220ustar00rootroot00000000000000ELF`@@ MIT @& :> B    bpf_argsargsuint64_tunsigned long__ARRAY_SIZE_TYPE__ctxintsched_process_execraw_tracepoint/sched_process_exec./testdata/raw_tracepoint.c return 0;char__licenselicense 0U Uw0;",@ .text.rel.BTF.ext.llvm_addrsig__licenseraw_tracepoint/sched_process_exec.strtab.symtab.rel.BTFNg@,@$PbT^ @   P @ oLV``golang-github-cilium-ebpf-0.11.0/testdata/raw_tracepoint-el.elf000066400000000000000000000030401456504511400244340ustar00rootroot00000000000000ELF`@@ MIT @&  :> B    bpf_argsargsuint64_tunsigned long__ARRAY_SIZE_TYPE__ctxintsched_process_execraw_tracepoint/sched_process_exec./testdata/raw_tracepoint.c return 0;char__licenselicense 0U Uw0;",@ .text.rel.BTF.ext.llvm_addrsig__licenseraw_tracepoint/sched_process_exec.strtab.symtab.rel.BTFNg@,@$PbT^ @   P @ LoV``golang-github-cilium-ebpf-0.11.0/testdata/raw_tracepoint.c000066400000000000000000000004051456504511400235140ustar00rootroot00000000000000/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_args { uint64_t args[0]; }; __section("raw_tracepoint/sched_process_exec") int sched_process_exec(struct bpf_args *ctx) { return 0; } golang-github-cilium-ebpf-0.11.0/testdata/strings-eb.elf000066400000000000000000000052101456504511400230730ustar00rootroot00000000000000ELF@@ ac c:0 @MITThis string is allocated in the string section =   ! 0 & /  <A@MQ W ^  %/ 5int__ARRAY_SIZE_TYPE__custkeycharuint32_tunsigned inttypemax_entrieskeyvaluemy_mapfilterxdp./testdata/strings.c uint32_t *value = bpf_map_lookup_elem(&my_map, KEY); if (value) (*value)++; uint32_t newValue = 1; bpf_map_update_elem(&my_map, KEY, &newValue, 0); return 2;__license.mapslicense |eei~P(iT0iX Xi` hipidiphPa) >p,@P`p.text.rel.BTF.ext.mapsfilter.relxdpmy_map.llvm_addrsig__license.strtab.symtab.rel.BTFLBB0_3LBB0_2.rodata.str1.1H~@%@! @@ @ o20\HX @  , @ 0oLPgolang-github-cilium-ebpf-0.11.0/testdata/strings-el.elf000066400000000000000000000052101456504511400231050ustar00rootroot00000000000000ELF@@ac cMITThis string is allocated in the string section =   ! 0 & /  <A@MQ W ^  %/ 5int__ARRAY_SIZE_TYPE__custkeycharuint32_tunsigned inttypemax_entrieskeyvaluemy_mapfilterxdp./testdata/strings.c uint32_t *value = bpf_map_lookup_elem(&my_map, KEY); if (value) (*value)++; uint32_t newValue = 1; bpf_map_update_elem(&my_map, KEY, &newValue, 0); return 2;__license.mapslicense |eei~P(iT0i XXi `hipidiphPa) >p,@P`p.text.rel.BTF.ext.mapsfilter.relxdpmy_map.llvm_addrsig__license.strtab.symtab.rel.BTFLBB0_3LBB0_2.rodata.str1.1H~@%@! @@ @ o20\HX @  , @ 0LoPgolang-github-cilium-ebpf-0.11.0/testdata/strings.c000066400000000000000000000013471456504511400221720ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "MIT"; typedef char custkey[48]; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 2); __type(key, custkey); __type(value, uint32_t); } my_map __section(".maps"); static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *)1; static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, uint64_t flags) = (void *)2; #define KEY "This string is allocated in the string section\n" __section("xdp") int filter() { uint32_t *value = bpf_map_lookup_elem(&my_map, KEY); if (value) (*value)++; else { uint32_t newValue = 1; bpf_map_update_elem(&my_map, KEY, &newValue, 0); } return 2; } golang-github-cilium-ebpf-0.11.0/testdata/subprog_reloc-eb.elf000066400000000000000000000062701456504511400242560ustar00rootroot00000000000000ELF 8@@c*{* :0@c{j`:0&@ 0@&yMIT      L     @ `     intfp_relocationxdp./testdata/subprog_reloc.c__section("xdp") int fp_relocation() { uint32_t key = 0; uint64_t val = 1; map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); for_each_map_elem(&hash_map, sub_prog, (void *)0, 0); uint64_t *new_val = map_lookup_elem(&hash_map, &key); if (!new_val) { return *new_val;}sub_prog.textstatic int sub_prog() { uint64_t val = 42; return 0;char__ARRAY_SIZE_TYPE____licensebpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned inthash_maplicensemaps $$8U 2`Yd lh (@phx'8 JU[<Y@ sD (@L`T<pv.%S@@hx0,<P`p(8H.rel.text.rel.BTF.extmaps.relxdphash_mapfp_relocationsub_prog.llvm_addrsig__license.strtab.symtab.rel.BTFLBB0_2]}@p @  ! @0@ UqKm @p X @ EoLe`golang-github-cilium-ebpf-0.11.0/testdata/subprog_reloc-el.elf000066400000000000000000000062701456504511400242700ustar00rootroot00000000000000ELF8 @@c*{c{bbyMIT      L     @ `     intfp_relocationxdp./testdata/subprog_reloc.c__section("xdp") int fp_relocation() { uint32_t key = 0; uint64_t val = 1; map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); for_each_map_elem(&hash_map, sub_prog, (void *)0, 0); uint64_t *new_val = map_lookup_elem(&hash_map, &key); if (!new_val) { return *new_val;}sub_prog.textstatic int sub_prog() { uint64_t val = 42; return 0;char__ARRAY_SIZE_TYPE____licensebpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned inthash_maplicensemaps $$8U 2`Y dl h(@phx'8 JU[<Y @s D(@L`T<pv.%S@@hx0,<P`p(8H.rel.text.rel.BTF.extmaps.relxdphash_mapfp_relocationsub_prog.llvm_addrsig__license.strtab.symtab.rel.BTFLBB0_2]}@p @  ! @0@ UqKm @p X @  ELoe`golang-github-cilium-ebpf-0.11.0/testdata/subprog_reloc.c000066400000000000000000000013311456504511400233370ustar00rootroot00000000000000/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, }; static int sub_prog() { uint32_t key = 0; uint64_t val = 42; map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); return 0; } __section("xdp") int fp_relocation() { uint32_t key = 0; uint64_t val = 1; map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); for_each_map_elem(&hash_map, sub_prog, (void *)0, 0); uint64_t *new_val = map_lookup_elem(&hash_map, &key); if (!new_val) { return -1; } return *new_val; } golang-github-cilium-ebpf-0.11.0/testdata/vdso.bin000066400000000000000000000074101456504511400217770ustar00rootroot00000000000000ELF>@ @8@< <   TTPtdDD   48Fe ~UqR0hK x|Cn*&&bemX("  @=" 6 " @ " ! hJ %Q" %__vdso_gettimeofday__vdso_time__vdso_clock_gettime__vdso_clock_getres__vdso_getcpulinux-vdso.so.1LINUX_2.6 XuhX oh r ohooJLinux|LinuxGNUB2:Or!G=k;@l\,|4LzRx <nAT Ec GBA A [ BEA F \8H4ptAC BHI BA A R BA A 4AC BEs DA A H IA A @ 8%UHcIIHL7HAUATS7@uGttu[A\A]]fH H I@(HOHxD_H9s H)IHЋOMX 9uHH=ɚ;v1H-ʚ;H=ɚ;wI[A\MA]]I1L%A$xL-LfA $9uH H ILcHA+fH H HuH+ZIĉIHL PHIHHH H 2A9uHfw1f੃tHt%H1HVè`u uú =1UHATISHHt8HHUHM1H=u+HEHEHiMbH&HC1MuH[A\]HL`H[A\]ËA$AT$HHtHUHAUIATAw1੃t4HNHH=mhuA\1A]]DLA\A]]è`utHNHH=!,HcH HHHuH1IuHqIu29t@[{Ht Ht 1WrO;sErf=;3r+;!111GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0.shstrtab.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_d.dynamic.note.eh_frame_hdr.eh_frame.text.altinstructions.altinstr_replacement.comment  D ohhP  r%oJJ2ohh8A JTPD^XXhEn [ 0< *f golang-github-cilium-ebpf-0.11.0/testdata/vdso_multiple_notes.bin000066400000000000000000000200001456504511400251100ustar00rootroot00000000000000ELF>p@P @8@  HHPtdLL    A Cnx|'be R0hK~UqmX ! W" `j" Tw * `6 TJ" \" " *__vdso_gettimeofday__vdso_time__vdso_clock_gettime__vdso_clock_getresgettimeofdaytimeclock_gettimeclock_getres__vdso_getcpugetcpulinux-vdso.so.1LINUX_2.6 u  o o~ooLinuxLinuxGNU/VbXp;LhH(P(hzRx $(TAC { A S $D`AC Gh A lAC P $AC Cw A $@TAC { A S $xUAC IG *AC e UHw.ƒtH@B`uHu ]1HtHHN]ff.UHAVSHIHtQHHEHEH=HU1xt`HL HEHEHiMbH&HC1Mu H[A^]Ë A ANUHH5HtH]Ðf.UHAVSIwu੃tH=L1uL[A^]è`t?HH HH uHIHAIF519t٨uLH=UUHw.9ƒtH@B`uHu ]1HtHHN]ff.UHAWAVSHIHcHL I(DAtGAtfH H Hy|5 6fH H H+D5 HHHHHLuHHeH H 9uHHxvM9HG_OMq7D93HH)HE1H9IFLHHuHʚ;r'1f.H6eHu؃Hɚ;w1IM0IpDH[A^A_]Ð @L$ÐUH{Ht Ht 1]Ú=r5;+r#;11Linker: LLD 11.0.0 (/var/cache/chromeos-cache/distfiles/host/egit-src/llvm-project a8e5dcb072b1f794883ae8125fb08c06db678d56)Chromium OS 11.0_pre391452_p20200527-r7 clang version 11.0.0 (/var/cache/chromeos-cache/distfiles/host/egit-src/llvm-project a8e5dcb072b1f794883ae8125fb08c06db678d56).shstrtab.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_d.dynamic.rodata.note.eh_frame_hdr.eh_frame.text.altinstructions.altinstr_replacement.comment  ` oP %o~~2o8AJRHXLf((<pppv* * Ak k 0 % golang-github-cilium-ebpf-0.11.0/types.go000066400000000000000000000204071456504511400202150ustar00rootroot00000000000000package ebpf import ( "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) //go:generate stringer -output types_string.go -type=MapType,ProgramType,PinType // MapType indicates the type map structure // that will be initialized in the kernel. type MapType uint32 // All the various map types that can be created const ( UnspecifiedMap MapType = iota // Hash is a hash map Hash // Array is an array map Array // ProgramArray - A program array map is a special kind of array map whose map // values contain only file descriptors referring to other eBPF // programs. Thus, both the key_size and value_size must be // exactly four bytes. This map is used in conjunction with the // TailCall helper. ProgramArray // PerfEventArray - A perf event array is used in conjunction with PerfEventRead // and PerfEventOutput calls, to read the raw bpf_perf_data from the registers. PerfEventArray // PerCPUHash - This data structure is useful for people who have high performance // network needs and can reconcile adds at the end of some cycle, so that // hashes can be lock free without the use of XAdd, which can be costly. PerCPUHash // PerCPUArray - This data structure is useful for people who have high performance // network needs and can reconcile adds at the end of some cycle, so that // hashes can be lock free without the use of XAdd, which can be costly. // Each CPU gets a copy of this hash, the contents of all of which can be reconciled // later. PerCPUArray // StackTrace - This holds whole user and kernel stack traces, it can be retrieved with // GetStackID StackTrace // CGroupArray - This is a very niche structure used to help SKBInCGroup determine // if an skb is from a socket belonging to a specific cgroup CGroupArray // LRUHash - This allows you to create a small hash structure that will purge the // least recently used items rather than thow an error when you run out of memory LRUHash // LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs, // it has more to do with including the CPU id with the LRU calculation so that if a // particular CPU is using a value over-and-over again, then it will be saved, but if // a value is being retrieved a lot but sparsely across CPUs it is not as important, basically // giving weight to CPU locality over overall usage. LRUCPUHash // LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful, // for storing things like IP addresses which can be bit masked allowing for keys of differing // values to refer to the same reference based on their masks. See wikipedia for more details. LPMTrie // ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps // itself. ArrayOfMaps // HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps // itself. HashOfMaps // DevMap - Specialized map to store references to network devices. DevMap // SockMap - Specialized map to store references to sockets. SockMap // CPUMap - Specialized map to store references to CPUs. CPUMap // XSKMap - Specialized map for XDP programs to store references to open sockets. XSKMap // SockHash - Specialized hash to store references to sockets. SockHash // CGroupStorage - Special map for CGroups. CGroupStorage // ReusePortSockArray - Specialized map to store references to sockets that can be reused. ReusePortSockArray // PerCPUCGroupStorage - Special per CPU map for CGroups. PerCPUCGroupStorage // Queue - FIFO storage for BPF programs. Queue // Stack - LIFO storage for BPF programs. Stack // SkStorage - Specialized map for local storage at SK for BPF programs. SkStorage // DevMapHash - Hash-based indexing scheme for references to network devices. DevMapHash // StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF // program. StructOpsMap // RingBuf - Similar to PerfEventArray, but shared across all CPUs. RingBuf // InodeStorage - Specialized local storage map for inodes. InodeStorage // TaskStorage - Specialized local storage map for task_struct. TaskStorage ) // hasPerCPUValue returns true if the Map stores a value per CPU. func (mt MapType) hasPerCPUValue() bool { return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash || mt == PerCPUCGroupStorage } // canStoreMap returns true if the map type accepts a map fd // for update and returns a map id for lookup. func (mt MapType) canStoreMap() bool { return mt == ArrayOfMaps || mt == HashOfMaps } // canStoreProgram returns true if the map type accepts a program fd // for update and returns a program id for lookup. func (mt MapType) canStoreProgram() bool { return mt == ProgramArray } // ProgramType of the eBPF program type ProgramType uint32 // eBPF program types const ( UnspecifiedProgram ProgramType = iota SocketFilter Kprobe SchedCLS SchedACT TracePoint XDP PerfEvent CGroupSKB CGroupSock LWTIn LWTOut LWTXmit SockOps SkSKB CGroupDevice SkMsg RawTracepoint CGroupSockAddr LWTSeg6Local LircMode2 SkReuseport FlowDissector CGroupSysctl RawTracepointWritable CGroupSockopt Tracing StructOps Extension LSM SkLookup Syscall ) // AttachType of the eBPF program, needed to differentiate allowed context accesses in // some newer program types like CGroupSockAddr. Should be set to AttachNone if not required. // Will cause invalid argument (EINVAL) at program load time if set incorrectly. type AttachType uint32 //go:generate stringer -type AttachType -trimprefix Attach // AttachNone is an alias for AttachCGroupInetIngress for readability reasons. const AttachNone AttachType = 0 const ( AttachCGroupInetIngress AttachType = iota AttachCGroupInetEgress AttachCGroupInetSockCreate AttachCGroupSockOps AttachSkSKBStreamParser AttachSkSKBStreamVerdict AttachCGroupDevice AttachSkMsgVerdict AttachCGroupInet4Bind AttachCGroupInet6Bind AttachCGroupInet4Connect AttachCGroupInet6Connect AttachCGroupInet4PostBind AttachCGroupInet6PostBind AttachCGroupUDP4Sendmsg AttachCGroupUDP6Sendmsg AttachLircMode2 AttachFlowDissector AttachCGroupSysctl AttachCGroupUDP4Recvmsg AttachCGroupUDP6Recvmsg AttachCGroupGetsockopt AttachCGroupSetsockopt AttachTraceRawTp AttachTraceFEntry AttachTraceFExit AttachModifyReturn AttachLSMMac AttachTraceIter AttachCgroupInet4GetPeername AttachCgroupInet6GetPeername AttachCgroupInet4GetSockname AttachCgroupInet6GetSockname AttachXDPDevMap AttachCgroupInetSockRelease AttachXDPCPUMap AttachSkLookup AttachXDP AttachSkSKBVerdict AttachSkReuseportSelect AttachSkReuseportSelectOrMigrate AttachPerfEvent AttachTraceKprobeMulti ) // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command type AttachFlags uint32 // PinType determines whether a map is pinned into a BPFFS. type PinType int // Valid pin types. // // Mirrors enum libbpf_pin_type. const ( PinNone PinType = iota // Pin an object by using its name as the filename. PinByName ) // LoadPinOptions control how a pinned object is loaded. type LoadPinOptions struct { // Request a read-only or write-only object. The default is a read-write // object. Only one of the flags may be set. ReadOnly bool WriteOnly bool // Raw flags for the syscall. Other fields of this struct take precedence. Flags uint32 } // Marshal returns a value suitable for BPF_OBJ_GET syscall file_flags parameter. func (lpo *LoadPinOptions) Marshal() uint32 { if lpo == nil { return 0 } flags := lpo.Flags if lpo.ReadOnly { flags |= unix.BPF_F_RDONLY } if lpo.WriteOnly { flags |= unix.BPF_F_WRONLY } return flags } // BatchOptions batch map operations options // // Mirrors libbpf struct bpf_map_batch_opts // Currently BPF_F_FLAG is the only supported // flag (for ElemFlags). type BatchOptions struct { ElemFlags uint64 Flags uint64 } // LogLevel controls the verbosity of the kernel's eBPF program verifier. // These constants can be used for the ProgramOptions.LogLevel field. type LogLevel = sys.LogLevel const ( // Print verifier state at branch points. LogLevelBranch = sys.BPF_LOG_LEVEL1 // Print verifier state for every instruction. // Available since Linux v5.2. LogLevelInstruction = sys.BPF_LOG_LEVEL2 // Print verifier errors and stats at the end of the verification process. // Available since Linux v5.2. LogLevelStats = sys.BPF_LOG_STATS ) golang-github-cilium-ebpf-0.11.0/types_string.go000066400000000000000000000073051456504511400216050ustar00rootroot00000000000000// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,PinType"; DO NOT EDIT. package ebpf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[UnspecifiedMap-0] _ = x[Hash-1] _ = x[Array-2] _ = x[ProgramArray-3] _ = x[PerfEventArray-4] _ = x[PerCPUHash-5] _ = x[PerCPUArray-6] _ = x[StackTrace-7] _ = x[CGroupArray-8] _ = x[LRUHash-9] _ = x[LRUCPUHash-10] _ = x[LPMTrie-11] _ = x[ArrayOfMaps-12] _ = x[HashOfMaps-13] _ = x[DevMap-14] _ = x[SockMap-15] _ = x[CPUMap-16] _ = x[XSKMap-17] _ = x[SockHash-18] _ = x[CGroupStorage-19] _ = x[ReusePortSockArray-20] _ = x[PerCPUCGroupStorage-21] _ = x[Queue-22] _ = x[Stack-23] _ = x[SkStorage-24] _ = x[DevMapHash-25] _ = x[StructOpsMap-26] _ = x[RingBuf-27] _ = x[InodeStorage-28] _ = x[TaskStorage-29] } const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorage" var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290} func (i MapType) String() string { if i >= MapType(len(_MapType_index)-1) { return "MapType(" + strconv.FormatInt(int64(i), 10) + ")" } return _MapType_name[_MapType_index[i]:_MapType_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[UnspecifiedProgram-0] _ = x[SocketFilter-1] _ = x[Kprobe-2] _ = x[SchedCLS-3] _ = x[SchedACT-4] _ = x[TracePoint-5] _ = x[XDP-6] _ = x[PerfEvent-7] _ = x[CGroupSKB-8] _ = x[CGroupSock-9] _ = x[LWTIn-10] _ = x[LWTOut-11] _ = x[LWTXmit-12] _ = x[SockOps-13] _ = x[SkSKB-14] _ = x[CGroupDevice-15] _ = x[SkMsg-16] _ = x[RawTracepoint-17] _ = x[CGroupSockAddr-18] _ = x[LWTSeg6Local-19] _ = x[LircMode2-20] _ = x[SkReuseport-21] _ = x[FlowDissector-22] _ = x[CGroupSysctl-23] _ = x[RawTracepointWritable-24] _ = x[CGroupSockopt-25] _ = x[Tracing-26] _ = x[StructOps-27] _ = x[Extension-28] _ = x[LSM-29] _ = x[SkLookup-30] _ = x[Syscall-31] } const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupSyscall" var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294, 301} func (i ProgramType) String() string { if i >= ProgramType(len(_ProgramType_index)-1) { return "ProgramType(" + strconv.FormatInt(int64(i), 10) + ")" } return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[PinNone-0] _ = x[PinByName-1] } const _PinType_name = "PinNonePinByName" var _PinType_index = [...]uint8{0, 7, 16} func (i PinType) String() string { if i < 0 || i >= PinType(len(_PinType_index)-1) { return "PinType(" + strconv.FormatInt(int64(i), 10) + ")" } return _PinType_name[_PinType_index[i]:_PinType_index[i+1]] }