pax_global_header00006660000000000000000000000064141452455570014526gustar00rootroot0000000000000052 comment=bf2a39333dba0294ed09f5a0c54816b721c54c9d golang-github-cilium-ebpf-0.7.0/000077500000000000000000000000001414524555700164535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/.clang-format000066400000000000000000000007531414524555700210330ustar00rootroot00000000000000--- 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 ... golang-github-cilium-ebpf-0.7.0/.github/000077500000000000000000000000001414524555700200135ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/.github/images/000077500000000000000000000000001414524555700212605ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/.github/images/cilium-ebpf.png000066400000000000000000001151661414524555700241740ustar00rootroot00000000000000PNG  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.7.0/.gitignore000066400000000000000000000003161414524555700204430ustar00rootroot00000000000000# 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.7.0/.golangci.yaml000066400000000000000000000007131414524555700212010ustar00rootroot00000000000000--- issues: exclude-rules: # syscall param structs will have unused fields in Go code. - path: syscall.*.go linters: - structcheck linters: disable-all: true enable: - deadcode - errcheck - goimports - gosimple - govet - ineffassign - misspell - staticcheck - structcheck - typecheck - unused - varcheck # Could be enabled later: # - gocyclo # - maligned # - gosec golang-github-cilium-ebpf-0.7.0/.semaphore/000077500000000000000000000000001414524555700205145ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/.semaphore/semaphore.yml000066400000000000000000000052661414524555700232330ustar00rootroot00000000000000version: v1.0 name: CI Build agent: machine: type: e1-standard-2 os_image: ubuntu2004 global_job_config: secrets: - name: Coveralls auto_cancel: running: when: "branch != 'master'" blocks: - name: Run tests task: prologue: commands: - checkout - curl -sSfL http://security.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20210119~20.04.2_all.deb -o /tmp/ca-certificates.deb && sudo dpkg -i /tmp/ca-certificates.deb - sudo mkdir -p /usr/local/golang/1.17 && curl -fL "https://golang.org/dl/go1.17.linux-amd64.tar.gz" | sudo tar -xz -C /usr/local/golang/1.17 - sem-version go 1.17 - go install github.com/mattn/goveralls@latest - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.42.1 - export PATH="$PATH:$(go env GOPATH)/bin" - cache restore - go mod tidy - 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 - sudo dmesg -C epilogue: commands: - sudo dmesg - cache store env_vars: - name: TMPDIR value: /tmp jobs: - name: Build commands: - pushd ./examples; go build -v -o "$(mktemp -d)" ./...; popd - ( 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 ) - name: Lint commands: - golangci-lint run - go generate ./cmd/bpf2go - pushd ./examples; go generate ./...; go build ./...; popd - git diff --exit-code || { echo "generated files are not up to date" >&2; false; } - name: Run unit tests on previous stable Go commands: - sem-version go 1.16 - go test -v ./cmd/bpf2go -run TestRun - timeout -s KILL 600s ./run-tests.sh 5.10 - name: Run unit tests matrix: - env_var: KERNEL_VERSION values: ["5.10", "5.4", "4.19", "4.9"] commands: - timeout -s KILL 600s ./run-tests.sh $KERNEL_VERSION - goveralls -coverprofile="coverage.out" -service=semaphore -repotoken "$COVERALLS_TOKEN" || echo Submission to coveralls failed golang-github-cilium-ebpf-0.7.0/ARCHITECTURE.md000066400000000000000000000067741414524555700206750ustar00rootroot00000000000000Architecture of the library === ELF -> Specifications -> Objects -> Links 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 (aka 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. ### BTF The BPF Type Format describes more than just the types used by a BPF program. It includes debug aids like which source line corresponds to which instructions and what global variables are used. [BTF parsing](internal/btf/) lives in a separate internal package since exposing it would mean an additional maintenance burden, and because the API still has sharp corners. The most important concept is the `btf.Type` interface, which also describes things that aren't really types like `.rodata` or `.bss` sections. `btf.Type`s can form cyclical graphs, which can easily lead to infinite loops if one is not careful. Hopefully a safe pattern to work with `btf.Type` emerges as we write more code that deals with it. Specifications --- `CollectionSpec`, `ProgramSpec` and `MapSpec` are blueprints for in-kernel objects and contain everything necessary to execute the relevant `bpf(2)` syscalls. Since the ELF reader outputs a `CollectionSpec` it's possible to modify clang-compiled BPF code, for example to rewrite constants. At the same time the [asm](asm/) package provides an assembler that can be used to generate `ProgramSpec` on the fly. Creating a spec should never require any privileges or be restricted in any way, for example by only allowing programs in native endianness. This ensures that the library stays flexible. Objects --- `Program` and `Map` are the result of loading specs into the kernel. 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 --- BPF 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. golang-github-cilium-ebpf-0.7.0/CODE_OF_CONDUCT.md000066400000000000000000000062551414524555700212620ustar00rootroot00000000000000# 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.7.0/CONTRIBUTING.md000066400000000000000000000026101414524555700207030ustar00rootroot00000000000000# 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. New features must be accompanied by tests. Before starting work on any large feature, please [join](https://ebpf.io/slack) the [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack to discuss the design first. When submitting pull requests, consider writing details about what problem you are solving and why the proposed approach solves that problem in commit messages and/or pull request description to help future library users and maintainers to reason about the proposed changes. ## 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 go test ./link ``` golang-github-cilium-ebpf-0.7.0/LICENSE000066400000000000000000000021661414524555700174650ustar00rootroot00000000000000MIT 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.7.0/Makefile000066400000000000000000000044271414524555700201220ustar00rootroot00000000000000# 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-12 CFLAGS := -target bpf -O2 -g -Wall -Werror $(CFLAGS) # 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}) 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/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 \ internal/btf/testdata/relocs .PHONY: all clean docker-all docker-shell .DEFAULT_TARGET = docker-all # Build all ELF binaries using a Dockerized LLVM toolchain. docker-all: docker run --rm --user "${UIDGID}" \ -v "${REPODIR}":/ebpf -w /ebpf --env MAKEFLAGS \ --env CFLAGS="-fdebug-prefix-map=/ebpf=." \ "${IMAGE}:${VERSION}" \ make all # (debug) Drop the user into a shell inside the Docker container as root. docker-shell: docker run --rm -ti \ -v "${REPODIR}":/ebpf -w /ebpf \ "${IMAGE}:${VERSION}" clean: -$(RM) testdata/*.elf -$(RM) internal/btf/testdata/*.elf all: $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) ln -srf testdata/loader-$(CLANG)-el.elf testdata/loader-el.elf ln -srf testdata/loader-$(CLANG)-eb.elf testdata/loader-eb.elf testdata/loader-%-el.elf: testdata/loader.c $* $(CFLAGS) -mlittle-endian -c $< -o $@ testdata/loader-%-eb.elf: testdata/loader.c $* $(CFLAGS) -mbig-endian -c $< -o $@ %-el.elf: %.c $(CLANG) $(CFLAGS) -mlittle-endian -c $< -o $@ %-eb.elf : %.c $(CLANG) $(CFLAGS) -mbig-endian -c $< -o $@ # Usage: make VMLINUX=/path/to/vmlinux vmlinux-btf .PHONY: vmlinux-btf vmlinux-btf: internal/btf/testdata/vmlinux-btf.gz internal/btf/testdata/vmlinux-btf.gz: $(VMLINUX) objcopy --dump-section .BTF=/dev/stdout "$<" /dev/null | gzip > "$@" golang-github-cilium-ebpf-0.7.0/README.md000066400000000000000000000046541414524555700177430ustar00rootroot00000000000000# 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 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. The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Cilium](https://www.cilium.io). See [ebpf.io](https://ebpf.io) for other projects from the 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 are highly encouraged, as they highlight certain use cases of eBPF and the library, and help shape the future of the project. ## Getting Help Please [join](https://ebpf.io/slack) the [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you have questions regarding the library. ## 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 ## 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 LTS releases. ## 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. 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.7.0/asm/000077500000000000000000000000001414524555700172335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/asm/alu.go000066400000000000000000000062461414524555700203530ustar00rootroot00000000000000package 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.7.0/asm/alu_string.go000066400000000000000000000045071414524555700217370ustar00rootroot00000000000000// 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.7.0/asm/doc.go000066400000000000000000000000761414524555700203320ustar00rootroot00000000000000// Package asm is an assembler for eBPF bytecode. package asm golang-github-cilium-ebpf-0.7.0/asm/dsl_test.go000066400000000000000000000015261414524555700214070ustar00rootroot00000000000000package 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, }}, } for _, tc := range testcases { if tc.have != tc.want { t.Errorf("%s: have %v, want %v", tc.name, tc.have, tc.want) } } } golang-github-cilium-ebpf-0.7.0/asm/func.go000066400000000000000000000067651414524555700205330ustar00rootroot00000000000000package asm //go:generate stringer -output func_string.go -type=BuiltinFunc // BuiltinFunc is a built-in eBPF function. type BuiltinFunc int32 // eBPF built-in functions // // You can regenerate this list using the following gawk script: // // /FN\(.+\),/ { // match($1, /\((.+)\)/, 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 ) // 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.7.0/asm/func_string.go000066400000000000000000000206701414524555700221100ustar00rootroot00000000000000// 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] } const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysClose" 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} 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.7.0/asm/instruction.go000066400000000000000000000305041414524555700221450ustar00rootroot00000000000000package asm import ( "crypto/sha1" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "math" "strings" "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 // 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 Reference string Symbol string } // Sym creates a symbol. func (ins Instruction) Sym(name string) Instruction { ins.Symbol = name return ins } // Unmarshal decodes a BPF instruction. func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) { var bi bpfInstruction err := binary.Read(r, bo, &bi) if err != nil { return 0, err } ins.OpCode = bi.OpCode ins.Offset = bi.Offset ins.Constant = int64(bi.Constant) ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo) if err != nil { return 0, fmt.Errorf("can't unmarshal registers: %s", err) } if !bi.OpCode.IsDWordLoad() { return InstructionSize, nil } var bi2 bpfInstruction if err := binary.Read(r, bo, &bi2); err != nil { // No Wrap, to avoid io.EOF clash return 0, errors.New("64bit immediate is missing second half") } if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 { return 0, errors.New("64bit immediate has non-zero fields") } ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant))) 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) } bpfi := bpfInstruction{ ins.OpCode, regs, ins.Offset, cons, } if err := binary.Write(w, bo, &bpfi); err != nil { return 0, err } if !isDWordLoad { return InstructionSize, nil } bpfi = bpfInstruction{ Constant: int32(ins.Constant >> 32), } if err := binary.Write(w, bo, &bpfi); err != nil { return 0, err } return 2 * InstructionSize, nil } // RewriteMapPtr changes an instruction to use a new map fd. // // Returns an error if the instruction doesn't load a map. func (ins *Instruction) RewriteMapPtr(fd int) error { if !ins.OpCode.IsDWordLoad() { return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) } if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue { return errors.New("not a load from a map") } // Preserve the offset value for direct map loads. offset := uint64(ins.Constant) & (math.MaxUint32 << 32) rawFd := uint64(uint32(fd)) ins.Constant = int64(offset | rawFd) return nil } // MapPtr returns the map fd for this instruction. // // The result is undefined if the instruction is not a load from a map, // see IsLoadFromMap. func (ins *Instruction) MapPtr() int { return int(int32(uint64(ins.Constant) & math.MaxUint32)) } // 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 } // 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.MapPtr() switch ins.Src { case PseudoMapFD: fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) case PseudoMapValue: 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(); cls { case LdClass, LdXClass, StClass, StXClass: 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 ALU64Class, ALUClass: 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 JumpClass: switch jop := op.JumpOp(); jop { case Call: if ins.Src == PseudoCall { // bpf-to-bpf call fmt.Fprint(f, ins.Constant) } else { 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) } } // Instructions is an eBPF program. type Instructions []Instruction func (insns Instructions) String() string { return fmt.Sprint(insns) } // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd. // // Returns an error if the symbol isn't used, see IsUnreferencedSymbol. func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { if symbol == "" { return errors.New("empty symbol") } found := false for i := range insns { ins := &insns[i] if ins.Reference != symbol { continue } if err := ins.RewriteMapPtr(fd); err != nil { return err } found = true } if !found { return &unreferencedSymbolError{symbol} } 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 } // 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) } fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) } } // Marshal encodes a BPF program into the kernel format. func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { for i, ins := range insns { _, err := ins.Marshal(w, bo) if 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 } // 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 bpfInstruction struct { OpCode OpCode Registers bpfRegisters Offset int16 Constant int32 } 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) } } func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) { switch bo { case binary.LittleEndian: return Register(r & 0xF), Register(r >> 4), nil case binary.BigEndian: return Register(r >> 4), Register(r & 0xf), nil default: return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo) } } type unreferencedSymbolError struct { symbol string } func (use *unreferencedSymbolError) Error() string { return fmt.Sprintf("unreferenced symbol %s", use.symbol) } // IsUnreferencedSymbol returns true if err was caused by // an unreferenced symbol. func IsUnreferencedSymbol(err error) bool { _, ok := err.(*unreferencedSymbolError) return ok } golang-github-cilium-ebpf-0.7.0/asm/instruction_test.go000066400000000000000000000126501414524555700232060ustar00rootroot00000000000000package asm import ( "bytes" "encoding/binary" "encoding/hex" "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 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 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.MapPtr(); 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), Return(), } insns[0].Reference = "good" 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); !IsUnreferencedSymbol(err) { t.Error("Rewriting unreferenced map doesn't return appropriate error") } } // You can use format flags to change the way an eBPF // program is stringified. func ExampleInstructions_Format() { insns := Instructions{ FnMapLookupElem.Call().Sym("my_func"), LoadImm(R0, 42, DWord), 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: // 0: Call FnMapLookupElem // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Don't indent instructions: // my_func: // 0: Call FnMapLookupElem // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Indent using spaces: // my_func: // 0: Call FnMapLookupElem // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Control symbol indentation: // my_func: // 0: Call FnMapLookupElem // 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) } } } golang-github-cilium-ebpf-0.7.0/asm/jump.go000066400000000000000000000052751414524555700205460ustar00rootroot00000000000000package 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 dst to value, and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { if op == Exit || op == Call || op == Ja { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(ImmSource), Dst: dst, Offset: -1, Constant: int64(value), Reference: label, } } // Reg compares dst to src, and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Reg(dst, src Register, label string) Instruction { if op == Exit || op == Call || op == Ja { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(RegSource), Dst: dst, Src: src, Offset: -1, Reference: label, } } // 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, Reference: label, } } return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(op), Offset: -1, Reference: label, } } golang-github-cilium-ebpf-0.7.0/asm/jump_string.go000066400000000000000000000022761414524555700221320ustar00rootroot00000000000000// 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.7.0/asm/load_store.go000066400000000000000000000114611414524555700217200ustar00rootroot00000000000000package 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.7.0/asm/load_store_string.go000066400000000000000000000031431414524555700233040ustar00rootroot00000000000000// 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.7.0/asm/opcode.go000066400000000000000000000124501414524555700210350ustar00rootroot00000000000000package asm import ( "fmt" "strings" ) //go:generate stringer -output opcode_string.go -type=Class type encoding int const ( unknownEncoding encoding = iota loadOrStore jumpOrALU ) // Class of operations // // msb lsb // +---+--+---+ // | ?? |CLS| // +---+--+---+ type Class uint8 const classMask OpCode = 0x07 const ( // LdClass load memory LdClass Class = 0x00 // LdXClass load memory from constant LdXClass Class = 0x01 // StClass load register from memory StClass Class = 0x02 // StXClass load register from constant StXClass Class = 0x03 // ALUClass arithmetic operators ALUClass Class = 0x04 // JumpClass jump operators JumpClass Class = 0x05 // ALU64Class arithmetic in 64 bit mode ALU64Class Class = 0x07 ) func (cls Class) encoding() encoding { switch cls { case LdClass, LdXClass, StClass, StXClass: return loadOrStore case ALU64Class, ALUClass, JumpClass: return jumpOrALU default: return unknownEncoding } } // 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().encoding() != loadOrStore { return InvalidMode } return Mode(op & modeMask) } // Size returns the size for load and store operations. func (op OpCode) Size() Size { if op.Class().encoding() != loadOrStore { return InvalidSize } return Size(op & sizeMask) } // Source returns the source for branch and ALU operations. func (op OpCode) Source() Source { if op.Class().encoding() != jumpOrALU || op.ALUOp() == Swap { return InvalidSource } return Source(op & sourceMask) } // ALUOp returns the ALUOp. func (op OpCode) ALUOp() ALUOp { if op.Class().encoding() != jumpOrALU { 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. func (op OpCode) JumpOp() JumpOp { if op.Class().encoding() != jumpOrALU { return InvalidJumpOp } return JumpOp(op & jumpMask) } // 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().encoding() != loadOrStore || !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().encoding() != loadOrStore || !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().encoding() != jumpOrALU || !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 { class := op.Class() if (class != ALUClass && class != ALU64Class) || !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() != JumpClass || !valid(OpCode(jump), jumpMask) { return InvalidOpCode } return (op & ^jumpMask) | OpCode(jump) } func (op OpCode) String() string { var f strings.Builder switch class := op.Class(); class { case LdClass, LdXClass, StClass, StXClass: 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 ALU64Class, ALUClass: 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 JumpClass: f.WriteString(op.JumpOp().String()) 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.7.0/asm/opcode_string.go000066400000000000000000000015121414524555700224200ustar00rootroot00000000000000// 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[ALU64Class-7] } const ( _Class_name_0 = "LdClassLdXClassStClassStXClassALUClassJumpClass" _Class_name_1 = "ALU64Class" ) var ( _Class_index_0 = [...]uint8{0, 7, 15, 22, 30, 38, 47} ) func (i Class) String() string { switch { case 0 <= i && i <= 5: return _Class_name_0[_Class_index_0[i]:_Class_index_0[i+1]] case i == 7: return _Class_name_1 default: return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-cilium-ebpf-0.7.0/asm/register.go000066400000000000000000000014001414524555700214010ustar00rootroot00000000000000package 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 ) func (r Register) String() string { v := uint8(r) if v == 10 { return "rfp" } return fmt.Sprintf("r%d", v) } golang-github-cilium-ebpf-0.7.0/attachtype_string.go000066400000000000000000000053511414524555700225420ustar00rootroot00000000000000// 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] } const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEvent" 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} 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.7.0/cmd/000077500000000000000000000000001414524555700172165ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/000077500000000000000000000000001414524555700203755ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/api_test.go000066400000000000000000000011711414524555700225340ustar00rootroot00000000000000package main import ( "testing" // Raise RLIMIT_MEMLOCK _ "github.com/cilium/ebpf/internal/testutils" ) func TestLoadingSpec(t *testing.T) { spec, err := loadExample() if err != nil { t.Fatal("Can't load spec:", err) } if spec == nil { t.Fatal("Got a nil spec") } } func TestLoadingObjects(t *testing.T) { var objs exampleObjects if err := loadExampleObjects(&objs, nil); err != nil { t.Fatal("Can't load objects:", err) } defer objs.Close() if objs.Filter == nil { t.Error("Loading returns an object with nil programs") } if objs.Map1 == nil { t.Error("Loading returns an object with nil maps") } } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/compile.go000066400000000000000000000111611414524555700223540ustar00rootroot00000000000000package 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\""), ) cmd.Dir = args.dir var depRd, depWr *os.File if args.dep != nil { depRd, depWr, err = os.Pipe() if err != nil { return err } defer depRd.Close() defer depWr.Close() // This becomes /dev/fd/3 cmd.ExtraFiles = append(cmd.ExtraFiles, depWr) 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 our pipe "-MF/dev/fd/3", ) } if err := cmd.Start(); err != nil { return fmt.Errorf("can't execute %s: %s", args.cc, err) } if depRd != nil { // Close our copy of the write end so that Copy will terminate // when cc exits. depWr.Close() if _, err := io.Copy(args.dep, depRd); err != nil { return fmt.Errorf("error writing depfile: %w", err) } } if err := cmd.Wait(); err != nil { return fmt.Errorf("%s: %s", args.cc, 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 } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/compile_test.go000066400000000000000000000063011414524555700234130ustar00rootroot00000000000000package 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: "clang-9", 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) { dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) err := compile(compileArgs{ cc: "clang-9", 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: "clang-9", 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: "clang-9", 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.7.0/cmd/bpf2go/doc.go000066400000000000000000000023011414524555700214650ustar00rootroot00000000000000// Program bpf2go embeds eBPF in Go. // // It 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. // // Requires at least clang 9. // // For a full list of accepted options check the `-help` output. There is a // fully working example at https://github.com/cilium/ebpf/blob/master/cmd/bpf2go/example_test.go. package main golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/main.go000066400000000000000000000212621414524555700216530ustar00rootroot00000000000000package main import ( "bytes" "errors" "flag" "fmt" "go/token" "io" "os" "path/filepath" "sort" "strings" ) 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. 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"}, "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 := bpf2go{ stdout: stdout, pkg: pkg, outputDir: outputDir, } fs := flag.NewFlagSet("bpf2go", flag.ContinueOnError) fs.StringVar(&b2g.cc, "cc", "clang", "`binary` used to compile C to BPF") flagCFlags := fs.String("cflags", "", "flags passed to the compiler, may contain quoted arguments") fs.StringVar(&b2g.tags, "tags", "", "list of Go build tags to include in generated files") flagTarget := fs.String("target", "bpfel,bpfeb", "clang target to compile for") fs.StringVar(&b2g.makeBase, "makebase", "", "write make compatible depinfo files relative to `directory`") fs.SetOutput(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); errors.Is(err, flag.ErrHelp) { return nil } else if err != nil { return err } if b2g.pkg == "" { return errors.New("missing package, are you running via go generate?") } if b2g.cc == "" { return errors.New("no compiler specified") } args, cFlags := splitCFlagsFromArgs(fs.Args()) if *flagCFlags != "" { splitCFlags, err := splitArguments(*flagCFlags) if err != nil { return 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 fmt.Errorf("use -makebase instead of %q", cFlag) } } b2g.cFlags = cFlags[:len(cFlags):len(cFlags)] if len(args) < 2 { return errors.New("expected at least two arguments") } b2g.ident = args[0] if !token.IsIdentifier(b2g.ident) { return fmt.Errorf("%q is not a valid identifier", b2g.ident) } input := args[1] if _, err := os.Stat(input); os.IsNotExist(err) { return fmt.Errorf("file %s doesn't exist", input) } else if err != nil { return fmt.Errorf("state %s: %s", input, err) } b2g.sourceFile, err = filepath.Abs(input) if err != nil { return err } if b2g.makeBase != "" { b2g.makeBase, err = filepath.Abs(b2g.makeBase) if err != nil { return err } } if strings.ContainsRune(b2g.tags, '\n') { return fmt.Errorf("-tags mustn't contain new line characters") } targetArches := strings.Split(*flagTarget, ",") if len(targetArches) == 0 { return fmt.Errorf("no targets specified") } targets, err := collectTargets(targetArches) if errors.Is(err, errInvalidTarget) { printTargets(stdout) fmt.Fprintln(stdout) return err } if err != nil { return err } for target, arches := range targets { if err := b2g.convert(target, arches); err != nil { return err } } return nil } 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 // Valid go package name. pkg string // Valid go identifier. ident string // C compiler. cc string // C flags passed to the compiler. cFlags []string // Go tags included in the .go tags string // Base directory of the Makefile. Enables outputting make-style dependencies // in .d files. makeBase string } func (b2g *bpf2go) convert(tgt target, arches []string) (err error) { removeOnError := func(f *os.File) { if err != nil { os.Remove(f.Name()) } f.Close() } stem := fmt.Sprintf("%s_%s", strings.ToLower(b2g.ident), tgt.clang) if tgt.linux != "" { stem = fmt.Sprintf("%s_%s_%s", strings.ToLower(b2g.ident), tgt.clang, tgt.linux) } objFileName := filepath.Join(b2g.outputDir, stem+".o") cwd, err := os.Getwd() if err != nil { return err } var tags []string if len(arches) > 0 { tags = append(tags, strings.Join(arches, " ")) } if b2g.tags != "" { tags = append(tags, b2g.tags) } 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) // Write out generated go goFileName := filepath.Join(b2g.outputDir, stem+".go") goFile, err := os.Create(goFileName) if err != nil { return err } defer removeOnError(goFile) obj, err := os.Open(objFileName) if err != nil { return err } defer obj.Close() err = writeCommon(writeArgs{ pkg: b2g.pkg, ident: b2g.ident, tags: tags, obj: 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 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.7.0/cmd/bpf2go/main_test.go000066400000000000000000000106661414524555700227200ustar00rootroot00000000000000package main import ( "bytes" "fmt" "io" "os" "os/exec" "path/filepath" "sort" "strings" "testing" "github.com/google/go-cmp/cmp" ) func TestRun(t *testing.T) { if testing.Short() { t.Skip("Not compiling with -short") } 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) } } 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", ebpfModule), // Replace the module with the current version. fmt.Sprintf("-replace=%s=%s", ebpfModule, modRoot), ) err = run(io.Discard, "foo", tmpDir, []string{ "-cc", "clang-9", "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 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]) } 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"], }, }, } 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, ident: "test", cc: "clang-9", sourceFile: tmp + "/test.c", outputDir: tmp, } if err := b2g.convert(targetByGoArch["amd64"], nil); err != nil { t.Fatal("Can't target GOARCH:", err) } } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/output.go000066400000000000000000000162531414524555700222730ustar00rootroot00000000000000package main import ( "bytes" "errors" "fmt" "go/format" "go/scanner" "go/token" "io" "os" "path/filepath" "strings" "text/template" "unicode" "github.com/cilium/ebpf" ) const ebpfModule = "github.com/cilium/ebpf" const commonRaw = `// Code generated by bpf2go; DO NOT EDIT. {{- range .Tags }} // +build {{ . }} {{- end }} package {{ .Package }} import ( "bytes" _ "embed" "fmt" "io" "{{ .Module }}" ) // {{ .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 {{ tag $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 {{ tag $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 {{ tag $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 {{ tag $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 ` var ( tplFuncs = map[string]interface{}{ "tag": tag, } commonTemplate = template.Must(template.New("common").Funcs(tplFuncs).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 n.maybeExport(string(n) + "Specs") } func (n templateName) ProgramSpecs() string { return n.maybeExport(string(n) + "ProgramSpecs") } func (n templateName) MapSpecs() string { return n.maybeExport(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 n.maybeExport(string(n) + "Objects") } func (n templateName) Maps() string { return n.maybeExport(string(n) + "Maps") } func (n templateName) Programs() string { return n.maybeExport(string(n) + "Programs") } func (n templateName) CloseHelper() string { return "_" + toUpperFirst(string(n)) + "Close" } type writeArgs struct { pkg string ident string tags []string obj string out io.Writer } func writeCommon(args writeArgs) error { obj, err := os.ReadFile(args.obj) if err != nil { return fmt.Errorf("read object file contents: %s", err) } spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(obj)) if err != nil { return fmt.Errorf("can't load BPF from ELF: %s", err) } maps := make(map[string]string) for name := range spec.Maps { if strings.HasPrefix(name, ".") { // Skip .rodata, .data, .bss, etc. sections continue } maps[name] = identifier(name) } programs := make(map[string]string) for name := range spec.Programs { programs[name] = identifier(name) } ctx := struct { Module string Package string Tags []string Name templateName Maps map[string]string Programs map[string]string File string }{ ebpfModule, args.pkg, args.tags, templateName(args.ident), maps, programs, filepath.Base(args.obj), } var buf bytes.Buffer if err := commonTemplate.Execute(&buf, &ctx); err != nil { return fmt.Errorf("can't generate types: %s", err) } return writeFormatted(buf.Bytes(), args.out) } 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 } 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) } func tag(str string) string { return "`ebpf:\"" + str + "\"`" } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/output_test.go000066400000000000000000000010221414524555700233160ustar00rootroot00000000000000package main import ( "testing" ) 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) } } } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/testdata/000077500000000000000000000000001414524555700222065ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/testdata/minimal.c000066400000000000000000000004121414524555700237750ustar00rootroot00000000000000#include "../../../testdata/common.h" char __license[] __section("license") = "MIT"; struct bpf_map_def map1 __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = 4, .value_size = 4, .max_entries = 1, }; __section("socket") int filter() { return 0; } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/tools.go000066400000000000000000000024241414524555700220660ustar00rootroot00000000000000package main import ( "errors" "fmt" "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:] } golang-github-cilium-ebpf-0.7.0/cmd/bpf2go/tools_test.go000066400000000000000000000016501414524555700231250ustar00rootroot00000000000000package 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.7.0/collection.go000066400000000000000000000417661414524555700211530ustar00rootroot00000000000000package ebpf import ( "encoding/binary" "errors" "fmt" "io" "math" "reflect" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" ) // CollectionOptions control loading a collection into the kernel. // // Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. type CollectionOptions struct { Maps MapOptions Programs ProgramOptions } // CollectionSpec describes a collection. type CollectionSpec struct { Maps map[string]*MapSpec Programs map[string]*ProgramSpec // 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, } 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. 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 fd := m.FD() for progName, progSpec := range cs.Programs { err := progSpec.Instructions.RewriteMapPtr(symbol, fd) switch { case err == nil: seen = true case asm.IsUnreferencedSymbol(err): // 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 } // 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 if a constant doesn't exist. func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { rodata := cs.Maps[".rodata"] if rodata == nil { return errors.New("missing .rodata section") } if rodata.BTF == nil { return errors.New(".rodata section has no BTF") } if n := len(rodata.Contents); n != 1 { return fmt.Errorf("expected one key in .rodata, found %d", n) } kv := rodata.Contents[0] value, ok := kv.Value.([]byte) if !ok { return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value) } buf := make([]byte, len(value)) copy(buf, value) err := patchValue(buf, rodata.BTF.Value, consts) if err != nil { return err } rodata.Contents[0] = MapKV{kv.Key, buf} 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. // // 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 := newCollectionLoader(cs, opts) defer loader.cleanup() // Support assigning Programs and Maps, lazy-loading the required objects. assignedMaps := make(map[string]bool) getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*Program)(nil)): 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. // Without any references, they will be closed on the first GC and all tail // calls into them will miss. if !assignedMaps[n] { return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n) } } } loader.finalize() 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 a specification. func NewCollection(spec *CollectionSpec) (*Collection, error) { return NewCollectionWithOptions(spec, CollectionOptions{}) } // NewCollectionWithOptions creates a Collection from a specification. func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { loader := newCollectionLoader(spec, &opts) defer loader.cleanup() // 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 := range spec.Programs { 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 } maps, progs := loader.maps, loader.programs loader.finalize() return &Collection{ progs, maps, }, nil } type handleCache struct { btfHandles map[*btf.Spec]*btf.Handle btfSpecs map[io.ReaderAt]*btf.Spec } func newHandleCache() *handleCache { return &handleCache{ btfHandles: make(map[*btf.Spec]*btf.Handle), btfSpecs: make(map[io.ReaderAt]*btf.Spec), } } func (hc handleCache) btfHandle(spec *btf.Spec) (*btf.Handle, error) { if hc.btfHandles[spec] != nil { return hc.btfHandles[spec], nil } handle, err := btf.NewHandle(spec) if err != nil { return nil, err } hc.btfHandles[spec] = handle return handle, nil } func (hc handleCache) btfSpec(rd io.ReaderAt) (*btf.Spec, error) { if hc.btfSpecs[rd] != nil { return hc.btfSpecs[rd], nil } spec, err := btf.LoadSpecFromReader(rd) if err != nil { return nil, err } hc.btfSpecs[rd] = spec return spec, nil } func (hc handleCache) close() { for _, handle := range hc.btfHandles { handle.Close() } } type collectionLoader struct { coll *CollectionSpec opts *CollectionOptions maps map[string]*Map programs map[string]*Program handles *handleCache } func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) *collectionLoader { if opts == nil { opts = &CollectionOptions{} } return &collectionLoader{ coll, opts, make(map[string]*Map), make(map[string]*Program), newHandleCache(), } } // finalize should be called when all the collectionLoader's resources // have been successfully loaded into the kernel and populated with values. func (cl *collectionLoader) finalize() { cl.maps, cl.programs = nil, nil } // cleanup cleans up all resources left over in the collectionLoader. // Call finalize() when Map and Program creation/population is successful // to prevent them from getting closed. func (cl *collectionLoader) cleanup() { cl.handles.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) } m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles) 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) } progSpec = progSpec.Copy() // Rewrite any reference to a valid map. for i := range progSpec.Instructions { ins := &progSpec.Instructions[i] if !ins.IsLoadFromMap() || ins.Reference == "" { continue } if uint32(ins.Constant) != math.MaxUint32 { // Don't overwrite maps already rewritten, users can // rewrite programs in the spec themselves continue } m, err := cl.loadMap(ins.Reference) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } fd := m.FD() if fd < 0 { return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) } if err := ins.RewriteMapPtr(m.FD()); err != nil { return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference, err) } } prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.handles) 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) } mapSpec = mapSpec.Copy() // Replace any object stubs with loaded objects. for i, kv := range mapSpec.Contents { switch v := kv.Value.(type) { case programStub: // loadProgram is idempotent and could return an existing Program. prog, err := cl.loadProgram(string(v)) if err != nil { return fmt.Errorf("loading program %s, for map %s: %w", v, mapName, err) } mapSpec.Contents[i] = MapKV{kv.Key, prog} case mapStub: // loadMap is idempotent and could return an existing Map. innerMap, err := cl.loadMap(string(v)) if err != nil { return fmt.Errorf("loading inner map %s, for map %s: %w", v, 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 } // LoadCollection parses an object file and converts it to a collection. 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.7.0/collection_test.go000066400000000000000000000173561414524555700222100ustar00rootroot00000000000000package ebpf import ( "fmt" "reflect" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) 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), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } cs.Programs["test"].Instructions[0].Reference = "my-map" 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", }, }, } 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") } } func TestCollectionSpecRewriteMaps(t *testing.T) { insns := asm.Instructions{ // R1 map asm.LoadMapPtr(asm.R1, 0), // 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().Sym("ret"), } insns[0].Reference = "test-map" 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(make([]byte, 14)) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 2 { t.Fatal("new / override map not used") } } 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) } 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 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) } fmt.Println(objs.Program.Type()) fmt.Println(objs.Map.Type()) // Output: SocketFilter // Array } golang-github-cilium-ebpf-0.7.0/doc.go000066400000000000000000000015051414524555700175500ustar00rootroot00000000000000// 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. package ebpf golang-github-cilium-ebpf-0.7.0/elf_reader.go000066400000000000000000000774011414524555700211030ustar00rootroot00000000000000package ebpf import ( "bufio" "bytes" "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" ) // 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 } // 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 } defer f.Close() 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, err := btf.LoadSpecFromReader(rd) if err != nil && !errors.Is(err, btf.ErrNotFound) { return nil, fmt.Errorf("load BTF: %w", err) } // Assign symbols to all the sections we're interested in. symbols, err := f.Symbols() if err != nil { return nil, fmt.Errorf("load symbols: %v", err) } for _, symbol := range symbols { idx := symbol.Section symType := elf.ST_TYPE(symbol.Info) section := sections[idx] if section == nil { continue } // Older versions of LLVM don't tag symbols correctly, so keep // all NOTYPE ones. keep := symType == elf.STT_NOTYPE switch section.kind { case mapSection, btfMapSection, dataSection: keep = keep || symType == elf.STT_OBJECT case programSection: keep = keep || symType == elf.STT_FUNC } if !keep || symbol.Name == "" { continue } section.symbols[symbol.Value] = symbol } ec := &elfCode{ SafeELFFile: f, sections: sections, license: license, version: version, btf: btfSpec, } // Go through relocation sections, and parse the ones for sections we're // interested in. Make sure that relocations point at valid sections. for idx, relSection := range relSections { section := sections[idx] if section == nil { continue } rels, err := ec.loadRelocations(relSection, symbols) if err != nil { return nil, fmt.Errorf("relocation for section %q: %w", section.Name, err) } for _, rel := range rels { target := sections[rel.Section] if target == nil { return nil, fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported) } if target.Flags&elf.SHF_STRINGS > 0 { return nil, fmt.Errorf("section %q: string is not stack allocated: %w", section.Name, ErrNotSupported) } target.references++ } section.relocations = rels } // Collect all the various ways to define maps. maps := make(map[string]*MapSpec) if err := ec.loadMaps(maps); err != nil { return nil, fmt.Errorf("load maps: %w", err) } if err := ec.loadBTFMaps(maps); err != nil { return nil, fmt.Errorf("load BTF maps: %w", err) } if err := ec.loadDataSections(maps); err != nil { return nil, fmt.Errorf("load data sections: %w", err) } // Finally, collect programs and link them. progs, err := ec.loadPrograms() if err != nil { return nil, fmt.Errorf("load programs: %w", err) } return &CollectionSpec{maps, progs, 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, } } func (ec *elfCode) loadPrograms() (map[string]*ProgramSpec, error) { var ( progs []*ProgramSpec libs []*ProgramSpec ) 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) } funcSym, ok := sec.symbols[0] if !ok { return nil, fmt.Errorf("section %v: no label at start", sec.Name) } insns, length, err := ec.loadInstructions(sec) if err != nil { return nil, fmt.Errorf("program %s: %w", funcSym.Name, err) } progType, attachType, progFlags, attachTo := getProgType(sec.Name) spec := &ProgramSpec{ Name: funcSym.Name, Type: progType, Flags: progFlags, AttachType: attachType, AttachTo: attachTo, License: ec.license, KernelVersion: ec.version, Instructions: insns, ByteOrder: ec.ByteOrder, } if ec.btf != nil { spec.BTF, err = ec.btf.Program(sec.Name, length) if err != nil && !errors.Is(err, btf.ErrNoExtendedInfo) { return nil, fmt.Errorf("program %s: %w", funcSym.Name, err) } } if spec.Type == UnspecifiedProgram { // There is no single name we can use for "library" sections, // since they may contain multiple functions. We'll decode the // labels they contain later on, and then link sections that way. libs = append(libs, spec) } else { progs = append(progs, spec) } } res := make(map[string]*ProgramSpec, len(progs)) for _, prog := range progs { err := link(prog, libs) if err != nil { return nil, fmt.Errorf("program %s: %w", prog.Name, err) } res[prog.Name] = prog } return res, nil } func (ec *elfCode) loadInstructions(section *elfSection) (asm.Instructions, uint64, error) { var ( r = bufio.NewReader(section.Open()) insns asm.Instructions offset uint64 ) for { var ins asm.Instruction n, err := ins.Unmarshal(r, ec.ByteOrder) if err == io.EOF { return insns, offset, nil } if err != nil { return nil, 0, fmt.Errorf("offset %d: %w", offset, err) } ins.Symbol = section.symbols[offset].Name if rel, ok := section.relocations[offset]; ok { if err = ec.relocateInstruction(&ins, rel); err != nil { return nil, 0, fmt.Errorf("offset %d: relocate instruction: %w", offset, err) } } insns = append(insns, ins) offset += n } } 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 // Mark the instruction as needing an update when creating the // collection. if err := ins.RewriteMapPtr(-1); err != nil { return err } case dataSection: var offset uint32 switch typ { case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: unsupported 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: if bind != elf.STB_GLOBAL { return fmt.Errorf("direct load: %s: unsupported 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 // Mark the instruction as needing an update when creating the // collection. if err := ins.RewriteMapPtr(-1); err != nil { return err } case programSection: if ins.OpCode.JumpOp() != asm.Call { return fmt.Errorf("not a call instruction: %s", ins) } 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 if offset < 0 { return fmt.Errorf("call: %s: invalid offset %d", name, offset) } sym, ok := target.symbols[uint64(offset)] if !ok { return fmt.Errorf("call: %s: no symbol at offset %d", name, offset) } ins.Constant = -1 name = sym.Name default: return fmt.Errorf("call: %s: invalid symbol type %s", name, typ) } 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) } // There is nothing to do here but set ins.Reference. default: return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported) } ins.Reference = name return nil } func (ec *elfCode) loadMaps(maps map[string]*MapSpec) 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 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) } 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(maps map[string]*MapSpec) 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.FindType(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 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) } 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 } // A programStub is a placeholder for a Program to be inserted at a certain map key. // It needs to be resolved into a Program later on in the loader process. type programStub string // A mapStub is a placeholder for a Map to be inserted at a certain map key. // It needs to be resolved into a Map later on in the loader process. type mapStub string // 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) } } if key == nil { key = &btf.Void{} } if value == nil { value = &btf.Void{} } return &MapSpec{ Name: SanitizeName(name, -1), Type: MapType(mapType), KeySize: keySize, ValueSize: valueSize, MaxEntries: maxEntries, Flags: flags, BTF: &btf.Map{Spec: spec, 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.OffsetBits / 8) + 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), programStub(r.Name)}) case elf.STT_OBJECT: contents = append(contents, MapKV{uint32(k), mapStub(r.Name)}) default: return nil, fmt.Errorf("unknown relocation type %v", t) } } return contents, nil } func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) 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 } if ec.btf == nil { return errors.New("data sections require BTF, make sure all consts are marked as static") } var datasec *btf.Datasec if err := ec.btf.FindType(sec.Name, &datasec); err != nil { return fmt.Errorf("data section %s: can't get BTF: %w", sec.Name, err) } 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 := &MapSpec{ Name: SanitizeName(sec.Name, -1), Type: Array, KeySize: 4, ValueSize: uint32(len(data)), MaxEntries: 1, Contents: []MapKV{{uint32(0), data}}, BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: datasec}, } switch sec.Name { case ".rodata": mapSpec.Flags = unix.BPF_F_RDONLY_PROG mapSpec.Freeze = true case ".bss": // The kernel already zero-initializes the map mapSpec.Contents = nil } maps[sec.Name] = mapSpec } return nil } func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { types := map[string]struct { progType ProgramType attachType AttachType progFlags uint32 }{ // From 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}, "seccomp": {SocketFilter, AttachNone, 0}, "kprobe/": {Kprobe, AttachNone, 0}, "uprobe/": {Kprobe, AttachNone, 0}, "kretprobe/": {Kprobe, AttachNone, 0}, "uretprobe/": {Kprobe, AttachNone, 0}, "tracepoint/": {TracePoint, AttachNone, 0}, "raw_tracepoint/": {RawTracepoint, AttachNone, 0}, "raw_tp/": {RawTracepoint, AttachNone, 0}, "tp_btf/": {Tracing, AttachTraceRawTp, 0}, "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}, "sockops": {SockOps, AttachCGroupSockOps, 0}, "sk_skb/stream_parser": {SkSKB, AttachSkSKBStreamParser, 0}, "sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser, 0}, "sk_msg": {SkMsg, AttachSkSKBStreamVerdict, 0}, "lirc_mode2": {LircMode2, AttachLircMode2, 0}, "flow_dissector": {FlowDissector, AttachFlowDissector, 0}, "iter/": {Tracing, AttachTraceIter, 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}, "sk_lookup/": {SkLookup, AttachSkLookup, 0}, "freplace/": {Extension, AttachNone, 0}, "lsm/": {LSM, AttachLSMMac, 0}, "lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE}, "cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress, 0}, "cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress, 0}, "cgroup/dev": {CGroupDevice, AttachCGroupDevice, 0}, "cgroup/skb": {CGroupSKB, AttachNone, 0}, "cgroup/sock": {CGroupSock, AttachCGroupInetSockCreate, 0}, "cgroup/post_bind4": {CGroupSock, AttachCGroupInet4PostBind, 0}, "cgroup/post_bind6": {CGroupSock, AttachCGroupInet6PostBind, 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/sysctl": {CGroupSysctl, AttachCGroupSysctl, 0}, "cgroup/getsockopt": {CGroupSockopt, AttachCGroupGetsockopt, 0}, "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0}, "classifier": {SchedCLS, AttachNone, 0}, "action": {SchedACT, AttachNone, 0}, "cgroup/getsockname4": {CGroupSockAddr, AttachCgroupInet4GetSockname, 0}, "cgroup/getsockname6": {CGroupSockAddr, AttachCgroupInet6GetSockname, 0}, "cgroup/getpeername4": {CGroupSockAddr, AttachCgroupInet4GetPeername, 0}, "cgroup/getpeername6": {CGroupSockAddr, AttachCgroupInet6GetPeername, 0}, } for prefix, t := range types { if !strings.HasPrefix(sectionName, prefix) { continue } if !strings.HasSuffix(prefix, "/") { return t.progType, t.attachType, t.progFlags, "" } return t.progType, t.attachType, t.progFlags, sectionName[len(prefix):] } return UnspecifiedProgram, AttachNone, 0, "" } func (ec *elfCode) loadRelocations(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.7.0/elf_reader_fuzz.go000066400000000000000000000005551414524555700221550ustar00rootroot00000000000000//go:build gofuzz // +build gofuzz // Use with https://github.com/dvyukov/go-fuzz package ebpf import "bytes" func FuzzLoadCollectionSpec(data []byte) int { spec, err := LoadCollectionSpecFromReader(bytes.NewReader(data)) if err != nil { if spec != nil { panic("spec is not nil") } return 0 } if spec == nil { panic("spec is nil") } return 1 } golang-github-cilium-ebpf-0.7.0/elf_reader_test.go000066400000000000000000000463361414524555700221450ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "flag" "fmt" "os" "path/filepath" "strings" "syscall" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "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" ) 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, License: "MIT", }, "no_relocation": { Name: "no_relocation", Type: SocketFilter, License: "MIT", }, "asm_relocation": { Name: "asm_relocation", Type: SocketFilter, License: "MIT", }, "data_sections": { Name: "data_sections", Type: SocketFilter, 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.Map), new(btf.Program)), cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder"), cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"), cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool { switch key { case ".bss", ".data", ".rodata": return true default: 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.Maps[".rodata"] != nil { err := have.RewriteConstants(map[string]interface{}{ "arg": uint32(1), }) 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: 1, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer coll.Close() ret, _, err := coll.Programs["xdp_prog"].Test(make([]byte, 14)) if err != nil { t.Fatal("Can't run program:", err) } if ret != 5 { t.Error("Expected return value to be 5, got", ret) } }) } 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(make([]byte, 14)) 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("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.(programStub); !ok { t.Errorf("expecting MapSpec entry Value to be of type programStub, got %T", 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") } 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.(mapStub); !ok { t.Errorf("expecting MapSpec entry Value to be of type mapStub, got %T", 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) { testutils.Files(t, testutils.Glob(t, "testdata/strings-*.elf"), func(t *testing.T, file string) { _, err := LoadCollectionSpec(file) t.Log(err) if !errors.Is(err, ErrNotSupported) { t.Error("References to a string section should be unsupported") } }) } 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: 1, }, }) 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() ret, _, err := obj.TailMain.Test(make([]byte, 14)) 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 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 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.LogSize = -1 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:", errno) } 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_sk_assign.o": t.Skip("Skipping due to incompatible struct bpf_map_def") case "test_map_in_map.o", "test_select_reuseport_kern.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") } t.Parallel() spec, err := LoadCollectionSpec(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("Can't read %s: %s", file, err) } 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 default: valid = !strings.Contains(name, "___err_") } fh, err := os.Open(coreFile) if err != nil { t.Fatal(err) } defer fh.Close() opts := opts // copy opts.Programs.TargetBTF = fh 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: "", }, "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", }, } 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.7.0/example_sock_elf_test.go000066400000000000000000000177551414524555700233600ustar00rootroot00000000000000//go:build linux // +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.7.0/example_sock_extract_dist_test.go000066400000000000000000000120161414524555700252700ustar00rootroot00000000000000package 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).Sym("ipv6"), asm.LoadAbs(-0x100000+7, asm.Byte), // stash the load result into FP[-4] asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).Sym("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()).Sym("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).Sym("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.7.0/features/000077500000000000000000000000001414524555700202715ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/features/doc.go000066400000000000000000000001471414524555700213670ustar00rootroot00000000000000// Package features allows probing for BPF features available to the calling process. package features golang-github-cilium-ebpf-0.7.0/features/map.go000066400000000000000000000115741414524555700214050ustar00rootroot00000000000000package features import ( "errors" "fmt" "os" "sync" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) func init() { mc.mapTypes = make(map[ebpf.MapType]error) } var ( mc mapCache ) type mapCache struct { sync.Mutex mapTypes map[ebpf.MapType]error } func createMapTypeAttr(mt ebpf.MapType) *internal.BPFMapCreateAttr { var ( keySize uint32 = 4 valueSize uint32 = 4 maxEntries uint32 = 1 innerMapFd uint32 flags uint32 btfKeyTypeID uint32 btfValueTypeID uint32 btfFd uint32 ) // switch on map types to generate correct bpfMapCreateAttr switch mt { case ebpf.StackTrace: // valueSize needs to be sizeof(uint64) valueSize = 8 case ebpf.LPMTrie: // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8 // BPF_F_NO_PREALLOC needs to be set // checked at allocation time for lpm_trie maps keySize = 8 valueSize = 8 flags = unix.BPF_F_NO_PREALLOC case ebpf.ArrayOfMaps, ebpf.HashOfMaps: // assign invalid innerMapFd to pass validation check // will return EBADF innerMapFd = ^uint32(0) case ebpf.CGroupStorage, ebpf.PerCPUCGroupStorage: // 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 // checked at allocation time var align int keySize = uint32(8 + unsafe.Sizeof(align)) maxEntries = 0 case ebpf.Queue, ebpf.Stack: // keySize needs to be 0, see alloc_check for queue and stack maps keySize = 0 case ebpf.RingBuf: // keySize and valueSize need to be 0 // maxEntries needs to be power of 2 and PAGE_ALIGNED // checked at allocation time keySize = 0 valueSize = 0 maxEntries = uint32(os.Getpagesize()) case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage: // 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 maxEntries = 0 flags = unix.BPF_F_NO_PREALLOC btfKeyTypeID = 1 // BTF_KIND_INT btfValueTypeID = 3 // BTF_KIND_ARRAY btfFd = ^uint32(0) } return &internal.BPFMapCreateAttr{ MapType: uint32(mt), KeySize: keySize, ValueSize: valueSize, MaxEntries: maxEntries, InnerMapFd: innerMapFd, Flags: flags, BTFKeyTypeID: btfKeyTypeID, BTFValueTypeID: btfValueTypeID, BTFFd: btfFd, } } // HaveMapType probes the running kernel for the availability of the specified map 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 map creation may succeed // despite an error being returned. Some map types cannot reliably be probed and will also // return error. Only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached and persist throughout any process capability changes. func HaveMapType(mt ebpf.MapType) error { if err := validateMaptype(mt); err != nil { return err } return haveMapType(mt) } func validateMaptype(mt ebpf.MapType) error { if mt > mt.Max() { return os.ErrInvalid } if mt == ebpf.StructOpsMap { // A probe for StructOpsMap has vmlinux BTF requirements we currently // cannot meet. Once we figure out how to add a working probe in this // package, we can remove this check. return errors.New("a probe for MapType StructOpsMap isn't implemented") } return nil } func haveMapType(mt ebpf.MapType) error { mc.Lock() defer mc.Unlock() err, ok := mc.mapTypes[mt] if ok { return err } fd, err := internal.BPFMapCreate(createMapTypeAttr(mt)) switch { // For nested and storage map types we accept EBADF as indicator that these maps are supported case errors.Is(err, unix.EBADF): if isMapOfMaps(mt) || isStorageMap(mt) { err = nil } // EINVAL occurs when attempting to create a map with an unknown type. // E2BIG occurs when BPFMapCreateAttr 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): err = ebpf.ErrNotSupported // EPERM is kept as-is and is not converted or wrapped. case errors.Is(err, unix.EPERM): break // Wrap unexpected errors. case err != nil: err = fmt.Errorf("unexpected error during feature probe: %w", err) default: fd.Close() } mc.mapTypes[mt] = err return err } func isMapOfMaps(mt ebpf.MapType) bool { switch mt { case ebpf.ArrayOfMaps, ebpf.HashOfMaps: return true } return false } func isStorageMap(mt ebpf.MapType) bool { switch mt { case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage: return true } return false } golang-github-cilium-ebpf-0.7.0/features/map_test.go000066400000000000000000000046261414524555700224440ustar00rootroot00000000000000package features import ( "fmt" "math" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) var mapTypeMinVersion = map[ebpf.MapType]string{ ebpf.Hash: "3.19", ebpf.Array: "3.19", ebpf.ProgramArray: "4.2", ebpf.PerfEventArray: "4.3", ebpf.PerCPUHash: "4.6", ebpf.PerCPUArray: "4.6", ebpf.StackTrace: "4.6", ebpf.CGroupArray: "4.8", ebpf.LRUHash: "4.10", ebpf.LRUCPUHash: "4.10", ebpf.LPMTrie: "4.11", ebpf.ArrayOfMaps: "4.12", ebpf.HashOfMaps: "4.12", ebpf.DevMap: "4.14", ebpf.SockMap: "4.14", ebpf.CPUMap: "4.15", ebpf.XSKMap: "4.18", ebpf.SockHash: "4.18", ebpf.CGroupStorage: "4.19", ebpf.ReusePortSockArray: "4.19", ebpf.PerCPUCGroupStorage: "4.20", ebpf.Queue: "4.20", ebpf.Stack: "4.20", ebpf.SkStorage: "5.2", ebpf.DevMapHash: "5.4", ebpf.StructOpsMap: "5.6", // requires vmlinux BTF, skip for now ebpf.RingBuf: "5.8", ebpf.InodeStorage: "5.10", ebpf.TaskStorage: "5.11", } func TestHaveMapType(t *testing.T) { for mt := ebpf.UnspecifiedMap + 1; mt <= mt.Max(); mt++ { minVersion, ok := mapTypeMinVersion[mt] if !ok { // In cases were a new map type wasn't added to testCases // we should make sure the test runs anyway and fails on old kernels minVersion = "0.0" } feature := fmt.Sprintf("map type %s", mt.String()) t.Run(mt.String(), func(t *testing.T) { if mt == ebpf.StructOpsMap { t.Skip("Test for map type StructOpsMap requires working probe") } testutils.SkipOnOldKernel(t, minVersion, feature) if err := HaveMapType(mt); err != nil { t.Fatalf("Map type %s isn't supported even though kernel is at least %s: %v", mt.String(), minVersion, err) } }) } } func TestHaveMapTypeUnsupported(t *testing.T) { if err := haveMapType(ebpf.MapType(math.MaxUint32)); err != ebpf.ErrNotSupported { t.Fatalf("Expected ebpf.ErrNotSupported but was: %v", err) } } func TestHaveMapTypeInvalid(t *testing.T) { if err := HaveMapType(ebpf.MapType(math.MaxUint32)); err != os.ErrInvalid { t.Fatalf("Expected os.ErrInvalid but was: %v", err) } if err := HaveMapType(ebpf.MapType(ebpf.StructOpsMap)); err == nil { t.Fatal("Expected but was nil") } } golang-github-cilium-ebpf-0.7.0/features/prog.go000066400000000000000000000101661414524555700215730ustar00rootroot00000000000000package features import ( "bytes" "errors" "fmt" "os" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) func init() { pc.progTypes = make(map[ebpf.ProgramType]error) } var ( pc progCache ) type progCache struct { sync.Mutex progTypes map[ebpf.ProgramType]error } func createProgLoadAttr(pt ebpf.ProgramType) (*internal.BPFProgLoadAttr, error) { var expectedAttachType ebpf.AttachType insns := asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize)) if err := insns.Marshal(buf, internal.NativeEndian); err != nil { return nil, err } bytecode := buf.Bytes() instructions := internal.NewSlicePointer(bytecode) // Some programs have expected attach types which are checked during the // BPD_PROG_LOAD syscall. switch pt { case ebpf.CGroupSockAddr: expectedAttachType = ebpf.AttachCGroupInet4Connect case ebpf.CGroupSockopt: expectedAttachType = ebpf.AttachCGroupGetsockopt case ebpf.SkLookup: expectedAttachType = ebpf.AttachSkLookup default: expectedAttachType = ebpf.AttachNone } // 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. v, err := internal.KernelVersion() if err != nil { return nil, fmt.Errorf("detecting kernel version: %w", err) } kv := v.Kernel() return &internal.BPFProgLoadAttr{ ProgType: uint32(pt), Instructions: instructions, InsCount: uint32(len(bytecode) / asm.InstructionSize), ExpectedAttachType: uint32(expectedAttachType), License: internal.NewStringPointer("GPL"), KernelVersion: kv, }, nil } // HaveProgType probes the running kernel for the availability of the 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. Some program types cannot reliably be probed and // will also return error. Only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached and persist throughout any process capability changes. func HaveProgType(pt ebpf.ProgramType) error { if err := validateProgType(pt); err != nil { return err } return haveProgType(pt) } func validateProgType(pt ebpf.ProgramType) error { if pt > pt.Max() { return os.ErrInvalid } if progLoadProbeNotImplemented(pt) { // A probe for a these prog types has BTF requirements we currently cannot meet // Once we figure out how to add a working probe in this package, we can remove // this check return fmt.Errorf("a probe for ProgType %s isn't implemented", pt.String()) } return nil } func haveProgType(pt ebpf.ProgramType) error { pc.Lock() defer pc.Unlock() err, ok := pc.progTypes[pt] if ok { return err } attr, err := createProgLoadAttr(pt) if err != nil { return fmt.Errorf("couldn't create the program load attribute: %w", err) } fd, err := internal.BPFProgLoad(attr) switch { // EINVAL occurs when attempting to create a program with an unknown type. // E2BIG occurs when BPFProgLoadAttr 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): err = ebpf.ErrNotSupported // EPERM is kept as-is and is not converted or wrapped. case errors.Is(err, unix.EPERM): break // Wrap unexpected errors. case err != nil: err = fmt.Errorf("unexpected error during feature probe: %w", err) default: fd.Close() } pc.progTypes[pt] = err return err } func progLoadProbeNotImplemented(pt ebpf.ProgramType) bool { switch pt { case ebpf.Tracing, ebpf.StructOps, ebpf.Extension, ebpf.LSM: return true } return false } golang-github-cilium-ebpf-0.7.0/features/prog_test.go000066400000000000000000000054301414524555700226300ustar00rootroot00000000000000package features import ( "fmt" "math" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) var progTypeMinVersion = map[ebpf.ProgramType]string{ ebpf.SocketFilter: "3.19", ebpf.Kprobe: "4.1", ebpf.SchedCLS: "4.1", ebpf.SchedACT: "4.1", ebpf.TracePoint: "4.7", ebpf.XDP: "4.8", ebpf.PerfEvent: "4.9", ebpf.CGroupSKB: "4.10", ebpf.CGroupSock: "4.10", ebpf.LWTIn: "4.10", ebpf.LWTOut: "4.10", ebpf.LWTXmit: "4.10", ebpf.SockOps: "4.13", ebpf.SkSKB: "4.14", ebpf.CGroupDevice: "4.15", ebpf.SkMsg: "4.17", ebpf.RawTracepoint: "4.17", ebpf.CGroupSockAddr: "4.17", ebpf.LWTSeg6Local: "4.18", ebpf.LircMode2: "4.18", ebpf.SkReuseport: "4.19", ebpf.FlowDissector: "4.20", ebpf.CGroupSysctl: "5.2", ebpf.RawTracepointWritable: "5.2", ebpf.CGroupSockopt: "5.3", ebpf.Tracing: "5.5", ebpf.StructOps: "5.6", ebpf.Extension: "5.6", ebpf.LSM: "5.7", ebpf.SkLookup: "5.9", } func TestHaveProgType(t *testing.T) { for progType := ebpf.UnspecifiedProgram + 1; progType <= progType.Max(); progType++ { // Need inner loop copy to make use of t.Parallel() pt := progType minVersion, ok := progTypeMinVersion[pt] if !ok { // In cases where a new prog type wasn't added to progTypeMinVersion // we should make sure the test runs anyway and fails on old kernels minVersion = "0.0" } feature := fmt.Sprintf("program type %s", pt.String()) t.Run(pt.String(), func(t *testing.T) { t.Parallel() if progLoadProbeNotImplemented(pt) { t.Skipf("Test for prog type %s requires working probe", pt.String()) } testutils.SkipOnOldKernel(t, minVersion, feature) if err := HaveProgType(pt); err != nil { if pt == ebpf.LircMode2 { // CI kernels are built with CONFIG_BPF_LIRC_MODE2, but some // mainstream distro's don't ship with it. Make this prog type // optional to retain compatibility with those kernels. testutils.SkipIfNotSupported(t, err) } t.Fatalf("Program type %s isn't supported even though kernel is at least %s: %v", pt.String(), minVersion, err) } }) } } func TestHaveProgTypeUnsupported(t *testing.T) { if err := haveProgType(ebpf.ProgramType(math.MaxUint32)); err != ebpf.ErrNotSupported { t.Fatalf("Expected ebpf.ErrNotSupported but was: %v", err) } } func TestHaveProgTypeInvalid(t *testing.T) { if err := HaveProgType(ebpf.ProgramType(math.MaxUint32)); err != os.ErrInvalid { t.Fatalf("Expected os.ErrInvalid but was: %v", err) } } golang-github-cilium-ebpf-0.7.0/go.mod000066400000000000000000000002611414524555700175600ustar00rootroot00000000000000module github.com/cilium/ebpf go 1.16 require ( github.com/frankban/quicktest v1.11.3 github.com/google/go-cmp v0.5.4 golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 ) golang-github-cilium-ebpf-0.7.0/go.sum000066400000000000000000000022121414524555700176030ustar00rootroot00000000000000github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang-github-cilium-ebpf-0.7.0/info.go000066400000000000000000000141011414524555700177320ustar00rootroot00000000000000package ebpf import ( "bufio" "encoding/hex" "errors" "fmt" "io" "os" "strings" "syscall" "time" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" ) // 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. Name string } func newMapInfoFromFd(fd *internal.FD) (*MapInfo, error) { info, err := bpfGetMapInfoByFD(fd) if errors.Is(err, syscall.EINVAL) { return newMapInfoFromProc(fd) } if err != nil { return nil, err } return &MapInfo{ MapType(info.map_type), MapID(info.id), info.key_size, info.value_size, info.max_entries, info.map_flags, // name is available from 4.15. internal.CString(info.name[:]), }, nil } func newMapInfoFromProc(fd *internal.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. Tag string // Name as supplied by user space at load time. Name string // BTF for the program. btf btf.ID // IDS map ids related to program. ids []MapID stats *programStats } func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { info, err := bpfGetProgInfoByFD(fd, nil) if errors.Is(err, syscall.EINVAL) { return newProgramInfoFromProc(fd) } if err != nil { return nil, err } var mapIDs []MapID if info.nr_map_ids > 0 { mapIDs = make([]MapID, info.nr_map_ids) info, err = bpfGetProgInfoByFD(fd, mapIDs) if err != nil { return nil, err } } return &ProgramInfo{ Type: ProgramType(info.prog_type), id: ProgramID(info.id), // tag is available if the kernel supports BPF_PROG_GET_INFO_BY_FD. Tag: hex.EncodeToString(info.tag[:]), // name is available from 4.15. Name: internal.CString(info.name[:]), btf: btf.ID(info.btf_id), ids: mapIDs, stats: &programStats{ runtime: time.Duration(info.run_time_ns), runCount: info.run_cnt, }, }, nil } func newProgramInfoFromProc(fd *internal.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 } // BTFID returns the BTF ID associated with the program. // // 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 } // MapIDs returns the maps related to the program. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { return pi.ids, pi.ids != nil } func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { raw, err := fd.Value() if err != nil { return err } fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw)) 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 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) { attr := internal.BPFEnableStatsAttr{ StatsType: which, } fd, err := internal.BPFEnableStats(&attr) if err != nil { return nil, err } return fd, nil } golang-github-cilium-ebpf-0.7.0/info_test.go000066400000000000000000000202551414524555700210000ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "strings" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "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, }) if err != nil { t.Fatal(err) } defer hash.Close() info, err := newMapInfoFromProc(hash.fd) 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 := createSocketFilter(t) defer prog.Close() for name, fn := range map[string]func(*internal.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") } }) } } func TestProgramInfoMapIDs(t *testing.T) { testutils.SkipOnOldKernel(t, "4.10", "reading program info") 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() qt.Assert(t, err, qt.IsNil) ids, ok := info.MapIDs() if testutils.MustKernelVersion().Less(internal.Version{4, 15, 0}) { qt.Assert(t, ok, qt.IsFalse) qt.Assert(t, ids, qt.HasLen, 0) } else { qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, ids, qt.HasLen, 1) mapInfo, err := arr.Info() qt.Assert(t, err, qt.IsNil) mapID, ok := mapInfo.ID() qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, ids[0], qt.Equals, mapID) } } 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") spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 42, asm.DWord), asm.Return(), }, License: "MIT", } prog, err := NewProgram(spec) if err != nil { t.Fatal(err) } defer prog.Close() 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") spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 42, asm.DWord), asm.Return(), }, License: "MIT", } // Don't insert the program in a loop as it causes a flood of kaudit messages. prog, err := NewProgram(spec) if err != nil { b.Fatal(err) } defer prog.Close() 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 := make([]byte, 14) 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 } golang-github-cilium-ebpf-0.7.0/internal/000077500000000000000000000000001414524555700202675ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/internal/align.go000066400000000000000000000002461414524555700217120ustar00rootroot00000000000000package internal // Align returns 'n' updated to 'alignment' boundary. func Align(n, alignment int) int { return (int(n) + alignment - 1) / alignment * alignment } golang-github-cilium-ebpf-0.7.0/internal/btf/000077500000000000000000000000001414524555700210425ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/internal/btf/btf.go000066400000000000000000000453671414524555700221630ustar00rootroot00000000000000package btf import ( "bytes" "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "reflect" "sync" "unsafe" "github.com/cilium/ebpf/internal" "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") ) // ID represents the unique ID of a BTF object. type ID uint32 // Spec represents decoded BTF. type Spec struct { rawTypes []rawType strings stringTable types []Type namedTypes map[string][]NamedType funcInfos map[string]extInfo lineInfos map[string]extInfo coreRelos map[string]coreRelos byteOrder binary.ByteOrder } type btfHeader struct { Magic uint16 Version uint8 Flags uint8 HdrLen uint32 TypeOff uint32 TypeLen uint32 StringOff uint32 StringLen uint32 } // LoadSpecFromReader reads BTF sections from an ELF. // // Returns ErrNotFound if the reader contains no BTF. func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { file, err := internal.NewSafeELFFile(rd) if err != nil { return nil, err } defer file.Close() symbols, err := file.Symbols() if err != nil { return nil, fmt.Errorf("can't read symbols: %v", err) } variableOffsets := make(map[variable]uint32) for _, symbol := range symbols { if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { // Ignore things like SHN_ABS continue } if int(symbol.Section) >= len(file.Sections) { return nil, fmt.Errorf("symbol %s: invalid section %d", symbol.Name, symbol.Section) } secName := file.Sections[symbol.Section].Name if symbol.Value > math.MaxUint32 { return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) } variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) } return loadSpecFromELF(file, variableOffsets) } func loadSpecFromELF(file *internal.SafeELFFile, variableOffsets map[variable]uint32) (*Spec, error) { var ( btfSection *elf.Section btfExtSection *elf.Section sectionSizes = make(map[string]uint32) ) for _, sec := range file.Sections { switch sec.Name { case ".BTF": btfSection = sec case ".BTF.ext": btfExtSection = 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) } spec, err := loadRawSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) if err != nil { return nil, err } if btfExtSection == nil { return spec, nil } spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) if err != nil { return nil, fmt.Errorf("can't read ext info: %w", err) } return spec, nil } // LoadRawSpec reads a blob of BTF data that isn't wrapped in an ELF file. // // Prefer using LoadSpecFromReader, since this function only supports a subset // of BTF. func LoadRawSpec(btf io.Reader, bo binary.ByteOrder) (*Spec, error) { // This will return an error if we encounter a Datasec, since we can't fix // it up. return loadRawSpec(btf, bo, nil, nil) } func loadRawSpec(btf io.Reader, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { rawTypes, rawStrings, err := parseBTF(btf, bo) if err != nil { return nil, err } err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets) if err != nil { return nil, err } types, typesByName, err := inflateRawTypes(rawTypes, rawStrings) if err != nil { return nil, err } return &Spec{ rawTypes: rawTypes, namedTypes: typesByName, types: types, strings: rawStrings, byteOrder: bo, }, nil } var kernelBTF struct { sync.Mutex *Spec } // LoadKernelSpec returns the current kernel's BTF information. // // Requires a >= 5.5 kernel with CONFIG_DEBUG_INFO_BTF enabled. Returns // ErrNotSupported if BTF is not enabled. func LoadKernelSpec() (*Spec, error) { kernelBTF.Lock() defer kernelBTF.Unlock() if kernelBTF.Spec != nil { return kernelBTF.Spec, nil } var err error kernelBTF.Spec, err = loadKernelSpec() return kernelBTF.Spec, err } func loadKernelSpec() (*Spec, error) { release, err := unix.KernelRelease() if err != nil { return nil, fmt.Errorf("can't read kernel release number: %w", err) } fh, err := os.Open("/sys/kernel/btf/vmlinux") if err == nil { defer fh.Close() return LoadRawSpec(fh, internal.NativeEndian) } // 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 { path := fmt.Sprintf(loc, release) fh, err := os.Open(path) if err != nil { continue } defer fh.Close() file, err := internal.NewSafeELFFile(fh) if err != nil { return nil, err } defer file.Close() return loadSpecFromELF(file, nil) } return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported) } func parseBTF(btf io.Reader, bo binary.ByteOrder) ([]rawType, stringTable, error) { rawBTF, err := io.ReadAll(btf) if err != nil { return nil, nil, fmt.Errorf("can't read BTF: %v", err) } rd := bytes.NewReader(rawBTF) var header btfHeader if err := binary.Read(rd, bo, &header); err != nil { return nil, nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { return nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { return nil, nil, errors.New("header is too short") } if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { return nil, nil, fmt.Errorf("header padding: %v", err) } if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err) } rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) if err != nil { return nil, nil, fmt.Errorf("can't read type names: %w", err) } if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err) } rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) if err != nil { return nil, nil, fmt.Errorf("can't read types: %w", err) } return rawTypes, rawStrings, nil } type variable struct { section string name string } func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error { for i, rawType := range rawTypes { if rawType.Kind() != kindDatasec { continue } name, err := rawStrings.Lookup(rawType.NameOff) if err != nil { return err } if name == ".kconfig" || name == ".ksyms" { return fmt.Errorf("reference to %s: %w", name, ErrNotSupported) } if rawTypes[i].SizeType != 0 { continue } size, ok := sectionSizes[name] if !ok { return fmt.Errorf("data section %s: missing size", name) } rawTypes[i].SizeType = size secinfos := rawType.data.([]btfVarSecinfo) for j, secInfo := range secinfos { id := int(secInfo.Type - 1) if id >= len(rawTypes) { return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) } varName, err := rawStrings.Lookup(rawTypes[id].NameOff) if err != nil { return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err) } offset, ok := variableOffsets[variable{name, varName}] if !ok { return fmt.Errorf("data section %s: missing offset for variable %s", name, varName) } secinfos[j].Offset = offset } } return nil } // Copy creates a copy of Spec. func (s *Spec) Copy() *Spec { types, _ := copyTypes(s.types, nil) namedTypes := make(map[string][]NamedType) for _, typ := range types { if named, ok := typ.(NamedType); ok { name := essentialName(named.TypeName()) namedTypes[name] = append(namedTypes[name], named) } } // NB: Other parts of spec are not copied since they are immutable. return &Spec{ s.rawTypes, s.strings, types, namedTypes, s.funcInfos, s.lineInfos, s.coreRelos, s.byteOrder, } } type marshalOpts struct { ByteOrder binary.ByteOrder StripFuncLinkage bool } func (s *Spec) marshal(opts marshalOpts) ([]byte, error) { var ( buf bytes.Buffer header = new(btfHeader) headerLen = binary.Size(header) ) // Reserve space for the header. We have to write it last since // we don't know the size of the type section yet. _, _ = buf.Write(make([]byte, headerLen)) // Write type section, just after the header. for _, raw := range s.rawTypes { switch { case opts.StripFuncLinkage && raw.Kind() == kindFunc: raw.SetLinkage(StaticFunc) } if err := raw.Marshal(&buf, opts.ByteOrder); err != nil { return nil, fmt.Errorf("can't marshal BTF: %w", err) } } typeLen := uint32(buf.Len() - headerLen) // Write string section after type section. _, _ = buf.Write(s.strings) // Fill out the header, and write it out. header = &btfHeader{ Magic: btfMagic, Version: 1, Flags: 0, HdrLen: uint32(headerLen), TypeOff: 0, TypeLen: typeLen, StringOff: typeLen, StringLen: uint32(len(s.strings)), } raw := buf.Bytes() err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header) if err != nil { return nil, fmt.Errorf("can't write header: %v", err) } return raw, nil } 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 } // Program finds the BTF for a specific section. // // Length is the number of bytes in the raw BPF instruction stream. // // Returns an error which may wrap ErrNoExtendedInfo if the Spec doesn't // contain extended BTF info. func (s *Spec) Program(name string, length uint64) (*Program, error) { if length == 0 { return nil, errors.New("length musn't be zero") } if s.funcInfos == nil && s.lineInfos == nil && s.coreRelos == nil { return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo) } funcInfos, funcOK := s.funcInfos[name] lineInfos, lineOK := s.lineInfos[name] relos, coreOK := s.coreRelos[name] if !funcOK && !lineOK && !coreOK { return nil, fmt.Errorf("no extended BTF info for section %s", name) } return &Program{s, length, funcInfos, lineInfos, relos}, nil } // FindType searches for a type with a specific name. // // Called T a type that satisfies Type, typ must be a non-nil **T. // On success, the address of the found type will be copied in typ. // // Returns an error wrapping ErrNotFound if no matching // type exists in spec. func (s *Spec) FindType(name string, typ interface{}) error { 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.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) { return fmt.Errorf("%T does not satisfy Type interface", typ) } var candidate Type for _, typ := range s.namedTypes[essentialName(name)] { if reflect.TypeOf(typ) != wanted { continue } // Match against the full name, not just the essential one. if typ.TypeName() != name { continue } if candidate != nil { return fmt.Errorf("type %s: multiple candidates for %T", name, typ) } candidate = typ } if candidate == nil { return fmt.Errorf("type %s: %w", name, ErrNotFound) } typPtr.Set(reflect.ValueOf(candidate)) return nil } // Handle is a reference to BTF loaded into the kernel. type Handle struct { spec *Spec fd *internal.FD } // NewHandle loads BTF into the kernel. // // Returns ErrNotSupported if BTF is not supported. func NewHandle(spec *Spec) (*Handle, error) { if err := haveBTF(); err != nil { return nil, err } if spec.byteOrder != internal.NativeEndian { return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian) } btf, err := spec.marshal(marshalOpts{ ByteOrder: internal.NativeEndian, StripFuncLinkage: haveFuncLinkage() != nil, }) if err != nil { return nil, fmt.Errorf("can't marshal BTF: %w", err) } if uint64(len(btf)) > math.MaxUint32 { return nil, errors.New("BTF exceeds the maximum size") } attr := &bpfLoadBTFAttr{ btf: internal.NewSlicePointer(btf), btfSize: uint32(len(btf)), } fd, err := bpfLoadBTF(attr) if err != nil { logBuf := make([]byte, 64*1024) attr.logBuf = internal.NewSlicePointer(logBuf) attr.btfLogSize = uint32(len(logBuf)) attr.btfLogLevel = 1 _, logErr := bpfLoadBTF(attr) return nil, internal.ErrorWithLog(err, logBuf, logErr) } return &Handle{spec.Copy(), fd}, nil } // NewHandleFromID returns the BTF handle for a given id. // // Returns ErrNotExist, if there is no BTF with the given id. // // Requires CAP_SYS_ADMIN. func NewHandleFromID(id ID) (*Handle, error) { fd, err := internal.BPFObjGetFDByID(internal.BPF_BTF_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, fmt.Errorf("get BTF by id: %w", err) } info, err := newInfoFromFd(fd) if err != nil { _ = fd.Close() return nil, fmt.Errorf("get BTF spec for handle: %w", err) } return &Handle{info.BTF, fd}, nil } // Spec returns the Spec that defined the BTF loaded into the kernel. func (h *Handle) Spec() *Spec { return h.spec } // Close destroys the handle. // // Subsequent calls to FD will return an invalid value. func (h *Handle) Close() error { return h.fd.Close() } // FD returns the file descriptor for the handle. func (h *Handle) FD() int { value, err := h.fd.Value() if err != nil { return -1 } return int(value) } // Map is the BTF for a map. type Map struct { Spec *Spec Key, Value Type } // Program is the BTF information for a stream of instructions. type Program struct { spec *Spec length uint64 funcInfos, lineInfos extInfo coreRelos coreRelos } // Spec returns the BTF spec of this program. func (p *Program) Spec() *Spec { return p.spec } // Append the information from other to the Program. func (p *Program) Append(other *Program) error { if other.spec != p.spec { return fmt.Errorf("can't append program with different BTF specs") } funcInfos, err := p.funcInfos.append(other.funcInfos, p.length) if err != nil { return fmt.Errorf("func infos: %w", err) } lineInfos, err := p.lineInfos.append(other.lineInfos, p.length) if err != nil { return fmt.Errorf("line infos: %w", err) } p.funcInfos = funcInfos p.lineInfos = lineInfos p.coreRelos = p.coreRelos.append(other.coreRelos, p.length) p.length += other.length return nil } // FuncInfos returns the binary form of BTF function infos. func (p *Program) FuncInfos() (recordSize uint32, bytes []byte, err error) { bytes, err = p.funcInfos.MarshalBinary() if err != nil { return 0, nil, fmt.Errorf("func infos: %w", err) } return p.funcInfos.recordSize, bytes, nil } // LineInfos returns the binary form of BTF line infos. func (p *Program) LineInfos() (recordSize uint32, bytes []byte, err error) { bytes, err = p.lineInfos.MarshalBinary() if err != nil { return 0, nil, fmt.Errorf("line infos: %w", err) } return p.lineInfos.recordSize, bytes, nil } // Fixups returns the changes required to adjust the program to the target. // // Passing a nil target will relocate against the running kernel. func (p *Program) Fixups(target *Spec) (COREFixups, error) { if len(p.coreRelos) == 0 { return nil, nil } if target == nil { var err error target, err = LoadKernelSpec() if err != nil { return nil, err } } return coreRelocate(p.spec, target, p.coreRelos) } type bpfLoadBTFAttr struct { btf internal.Pointer logBuf internal.Pointer btfSize uint32 btfLogSize uint32 btfLogLevel uint32 } func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) { fd, err := internal.BPF(internal.BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return internal.NewFD(uint32(fd)), nil } func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte { const minHeaderLength = 24 typesLen := uint32(binary.Size(types)) header := btfHeader{ Magic: btfMagic, Version: 1, HdrLen: minHeaderLength, TypeOff: 0, TypeLen: typesLen, StringOff: typesLen, StringLen: uint32(len(strings)), } buf := new(bytes.Buffer) _ = binary.Write(buf, bo, &header) _ = binary.Write(buf, bo, types) buf.Write(strings) return buf.Bytes() } var haveBTF = internal.FeatureTest("BTF", "5.1", func() error { var ( types struct { Integer btfType Var btfType btfVar struct{ Linkage uint32 } } strings = []byte{0, 'a', 0} ) // We use a BTF_KIND_VAR here, to make sure that // the kernel understands BTF at least as well as we // do. BTF_KIND_VAR was introduced ~5.1. types.Integer.SetKind(kindPointer) types.Var.NameOff = 1 types.Var.SetKind(kindVar) types.Var.SizeType = 1 btf := marshalBTF(&types, strings, internal.NativeEndian) fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ btf: internal.NewSlicePointer(btf), btfSize: uint32(len(btf)), }) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { // Treat both EINVAL and EPERM as not supported: loading the program // might still succeed without BTF. return internal.ErrNotSupported } if err != nil { return err } fd.Close() return nil }) var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() error { if err := haveBTF(); err != nil { return err } var ( types struct { FuncProto btfType Func btfType } strings = []byte{0, 'a', 0} ) types.FuncProto.SetKind(kindFuncProto) types.Func.SetKind(kindFunc) types.Func.SizeType = 1 // aka FuncProto types.Func.NameOff = 1 types.Func.SetLinkage(GlobalFunc) btf := marshalBTF(&types, strings, internal.NativeEndian) fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ btf: internal.NewSlicePointer(btf), btfSize: uint32(len(btf)), }) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if err != nil { return err } fd.Close() return nil }) golang-github-cilium-ebpf-0.7.0/internal/btf/btf_test.go000066400000000000000000000134751414524555700232150ustar00rootroot00000000000000package btf import ( "compress/gzip" "encoding/binary" "errors" "fmt" "os" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestFindType(t *testing.T) { fh, err := os.Open("testdata/vmlinux-btf.gz") if err != nil { t.Fatal(err) } defer fh.Close() rd, err := gzip.NewReader(fh) if err != nil { t.Fatal(err) } defer rd.Close() spec, err := loadRawSpec(rd, binary.LittleEndian, nil, nil) if err != nil { t.Fatal("Can't load BTF:", err) } // spec.FindType MUST fail if typ is not a non-nil **T, where T satisfies btf.Type. i := 0 p := &i for _, typ := range []interface{}{ nil, Struct{}, &Struct{}, []Struct{}, &[]Struct{}, map[int]Struct{}, &map[int]Struct{}, p, &p, } { if err := spec.FindType("iphdr", typ); err == nil { t.Fatalf("FindType does not fail with type %T", typ) } } // spec.FindType MUST return the same address for multiple calls with the same type name. var iphdr1, iphdr2 *Struct if err := spec.FindType("iphdr", &iphdr1); err != nil { t.Fatal(err) } if err := spec.FindType("iphdr", &iphdr2); err != nil { t.Fatal(err) } if iphdr1 != iphdr2 { t.Fatal("multiple FindType calls for `iphdr` name do not return the same addresses") } } func TestParseVmlinux(t *testing.T) { fh, err := os.Open("testdata/vmlinux-btf.gz") if err != nil { t.Fatal(err) } defer fh.Close() rd, err := gzip.NewReader(fh) if err != nil { t.Fatal(err) } spec, err := loadRawSpec(rd, binary.LittleEndian, nil, nil) if err != nil { t.Fatal("Can't load BTF:", err) } var iphdr *Struct err = spec.FindType("iphdr", &iphdr) if err != nil { t.Fatalf("unable to find `iphdr` struct: %s", err) } for _, m := range iphdr.Members { if m.Name == "version" { // __u8 is a typedef td, ok := m.Type.(*Typedef) if !ok { t.Fatalf("version member of iphdr should be a __u8 typedef: actual: %T", m.Type) } u8int, ok := td.Type.(*Int) if !ok { t.Fatalf("__u8 typedef should point to an Int type: actual: %T", td.Type) } if u8int.Bits != 8 { t.Fatalf("incorrect bit size of an __u8 int: expected: 8 actual: %d", u8int.Bits) } if u8int.Encoding != 0 { t.Fatalf("incorrect encoding of an __u8 int: expected: 0 actual: %x", u8int.Encoding) } if u8int.OffsetBits != 0 { t.Fatalf("incorrect int offset of an __u8 int: expected: 0 actual: %d", u8int.OffsetBits) } break } } } func TestParseCurrentKernelBTF(t *testing.T) { spec, err := loadKernelSpec() testutils.SkipIfNotSupported(t, err) 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) { fh, err := os.Open(file) if err != nil { t.Fatal(err) } defer fh.Close() spec, err := LoadSpecFromReader(fh) if err != nil { t.Fatal("Can't load BTF:", err) } if spec == nil { t.Error("No BTF found in ELF") } if sec, err := spec.Program("xdp", 1); err != nil { t.Error("Can't get BTF for the xdp section:", err) } else if sec == nil { t.Error("Missing BTF for the xdp section") } if sec, err := spec.Program("socket", 1); err != nil { t.Error("Can't get BTF for the socket section:", err) } else if sec == nil { t.Error("Missing BTF for the socket section") } var bpfMapDef *Struct if err := spec.FindType("bpf_map_def", &bpfMapDef); err != nil { t.Error("Can't find bpf_map_def:", err) } var tmp *Void if err := spec.FindType("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) { t.Error("FindType doesn't return ErrNotFound:", err) } var fn *Func if err := spec.FindType("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.FindType("key3", &v); err != nil { t.Error("Cant find key3:", err) } else { if v.Linkage != GlobalVar { t.Error("Expected global linkage:", v) } } if spec.byteOrder != internal.NativeEndian { return } t.Run("Handle", func(t *testing.T) { btf, err := NewHandle(spec) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load BTF:", err) } defer btf.Close() }) }) } 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 TestSpecCopy(t *testing.T) { fh, err := os.Open("../../testdata/loader-el.elf") if err != nil { t.Fatal(err) } defer fh.Close() spec, err := LoadSpecFromReader(fh) if err != nil { t.Fatal("Can't load BTF:", err) } 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 TestHaveBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveBTF) } func TestHaveFuncLinkage(t *testing.T) { testutils.CheckFeatureTest(t, haveFuncLinkage) } func ExampleSpec_FindType() { // Acquire a Spec via one of its constructors. spec := new(Spec) // Declare a variable of the desired type var foo *Struct if err := spec.FindType("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) } golang-github-cilium-ebpf-0.7.0/internal/btf/btf_types.go000066400000000000000000000134211414524555700233710ustar00rootroot00000000000000package btf import ( "encoding/binary" "fmt" "io" ) //go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage // btfKind describes a Type. type btfKind uint8 // Equivalents of the BTF_KIND_* constants. const ( kindUnknown btfKind = iota kindInt kindPointer kindArray kindStruct kindUnion kindEnum kindForward kindTypedef kindVolatile kindConst kindRestrict // Added ~4.20 kindFunc kindFuncProto // Added ~5.1 kindVar kindDatasec // Added ~5.13 kindFloat ) // 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 ) // 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 (k btfKind) String() string { switch k { case kindUnknown: return "Unknown" case kindInt: return "Integer" case kindPointer: return "Pointer" case kindArray: return "Array" case kindStruct: return "Struct" case kindUnion: return "Union" case kindEnum: return "Enumeration" case kindForward: return "Forward" case kindTypedef: return "Typedef" case kindVolatile: return "Volatile" case kindConst: return "Const" case kindRestrict: return "Restrict" case kindFunc: return "Function" case kindFuncProto: return "Function Proto" case kindVar: return "Variable" case kindDatasec: return "Section" case kindFloat: return "Float" default: return fmt.Sprintf("Unknown (%d)", k) } } func mask(len uint32) uint32 { return (1 << len) - 1 } func (bt *btfType) info(len, shift uint32) uint32 { return (bt.Info >> shift) & mask(len) } func (bt *btfType) setInfo(value, len, shift uint32) { bt.Info &^= mask(len) << shift bt.Info |= (value & mask(len)) << shift } 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) KindFlag() bool { return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1 } 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) Size() uint32 { // TODO: Panic here if wrong kind? return bt.SizeType } type rawType struct { btfType data interface{} } func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error { if err := binary.Write(w, bo, &rt.btfType); err != nil { return err } if rt.data == nil { return nil } return binary.Write(w, bo, rt.data) } 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 int32 } type btfParam struct { NameOff uint32 Type TypeID } func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { var ( header btfType types []rawType ) 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(uint32) 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: 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}) } } func intEncoding(raw uint32) (IntEncoding, uint32, byte) { return IntEncoding((raw & 0x0f000000) >> 24), (raw & 0x00ff0000) >> 16, byte(raw & 0x000000ff) } golang-github-cilium-ebpf-0.7.0/internal/btf/btf_types_string.go000066400000000000000000000024301414524555700247550ustar00rootroot00000000000000// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage"; 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]] } golang-github-cilium-ebpf-0.7.0/internal/btf/core.go000066400000000000000000000607451414524555700223350ustar00rootroot00000000000000package btf import ( "errors" "fmt" "math" "reflect" "sort" "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 Poison 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 { return errors.New("can't poison individual instruction") } switch class := ins.OpCode.Class(); class { case asm.LdXClass, asm.StClass, asm.StXClass: if want := int16(f.Local); want != ins.Offset { return fmt.Errorf("invalid offset %d, expected %d", ins.Offset, want) } 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); want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want) } 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); want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want) } 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 } type COREFixups map[uint64]COREFixup // Apply a set of CO-RE relocations to a BPF program. func (fs COREFixups) Apply(insns asm.Instructions) (asm.Instructions, error) { if len(fs) == 0 { cpy := make(asm.Instructions, len(insns)) copy(cpy, insns) return insns, nil } cpy := make(asm.Instructions, 0, len(insns)) iter := insns.Iterate() for iter.Next() { fixup, ok := fs[iter.Offset.Bytes()] if !ok { cpy = append(cpy, *iter.Ins) continue } ins := *iter.Ins if fixup.Poison { const badRelo = asm.BuiltinFunc(0xbad2310) cpy = append(cpy, badRelo.Call()) if ins.OpCode.IsDWordLoad() { // 64 bit constant loads occupy two raw bpf instructions, so // we need to add another instruction as padding. cpy = append(cpy, badRelo.Call()) } continue } if err := fixup.apply(&ins); err != nil { return nil, fmt.Errorf("instruction %d, offset %d: %s: %w", iter.Index, iter.Offset.Bytes(), fixup.Kind, err) } cpy = append(cpy, ins) } return cpy, nil } // COREKind is the type of CO-RE relocation 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) 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" } } func (k COREKind) checksForExistence() bool { return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists } func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) { if local.byteOrder != target.byteOrder { return nil, fmt.Errorf("can't relocate %s against %s", local.byteOrder, target.byteOrder) } var ids []TypeID relosByID := make(map[TypeID]coreRelos) result := make(COREFixups, len(relos)) for _, 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[uint64(relo.insnOff)] = COREFixup{ relo.kind, uint32(relo.typeID), uint32(relo.typeID), false, } continue } relos, ok := relosByID[relo.typeID] if !ok { ids = append(ids, relo.typeID) } relosByID[relo.typeID] = append(relos, relo) } // Ensure we work on relocations in a deterministic order. sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) for _, id := range ids { if int(id) >= len(local.types) { return nil, fmt.Errorf("invalid type id %d", id) } localType := local.types[id] named, ok := localType.(NamedType) if !ok || named.TypeName() == "" { return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) } relos := relosByID[id] targets := target.namedTypes[essentialName(named.TypeName())] fixups, err := coreCalculateFixups(localType, targets, relos) if err != nil { return nil, fmt.Errorf("relocate %s: %w", localType, err) } for i, relo := range relos { result[uint64(relo.insnOff)] = fixups[i] } } return result, nil } var errAmbiguousRelocation = errors.New("ambiguous relocation") var errImpossibleRelocation = errors.New("impossible relocation") // coreCalculateFixups calculates the fixups for the given relocations using // the "best" target. // // The best target is determined by scoring: the less poisoning we have to do // the better the target is. func coreCalculateFixups(local Type, targets []NamedType, relos coreRelos) ([]COREFixup, error) { localID := local.ID() local, err := copyType(local, skipQualifierAndTypedef) if err != nil { return nil, err } bestScore := len(relos) var bestFixups []COREFixup for i := range targets { targetID := targets[i].ID() target, err := copyType(targets[i], skipQualifierAndTypedef) if err != nil { return nil, err } score := 0 // lower is better fixups := make([]COREFixup, 0, len(relos)) for _, relo := range relos { fixup, err := coreCalculateFixup(local, localID, target, targetID, relo) if err != nil { return nil, fmt.Errorf("target %s: %w", target, 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! bestFixups = make([]COREFixup, len(relos)) for i, relo := range relos { bestFixups[i] = COREFixup{Kind: relo.kind, Poison: true} } } return bestFixups, nil } // coreCalculateFixup calculates the fixup for a single local type, target type // and relocation. func coreCalculateFixup(local Type, localID TypeID, target Type, targetID TypeID, relo coreRelo) (COREFixup, error) { fixup := func(local, target uint32) (COREFixup, error) { return COREFixup{relo.kind, local, target, false}, nil } poison := func() (COREFixup, error) { if relo.kind.checksForExistence() { return fixup(1, 0) } return COREFixup{relo.kind, 0, 0, true}, nil } zero := COREFixup{} switch relo.kind { case reloTypeIDTarget, reloTypeSize, reloTypeExists: if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return zero, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) } err := coreAreTypesCompatible(local, target) if errors.Is(err, errImpossibleRelocation) { return poison() } if err != nil { return zero, fmt.Errorf("relocation %s: %w", relo.kind, err) } switch relo.kind { case reloTypeExists: return fixup(1, 1) case reloTypeIDTarget: return fixup(uint32(localID), 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, fmt.Errorf("relocation %s: %w", relo.kind, err) } switch relo.kind { case reloEnumvalExists: return fixup(1, 1) case reloEnumvalValue: return fixup(uint32(localValue.Value), uint32(targetValue.Value)) } case reloFieldByteOffset, reloFieldByteSize, reloFieldExists: if _, ok := target.(*Fwd); 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, fmt.Errorf("target %s: %w", target, err) } switch relo.kind { case reloFieldExists: return fixup(1, 1) case reloFieldByteOffset: return fixup(localField.offset/8, targetField.offset/8) 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 fixup(uint32(localSize), uint32(targetSize)) } } return zero, fmt.Errorf("relocation %s: %w", relo.kind, ErrNotSupported) } /* 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 := t.(*Enum) 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 } type coreField struct { Type Type offset uint32 } func adjustOffset(base uint32, t Type, n int) (uint32, error) { size, err := Sizeof(t) if err != nil { return 0, err } return base + (uint32(n) * uint32(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(local Type, localAcc coreAccessor, target Type) (_, _ coreField, _ error) { // The first index is used to offset a pointer of the base type like // when accessing an array. localOffset, err := adjustOffset(0, local, localAcc[0]) if err != nil { return coreField{}, coreField{}, err } targetOffset, err := adjustOffset(0, target, localAcc[0]) if err != nil { return coreField{}, coreField{}, err } if err := coreAreMembersCompatible(local, target); err != nil { return coreField{}, coreField{}, fmt.Errorf("fields: %w", err) } var localMaybeFlex, targetMaybeFlex bool for _, acc := range localAcc[1:] { switch localType := local.(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, local) } localMember := localMembers[acc] if localMember.Name == "" { _, ok := localMember.Type.(composite) 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 = localMember.Type localOffset += localMember.OffsetBits localMaybeFlex = false continue } targetType, ok := target.(composite) 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 } if targetMember.BitfieldSize > 0 { return coreField{}, coreField{}, fmt.Errorf("field %q is a bitfield: %w", targetMember.Name, ErrNotSupported) } local = localMember.Type localMaybeFlex = acc == len(localMembers)-1 localOffset += localMember.OffsetBits target = targetMember.Type targetMaybeFlex = last targetOffset += targetMember.OffsetBits case *Array: // For arrays, acc is the index in the target. targetType, ok := target.(*Array) 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 = localType.Type localMaybeFlex = false localOffset, err = adjustOffset(localOffset, local, acc) if err != nil { return coreField{}, coreField{}, err } target = targetType.Type targetMaybeFlex = false targetOffset, err = adjustOffset(targetOffset, target, acc) if err != nil { return coreField{}, coreField{}, err } default: return coreField{}, coreField{}, fmt.Errorf("relocate field of %T: %w", localType, ErrNotSupported) } if err := coreAreMembersCompatible(local, target); err != nil { return coreField{}, coreField{}, err } } return coreField{local, localOffset}, coreField{target, targetOffset}, 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 uint32 } 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.OffsetBits += 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 := member.Type.(composite) if !ok { return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) } targets = append(targets, offsetTarget{comp, target.offset + member.OffsetBits}) } } 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 := target.(*Enum) if !ok { return nil, nil, errImpossibleRelocation } localName := essentialName(localValue.Name) for i, targetValue := range targetEnum.Values { if essentialName(targetValue.Name) != localName { continue } return localValue, &targetEnum.Values[i], nil } return nil, nil, errImpossibleRelocation } /* 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 errImpossibleRelocation 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 = *l targetType = *t if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch: %w", errImpossibleRelocation) } switch lv := (localType).(type) { case *Void, *Struct, *Union, *Enum, *Fwd: // Nothing to do here case *Int: tv := targetType.(*Int) if lv.isBitfield() || tv.isBitfield() { return fmt.Errorf("bitfield: %w", errImpossibleRelocation) } case *Pointer, *Array: depth++ localType.walk(&localTs) targetType.walk(&targetTs) case *FuncProto: tv := targetType.(*FuncProto) if len(lv.Params) != len(tv.Params) { return fmt.Errorf("function param mismatch: %w", errImpossibleRelocation) } depth++ localType.walk(&localTs) targetType.walk(&targetTs) 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 { doNamesMatch := func(a, b string) error { if a == "" || b == "" { // allow anonymous and named type to match return nil } if essentialName(a) == essentialName(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: return nil case *Enum: tv := targetType.(*Enum) return doNamesMatch(lv.Name, tv.Name) case *Fwd: tv := targetType.(*Fwd) return doNamesMatch(lv.Name, tv.Name) case *Int: tv := targetType.(*Int) if lv.isBitfield() || tv.isBitfield() { return fmt.Errorf("bitfield: %w", errImpossibleRelocation) } return nil default: return fmt.Errorf("type %s: %w", localType, ErrNotSupported) } } func skipQualifierAndTypedef(typ Type) (Type, error) { 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, nil } } return nil, errors.New("exceeded type depth") } golang-github-cilium-ebpf-0.7.0/internal/btf/core_reloc_test.go000066400000000000000000000023301414524555700245420ustar00rootroot00000000000000package btf_test import ( "io" "os" "strings" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestCoreRelocationLoad(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/*-el.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) } 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{ TargetBTF: fh, }) 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(make([]byte, 14)) 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.7.0/internal/btf/core_test.go000066400000000000000000000350221414524555700233620ustar00rootroot00000000000000package btf import ( "errors" "math/rand" "os" "strings" "testing" "github.com/cilium/ebpf/internal/testutils" qt "github.com/frankban/quicktest" ) 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}, {&Int{OffsetBits: 1}, &Int{}, false}, {&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true}, {&Pointer{Target: &Void{}}, &Void{}, false}, {&Array{Type: &Void{}}, &Array{Type: &Void{}}, true}, {&Array{Type: &Int{}}, &Array{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, errImpossibleRelocation) { 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, errImpossibleRelocation) { 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, errImpossibleRelocation) { t.Errorf("Expected an error for %T, not errImpossibleRelocation", 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}, {&Int{OffsetBits: 1}, &Int{}, false}, {&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 int32 }{ {"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, OffsetBits: 1}, {Name: "bar", Type: u16, OffsetBits: 2}, } bFields := []Member{ {Name: "foo", Type: ptr, OffsetBits: 10}, {Name: "bar", Type: u32, OffsetBits: 20}, {Name: "other", OffsetBits: 4}, } aStruct := &Struct{Members: aFields, Size: 2} bStruct := &Struct{Members: bFields, Size: 7} 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, }, } 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) }) } bits := func(typ Type) uint32 { sz, err := Sizeof(typ) if err != nil { t.Fatal(err) } return uint32(sz * 8) } anon := func(t Type, offset uint32) []Member { return []Member{{Type: t, OffsetBits: 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}, coreField{u32, 0}, }, { "array[1]", aArray, bArray, coreAccessor{0, 1}, coreField{u16, bits(aArray.Type)}, coreField{u32, bits(bArray.Type)}, }, { "array[0] with base offset", aArray, bArray, coreAccessor{1, 0}, coreField{u16, bits(aArray)}, coreField{u32, bits(bArray)}, }, { "array[2] with base offset", aArray, bArray, coreAccessor{1, 2}, coreField{u16, bits(aArray) + 2*bits(aArray.Type)}, coreField{u32, bits(bArray) + 2*bits(bArray.Type)}, }, { "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, bits(u16) * 9000}, coreField{u32, bits(u32) * 9000}, }, { "struct.0", aStruct, bStruct, coreAccessor{0, 0}, coreField{ptr, 1}, coreField{ptr, 10}, }, { "struct.0 anon", aStruct, &Struct{Members: anon(bStruct, 23)}, coreAccessor{0, 0}, coreField{ptr, 1}, coreField{ptr, 23 + 10}, }, { "struct.0 with base offset", aStruct, bStruct, coreAccessor{3, 0}, coreField{ptr, 3*bits(aStruct) + 1}, coreField{ptr, 3*bits(bStruct) + 10}, }, { "struct.1", aStruct, bStruct, coreAccessor{0, 1}, coreField{u16, 2}, coreField{u32, 20}, }, { "struct.1 anon", aStruct, &Struct{Members: anon(bStruct, 1)}, coreAccessor{0, 1}, coreField{u16, 2}, coreField{u32, 1 + 20}, }, { "union.1", &Union{Members: aFields, Size: 32}, &Union{Members: bFields, Size: 32}, coreAccessor{0, 1}, coreField{u16, 2}, coreField{u32, 20}, }, { "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}, coreField{u16, 0}, }, } checkCoreField := func(t *testing.T, got, want coreField) { t.Helper() qt.Check(t, got.Type, qt.Equals, want.Type, qt.Commentf("type should match")) qt.Check(t, got.offset, qt.Equals, want.offset, qt.Commentf("offset should match")) } 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, localField, test.localField) checkCoreField(t, 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, err := LoadSpecFromReader(rd) if err != nil { t.Fatal(err) } errs := map[string]error{ "err_ambiguous": errAmbiguousRelocation, "err_ambiguous_flavour": errAmbiguousRelocation, } for section := range spec.funcInfos { name := strings.TrimPrefix(section, "socket_filter/") t.Run(name, func(t *testing.T) { prog, err := spec.Program(section, 1) if err != nil { t.Fatal("Retrieve program:", err) } relos, err := prog.Fixups(spec) 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, relo := range relos { if relo.Local != relo.Target { // Since we're relocating against ourselves both values // should match. t.Errorf("offset %d: local %v doesn't match target %d", offset, relo.Local, relo.Target) } } }) } }) } 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) _, err := copyType(root, skipQualifierAndTypedef) qt.Assert(t, err, qt.Not(qt.IsNil)) }) } 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, err := copyType(v, skipQualifierAndTypedef) qt.Assert(t, err, qt.IsNil) 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[rand.Intn(len(qualifiers))] v = q.fn(v) t.Log(q.name) } got, err := copyType(v, skipQualifierAndTypedef) qt.Assert(t, err, qt.IsNil) qt.Assert(t, got, qt.DeepEquals, root) }) } golang-github-cilium-ebpf-0.7.0/internal/btf/doc.go000066400000000000000000000005001414524555700221310ustar00rootroot00000000000000// 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 // // The API is very much unstable. You should only use this via the main // ebpf library. package btf golang-github-cilium-ebpf-0.7.0/internal/btf/ext_info.go000066400000000000000000000205661414524555700232150ustar00rootroot00000000000000package btf import ( "bufio" "bytes" "encoding/binary" "errors" "fmt" "io" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" ) type btfExtHeader struct { Magic uint16 Version uint8 Flags uint8 HdrLen uint32 FuncInfoOff uint32 FuncInfoLen uint32 LineInfoOff uint32 LineInfoLen uint32 } type btfExtCoreHeader struct { CoreReloOff uint32 CoreReloLen uint32 } func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, relos map[string]coreRelos, err error) { var header btfExtHeader var coreHeader btfExtCoreHeader if err := binary.Read(r, bo, &header); err != nil { return nil, nil, nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { return nil, nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { return nil, nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { return nil, nil, nil, errors.New("header is too short") } coreHdrSize := int64(binary.Size(&coreHeader)) if remainder >= coreHdrSize { if err := binary.Read(r, bo, &coreHeader); err != nil { return nil, nil, nil, fmt.Errorf("can't read CO-RE relocation header: %v", err) } remainder -= coreHdrSize } // Of course, the .BTF.ext header has different semantics than the // .BTF ext header. We need to ignore non-null values. _, err = io.CopyN(io.Discard, r, remainder) if err != nil { return nil, nil, nil, fmt.Errorf("header padding: %v", err) } if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { return nil, nil, nil, fmt.Errorf("can't seek to function info section: %v", err) } buf := bufio.NewReader(io.LimitReader(r, int64(header.FuncInfoLen))) funcInfo, err = parseExtInfo(buf, bo, strings) if err != nil { return nil, nil, nil, fmt.Errorf("function info: %w", err) } if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { return nil, nil, nil, fmt.Errorf("can't seek to line info section: %v", err) } buf = bufio.NewReader(io.LimitReader(r, int64(header.LineInfoLen))) lineInfo, err = parseExtInfo(buf, bo, strings) if err != nil { return nil, nil, nil, fmt.Errorf("line info: %w", err) } if coreHeader.CoreReloOff > 0 && coreHeader.CoreReloLen > 0 { if _, err := r.Seek(int64(header.HdrLen+coreHeader.CoreReloOff), io.SeekStart); err != nil { return nil, nil, nil, fmt.Errorf("can't seek to CO-RE relocation section: %v", err) } relos, err = parseExtInfoRelos(io.LimitReader(r, int64(coreHeader.CoreReloLen)), bo, strings) if err != nil { return nil, nil, nil, fmt.Errorf("CO-RE relocation info: %w", err) } } return funcInfo, lineInfo, relos, nil } type btfExtInfoSec struct { SecNameOff uint32 NumInfo uint32 } type extInfoRecord struct { InsnOff uint64 Opaque []byte } type extInfo struct { byteOrder binary.ByteOrder recordSize uint32 records []extInfoRecord } func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { if other.byteOrder != ei.byteOrder { return extInfo{}, fmt.Errorf("ext_info byte order mismatch, want %v (got %v)", ei.byteOrder, other.byteOrder) } if other.recordSize != ei.recordSize { return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) } records := make([]extInfoRecord, 0, len(ei.records)+len(other.records)) records = append(records, ei.records...) for _, info := range other.records { records = append(records, extInfoRecord{ InsnOff: info.InsnOff + offset, Opaque: info.Opaque, }) } return extInfo{ei.byteOrder, ei.recordSize, records}, nil } func (ei extInfo) MarshalBinary() ([]byte, error) { if ei.byteOrder != internal.NativeEndian { return nil, fmt.Errorf("%s is not the native byte order", ei.byteOrder) } if len(ei.records) == 0 { return nil, nil } buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records))) for _, info := range ei.records { // The kernel expects offsets in number of raw bpf instructions, // while the ELF tracks it in bytes. insnOff := uint32(info.InsnOff / asm.InstructionSize) if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { return nil, fmt.Errorf("can't write instruction offset: %v", err) } buf.Write(info.Opaque) } return buf.Bytes(), nil } func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { const maxRecordSize = 256 var recordSize uint32 if err := binary.Read(r, bo, &recordSize); err != nil { return nil, fmt.Errorf("can't read record size: %v", err) } if recordSize < 4 { // Need at least insnOff return nil, errors.New("record size too short") } if recordSize > maxRecordSize { return nil, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize) } result := make(map[string]extInfo) for { secName, infoHeader, err := parseExtInfoHeader(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } var records []extInfoRecord for i := uint32(0); i < infoHeader.NumInfo; i++ { var byteOff uint32 if err := binary.Read(r, bo, &byteOff); err != nil { return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err) } buf := make([]byte, int(recordSize-4)) if _, err := io.ReadFull(r, buf); err != nil { return nil, fmt.Errorf("section %v: can't read record: %v", secName, err) } if byteOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) } records = append(records, extInfoRecord{uint64(byteOff), buf}) } result[secName] = extInfo{ bo, recordSize, records, } } } // bpfCoreRelo matches `struct bpf_core_relo` from the kernel type bpfCoreRelo struct { InsnOff uint32 TypeID TypeID AccessStrOff uint32 Kind COREKind } type coreRelo struct { insnOff uint32 typeID TypeID accessor coreAccessor kind COREKind } type coreRelos []coreRelo // append two slices of extInfoRelo to each other. The InsnOff of b are adjusted // by offset. func (r coreRelos) append(other coreRelos, offset uint64) coreRelos { result := make([]coreRelo, 0, len(r)+len(other)) result = append(result, r...) for _, relo := range other { relo.insnOff += uint32(offset) result = append(result, relo) } return result } var extInfoReloSize = binary.Size(bpfCoreRelo{}) func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]coreRelos, error) { var recordSize uint32 if err := binary.Read(r, bo, &recordSize); err != nil { return nil, fmt.Errorf("read record size: %v", err) } if recordSize != uint32(extInfoReloSize) { return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize) } result := make(map[string]coreRelos) for { secName, infoHeader, err := parseExtInfoHeader(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } var relos coreRelos for i := uint32(0); i < infoHeader.NumInfo; i++ { var relo bpfCoreRelo if err := binary.Read(r, bo, &relo); err != nil { return nil, fmt.Errorf("section %v: read record: %v", secName, err) } if relo.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, relo.InsnOff) } 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) } relos = append(relos, coreRelo{ relo.InsnOff, relo.TypeID, accessor, relo.Kind, }) } result[secName] = relos } } func parseExtInfoHeader(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 infoHeader.NumInfo == 0 { return "", nil, fmt.Errorf("section %s has zero records", secName) } return secName, &infoHeader, nil } golang-github-cilium-ebpf-0.7.0/internal/btf/ext_info_test.go000066400000000000000000000006161414524555700242460ustar00rootroot00000000000000package btf import ( "strings" "testing" "github.com/cilium/ebpf/internal" ) func TestParseExtInfoBigRecordSize(t *testing.T) { rd := strings.NewReader("\xff\xff\xff\xff\x00\x00\x00\x000709171295166016") table := stringTable("\x00") _, err := parseExtInfo(rd, internal.NativeEndian, table) if err == nil { t.Error("Parsing ext info with large record size doesn't return an error") } } golang-github-cilium-ebpf-0.7.0/internal/btf/fuzz.go000066400000000000000000000015231414524555700223700ustar00rootroot00000000000000//go:build gofuzz // +build gofuzz // Use with https://github.com/dvyukov/go-fuzz package btf import ( "bytes" "encoding/binary" "github.com/cilium/ebpf/internal" ) func FuzzSpec(data []byte) int { if len(data) < binary.Size(btfHeader{}) { return -1 } spec, err := loadNakedSpec(bytes.NewReader(data), internal.NativeEndian, nil, nil) if err != nil { if spec != nil { panic("spec is not nil") } return 0 } if spec == nil { panic("spec is nil") } return 1 } func FuzzExtInfo(data []byte) int { if len(data) < binary.Size(btfExtHeader{}) { return -1 } table := stringTable("\x00foo\x00barfoo\x00") info, err := parseExtInfo(bytes.NewReader(data), internal.NativeEndian, table) if err != nil { if info != nil { panic("info is not nil") } return 0 } if info == nil { panic("info is nil") } return 1 } golang-github-cilium-ebpf-0.7.0/internal/btf/info.go000066400000000000000000000021541414524555700223260ustar00rootroot00000000000000package btf import ( "bytes" "github.com/cilium/ebpf/internal" ) // info describes a BTF object. type info struct { BTF *Spec ID ID // Name is an identifying name for the BTF, currently only used by the // kernel. Name string // KernelBTF is true if the BTf originated with the kernel and not // userspace. KernelBTF bool } func newInfoFromFd(fd *internal.FD) (*info, 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. bpfInfo, err := bpfGetBTFInfoByFD(fd, nil, nil) if err != nil { return nil, err } btfBuffer := make([]byte, bpfInfo.btfSize) nameBuffer := make([]byte, bpfInfo.nameLen) bpfInfo, err = bpfGetBTFInfoByFD(fd, btfBuffer, nameBuffer) if err != nil { return nil, err } spec, err := loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil) if err != nil { return nil, err } return &info{ BTF: spec, ID: ID(bpfInfo.id), Name: internal.CString(nameBuffer), KernelBTF: bpfInfo.kernelBTF != 0, }, nil } golang-github-cilium-ebpf-0.7.0/internal/btf/strings.go000066400000000000000000000021571414524555700230670ustar00rootroot00000000000000package btf import ( "bytes" "errors" "fmt" "io" ) type stringTable []byte func readStringTable(r io.Reader) (stringTable, error) { contents, err := io.ReadAll(r) if err != nil { return nil, fmt.Errorf("can't read string table: %v", err) } if len(contents) < 1 { return nil, errors.New("string table is empty") } if contents[0] != '\x00' { return nil, errors.New("first item in string table is non-empty") } if contents[len(contents)-1] != '\x00' { return nil, errors.New("string table isn't null terminated") } return stringTable(contents), nil } func (st stringTable) Lookup(offset uint32) (string, error) { if int64(offset) > int64(^uint(0)>>1) { return "", fmt.Errorf("offset %d overflows int", offset) } pos := int(offset) if pos >= len(st) { return "", fmt.Errorf("offset %d is out of bounds", offset) } if pos > 0 && st[pos-1] != '\x00' { return "", fmt.Errorf("offset %d isn't start of a string", offset) } str := st[pos:] end := bytes.IndexByte(str, '\x00') if end == -1 { return "", fmt.Errorf("offset %d isn't null terminated", offset) } return string(str[:end]), nil } golang-github-cilium-ebpf-0.7.0/internal/btf/strings_test.go000066400000000000000000000020411414524555700241160ustar00rootroot00000000000000package btf import ( "bytes" "strings" "testing" ) func TestStringTable(t *testing.T) { const in = "\x00one\x00two\x00" st, err := readStringTable(strings.NewReader(in)) if err != nil { t.Fatal(err) } if !bytes.Equal([]byte(in), []byte(st)) { t.Error("String table doesn't match input") } testcases := []struct { offset uint32 want string }{ {0, ""}, {1, "one"}, {5, "two"}, } for _, tc := range testcases { have, err := st.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")) if err == nil { t.Fatal("Accepted non-terminated string") } _, err = readStringTable(strings.NewReader("one\x00")) if err == nil { t.Fatal("Accepted non-empty first item") } } golang-github-cilium-ebpf-0.7.0/internal/btf/syscalls.go000066400000000000000000000012341414524555700232260ustar00rootroot00000000000000package btf import ( "fmt" "unsafe" "github.com/cilium/ebpf/internal" ) type bpfBTFInfo struct { btf internal.Pointer btfSize uint32 id uint32 name internal.Pointer nameLen uint32 kernelBTF uint32 } func bpfGetBTFInfoByFD(fd *internal.FD, btf, name []byte) (*bpfBTFInfo, error) { info := bpfBTFInfo{ btf: internal.NewSlicePointer(btf), btfSize: uint32(len(btf)), name: internal.NewSlicePointer(name), nameLen: uint32(len(name)), } if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { return nil, fmt.Errorf("can't get program info: %w", err) } return &info, nil } golang-github-cilium-ebpf-0.7.0/internal/btf/testdata/000077500000000000000000000000001414524555700226535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/internal/btf/testdata/bpf_core_read.h000066400000000000000000000431101414524555700255750ustar00rootroot00000000000000/* 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.7.0/internal/btf/testdata/relocs-eb.elf000066400000000000000000000447601414524555700252310ustar00rootroot00000000000000ELFC@@1a2]3Y4U6Q7 M8 I9 E: A; =<9=5>1?-@)A%D ]!E  ]!H  ]!I  ]! L ]! !Mc:d7e5f3g0h-i+j)k&l#m!nqUrUsUtUuUvUwU xU yUzU{U| U UU* c!s!  g* c!s!`* {!  Y* {!S* طc!s! U K* c!s!UD* ȷ{! U =* {!U7U4U1U.U+̷(ͷ%η"ϷзҷUӷUԷUշUַU طU ٷ U ڷPUPPPە#&%UI : ; ($> .@B: ; I?I : ; I : ; I: ; 8 I: ;  : ; I: ; 8  : ;  : ; 4: ; II!I7 $ > .@B: ; I?W SrCt0Z0C3CCC# C 0FZk|  ( 8  Y ^h 8ZX  P  P 8+s0( ZbZZZ0Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80internal/btf/testdata/relocs.c.unsigned intBPF_TYPE_ID_LOCALBPF_TYPE_ID_TARGETbpf_type_id_kindFRAPONETWOeBPF_TYPE_EXISTSBPF_TYPE_SIZEbpf_type_info_kindBPF_ENUMVAL_EXISTSBPF_ENUMVAL_VALUEbpf_enum_value_kindBPF_FIELD_BYTE_OFFSETBPF_FIELD_BYTE_SIZEBPF_FIELD_EXISTSBPF_FIELD_SIGNEDBPF_FIELD_LSHIFT_U64BPF_FIELD_RSHIFT_U64bpf_field_info_kind_1int_2charss_tuu_t__ARRAY_SIZE_TYPE__ttype_idstypesenumsfieldserr_ambiguouserr_ambiguous_flavourbarpbar   " C  %"D   U    B  "X P  $ %  ! 5"  r $ " inttype_idssocket_filter/type_ids./internal/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 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:1 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); 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_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); ddTt[I " %%F%}8%X%x%%%%G%`%%8%X%x%%'%H%a%(%X%% % $%.0%(%SH[%o%(%8%H%`%x%%'%@%\%s%%%%(%8%P%3h%Ox%h%%%%%%S %<%G@(%hD8%HX%Lx%X%\%`%%SpI$%^%8%H%p%%%%%% % (%2P%2`%S%S%x%x%%%% 0% B0H% h4`% 8x% <% @% H% L% /P% NT% mX % `8% dP% hh%x%Sx % ? % {({H{h{{ { { { {( {H{h{{{{{{{8 {H {h {x { { {{{{{[ { {X {p {{{{  { H { ` { { {  { H  h  { I& 0P X h 0 8 H p ( 8@ ^X p   8 ^    80 ^H X #{ &{ | 0I internal/btf/testdatabpf_core_read.hrelocs.c / =O 2<N 3<M 4<L 6<J 7<I 8<H 9<G :<F ;<E <<D =<C ><B ?<A <@ < X X X X X t  / .     . .     . .     . .     . .     . .   <  =~  ~  ~ <~ <~ <~  ~ <  !f~  X~  X~  J~  f~  X~  X~  J~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ J    G(?07x  L00`     & / 7 = D KY l x                      - 9 A M m y          ) 7 B P  (0 8 @ H P X ,<L\ l |  0@P`p(8HXhx(8HXhx 0@Ph x           ( 8 H X h x           ( 8 H X h x     $4DTdt$4DTdt   , < L \ t     , < L \ l |  , < L \ l | $ <  , 0D H\ ` t x  X)+   .debug_abbrev.text.rel.BTF.extsocket_filter/err_ambiguoussocket_filter/enumssocket_filter/types.rel.debug_rangessocket_filter/fieldssocket_filter/type_idssocket_filter/err_ambiguous_flavour.debug_str.rel.debug_info.llvm_addrsig.rel.debug_line.rel.debug_framerelocs.c.strtab.symtab.BTFLBB2_9LBB3_27LBB1_25LBB0_23BO@@0Rp>`x0"    [ 1p j 7pf 4 0 4+ L 5 * Ax+ B8`oLB#/`(golang-github-cilium-ebpf-0.7.0/internal/btf/testdata/relocs-el.elf000066400000000000000000000447601414524555700252430ustar00rootroot00000000000000ELFC@@1a2]3Y4U6Q7 M8 I9 E: A; =<9=5>1?-@)A%D]E  ]H  ]I  ] L]Mc:d7e5f3g0h-i+j)k&l#m!nqUrUsUtUuUvUwU xU yUzU{U| U UUcsgcs`{Y{ScsUKcsUD{U={U7U4U1U.U+(%"UUUUU U  U PUPPP#&%UI : ; ($> .@B: ; I?I : ; I : ; I: ; 8 I: ;  : ; I: ; 8  : ;  : ; 4: ; II!I7 $ > .@B: ; I?W SrCt0Z0C3CCC# C 0FZk|  ( 8  Y ^h 8ZX  P  P 8+s0ʑ( ZbZZZ0Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80internal/btf/testdata/relocs.c.unsigned intBPF_TYPE_ID_LOCALBPF_TYPE_ID_TARGETbpf_type_id_kindFRAPONETWOeBPF_TYPE_EXISTSBPF_TYPE_SIZEbpf_type_info_kindBPF_ENUMVAL_EXISTSBPF_ENUMVAL_VALUEbpf_enum_value_kindBPF_FIELD_BYTE_OFFSETBPF_FIELD_BYTE_SIZEBPF_FIELD_EXISTSBPF_FIELD_SIGNEDBPF_FIELD_LSHIFT_U64BPF_FIELD_RSHIFT_U64bpf_field_info_kind_1int_2charss_tuu_t__ARRAY_SIZE_TYPE__ttype_idstypesenumsfieldserr_ambiguouserr_ambiguous_flavourbarpbar   " C  %"D   U    B " X P  $  %   !5 " r  $ " inttype_idssocket_filter/type_ids./internal/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 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:1 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); 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_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); ddTt[I " %%F%}8%X%x%%%%G%`%%8%X%x%%'%H%a%(%X%% % $%.0%(%SH[%o%(%8%H%`%x%%'%@%\%s%%%%(%8%P%3h%Ox%h%%%%%%S %<%G@(%hD8%HX%Lx%X%\%`%%SpI$%^%8%H%p%%%%%% % (%2P%2`%S%S%x%x%%%% 0%B 0H%h 4`% 8x% <% @% H% L%/ P%N T%m X % `8% dP% hh%x%Sx %?  % {({H{h{{ { { { {( {H{h{{{{{{{8 {H {h {x { { {{{{{[ { {X {p {{{{  { H { ` { { {  { H  h  { I& 0P X h 0 8 H p (8 @^ X p  8 ^    8 0^ H X  #{ &{ | 0I internal/btf/testdatabpf_core_read.hrelocs.c / =O 2<N 3<M 4<L 6<J 7<I 8<H 9<G :<F ;<E <<D =<C ><B ?<A <@ < X X X X X t  / .     . .     . .     . .     . .     . .   <  =~  ~  ~ <~ <~ <~  ~ <  !f~  X~  X~  J~  f~  X~  X~  J~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ .~ J    G(?07x  L00`    & / 7 = D KY l x                      - 9 A M m y          ) 7 B P  (0 8 @ H P X ,<L\ l |  0@P`p(8HXhx(8HXhx 0@Ph x           ( 8 H X h x           ( 8 H X h x     $4DTdt$4DTdt  , < L \ t      , < L \ l |  , < L \ l | $ <  , 0D H\ ` t x  X)+   .debug_abbrev.text.rel.BTF.extsocket_filter/err_ambiguoussocket_filter/enumssocket_filter/types.rel.debug_rangessocket_filter/fieldssocket_filter/type_idssocket_filter/err_ambiguous_flavour.debug_str.rel.debug_info.llvm_addrsig.rel.debug_line.rel.debug_framerelocs.c.strtab.symtab.BTFLBB2_9LBB3_27LBB1_25LBB0_23BO@@0Rp>`x0"    [ 1p j7 pf 4 0 4+L  5 * xA+ 8B`LoB#`/(golang-github-cilium-ebpf-0.7.0/internal/btf/testdata/relocs.c000066400000000000000000000120441414524555700243070ustar00rootroot00000000000000#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; }; typedef struct s s_t; union u { int *_1; char *_2; }; 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__; \ } \ }) __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_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_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.7.0/internal/btf/testdata/vmlinux-btf.gz000066400000000000000000041472511414524555700255060ustar00rootroot000000000000003gst%JQ%  s袒D1 ( * J p筩a5NS< {ꃒ^@?_߼{0~=ѹ?@,:%Oj5ZQ.'t)7=7o 8ܞ5\M5\fQ < ϊy# \Eg; #K8wzI30ʑx}{KS,I~rxKC kR5`uDGfVa='/8ZK.?%ol'KZ~B<%:"7M= s\!WiFO?!u uSjA:7qd!\m~2D[ʴ[~8=e&]ßALp'8/9Ÿw+_wFpa^C{K>:!VUH=3op&'1_Gis f4Dw<n\s&{;lb+Cb7H'(2ȹDf~u~R_^} +t'l& 3b~xތ ^7ǨƟn@O۟= 9e C1nb}!q *Pf$' |eZǪ$]bmI g{ϫ#g\GU 1}i 2?.<,yyg1o K|y7xȃ3?>0hތE^$1j\^^YaC"_&򸖄"kE|{i6w&vQG% ~RcO8)C؇G p e >L9vCIr*y6F=gr/xwL]i]My/p*\ Lp(^I F BTd_\d^$BrapN$8 O:7L[ɫə}̳0'2O{IC&ɳȳNbY,/0½0. /$͓iILfr:( Kw!g"I_4W56T0#~J {{g?˳C{ c4NJ|^d_uD\|Q \#Eȃ[3]^wc~/\cƟ@Mj~7qBnOI`u i$h(% R6MTc8}n<|<6uc^oܛN>aij5\]yrx&%N;anwΧ4yVNܳ3~q er;iItʙ~zG#72̇+אٝ?1&Ox{k3R:cpn?/$ ~|/xyf^Z%+ɘ[~m˂D'o>g۠lt#xe  ώߔϖAlB>g:GqLJ]XKy2SF/e]K\v dC=FvE|O-|/S~?)>."|͇ |Utν9 { V*hmFَZUQvԤZ']1cu/?\c%bgrطO@a:g5x5q0)P[]ƸzIƵ2ψ ,cے{IOǰ`\77gE?ZebE7`)dKb}BnnL NQHqt~N~ <<܋ 2HUg Nσ_FUJ 3*7/˽f8"O^+N{e#]<[OF5 -B iX~a`am*Fm0&۾?+:VƵYd7߃+)npD)!"7Uʼ!Hx͐2;Vo]i3O뀵<)у^Opb:D|>g,D?g$9'^5ֺ̖P$ev[j_jޓO#r*͊٢͟9v&g @?sYpc8H1:vhQ[?-0v sg'?Δ>)#+G N/zYp-(j|0/vt|V {ل+uᛑ3z8S~yGjEÇߐŖ>BvY<:ml;RQE% |:޻ڮ(>`ҷ<uXʫgHE'_нg_ATg|?k xazCd_C[2שTDou5_Ӽ_ki[Vu͘Z4kˤt.li vCpO"dZV3Yj>ղbnZm(} s/xȃ>6oqn$ HkJu/:-E<>+M:W̦y?g]x)<*T毒z m=|GHo+xYg bwyn;}rQoTqS"g:ZP:!aztl&&y>QROK\Q9/u0%uݽK>;w1B1? > ?:_&TeLer\^Y1mۦ=S5MSUicSU_ZjRu 5By=_DY^GT;G! 0skR ]IEGRu"cʥ)"oʥWg˨oY vr'd3BqWʖ̣KN:dkK:uU-z{ipRyfR6~7ᒵ%Yn8\ʖE Yt\)ۦlzywh,}}[Yzo)[2:.\3k;XigVΑHWL*~ik:m/z?GǿI?C:#3ކyliNM+ͽFmYiUsJ:o)z2*Ŏ ?=,c3 ~AGi T&\,SYf}cIm~\kG]iǤ|=,/Lǜe>2MXLFsG~euOޜ,\VT0, Sq3σeet(},m|+uχi36:~Y4֯҆tl-IފnGZϲX/Q&$8eiWc1Wz\Y\rϹ;ZZى\c.N7p@Z7Zg _*SZ9v6py9k*s 2܂3xDPӥg9xpM<OP*(yy.~S_ϓ.,ge_:׬"bc ^WА.Dzo ;!{i(m@&}<5hscqdOL)ֱ+ P%Oyp>qgY]%P%ogGk~sDzwN ~'ɩs:;:ⷪ-/COnɲ>v/Ŧ6_^ׯZ6npL[?/}aW1{ޟ۶V V2*G vΫ^;%s 7Q*M"$K䆑S+> vĂ ZvVWUTt\Gǫ&L8p"?l 0y #|~ȃ/1+{ Td .Q2DN&g3m<ƮFȃӍ#i;ncy#qKt?3IUL해NGӠ2'+Բ'jY݃{ͩD-hRd^0P˞%X-6tU%;\IÿUHkV*¿DeBeVeye;ﹹ C+3ЏUU^ߧ_Я_KWQ*WN]լ_ZUkY0VWgʬYUU>fU5ڏS<ԃ\U;TյCz9JҸ9 ]܀jgrD5][3WY>1;jjZ>Vv|5:ƒ\r啯M޺u]--R޴8oiuczϪTW:-/)5lX | f I ۟l+Dّ5l2h݃ߦ?cl] b_ökGD&}~n6 (rx{ w_A6N7U?V5TY=^a5m"|킼:!Te^p~+^S~Jʃ55mkrg G\m8+Sw9{`MNCkMc5xˬ6-꽾2籚֦} 5ZM7SqfʿUWˎdgSƵnʹ7JuÜ{+GW9ϱ:Wc{g{,s{,h;!׵7Hemۡz`Nq|-;|ҙyR=>YKT˦;/ dDqzvB=K=C+ϦJ;|6Y)~xQvvmH_Ovmm;fݭmO8mγdoYKW[Vbm;o'>89>pufڼ_[yx7Y|NO:eqp+Y^6=8Koщ9b<un02'ySPN:7`cPSo\'<hCJF40< <߻먝"aCK::6M^q4ytxʝ;oNܟ\Ƿ|ru՛u@uelD]mgU{ɺ u>婀d/_e/G-x`)wyKuK+Ur8} \7OUzGF7gog٩_aq?l-nv]*k1~ 1&~o끍ޮ{|r-zm?k9yi`\_g?$pFۑY?Գu2q3:h=x3o>b +l`F|;b ~r6*PGy y[B^Q=VC{f ?~q3̳ohZ5s=]̖6v@Cnih͚|;PmB̆+ <EG$س>bHOnNaJtr@dm9D@dF,d1Xy &Ӝ^eyYFCy7F>=G"|ޟ3t^?|1/e]Sc/8迤i5(J n@NosrN: ܘc驶 -<WXxL3vOeS=n=nYRv$?=h}C3{E^2*Ķ嚨EY䛒3mĶ={7okbqMȔ&ޞD&6+0?֓775kw4mЧMltc/V֝_phjjj[sYS[?WijMm|wj:eȃGy$#fGb5GýͭMρ0̨OOOWF;H^DI_Yр&^GXYLfXɢ&d^Ozv?Ek3vt*kg9ۄ׫{:03|~|KLgɳRA9d_ɒjEIKiT ?TݮM_#Y8pf%o\cKm̌ 㛦^Okct<.'8.x|Vޗkx2Yd9ky% S]oƀEWZ.~2蹓Kj7s7Mr8=LlYv٥'Y̘tZPNl3SgcvwL1ScGq'GO8[ٯqsyґ񿆓vJa`Ӻ7oФ|õR#ߣ0j zn7oϊ߸n-rFo5R70U2W\-N};"ۜp\}5g@Kt3fJKeAK-nO6jKK?{[r<-5$MAV֮ʮ32roV֎0{3mCEݺ?pXwV6;2o߷*8sZJ\ri"֟H95}GΩttPE.w7U{\65cy#ruӆF7:r9Mu93gM>Ƶk{1_Mv\Ti辱}里[#[w>VY+kJ?v&fnG'ZYO~^\{iet:qOedb_/KC-l|;o]+kor&bW1q6:ڈZ>m$^^ ۥVz֔ј`ZJ܉vXpseZ=`EpZmmm,ygnm nM;譍7{-$m[s+#[[u|kkڎOoegEw"H$Vxk} >o^ʯfMcp1w[:aᶃeow֜~3^o= )6??ކ;8ڔg1KYl&m8~ PR'7v7( ; F-/lNY9,6d;?F^jh?E71,{)sG3ֆg.圤Om8Nsϵ1K [dDe % |j ~,_uX2_jyDU䫛CnP ,w'M5^xuh7+c2ϲW%̏8VV8V=" +aNr36/zIEB;nuKۅq 3T;WvzFın;=Lꓦoc,tja<)FBvoomq +n0E8~ipx,kw׶*8OgGҭ}^# vAr?ӎcȲ߷ڒ2=gd9.Zƻ4O skp<\F ڇ:dbcdYe2/g=_<33eY;rOg YƖv6c;j^^m. P)Wu~ _wٽ2ٖah:sgGM ))= >sum=ٮ-6Wvc;۽/v<~>_jH~o<\uuam'H_FE1te${"d w5YQ[mc.:m¶)w|S_=Nݵ ܦ畻={=p2W,9sօi{w|)ߠ]tDցbX.y* aMYf1.iN|e ~GGu`w]" (/ 7=n\owyTF[79:j 5 xhIw="yݨer썺/,Wܨe5,繍(c;uR>kn6|]ڝoި.z|FA#gu<8?ٟm{U~W|nnզm]:]yaSw~ӕiϰezOp;jUaU.ԍg.w|7?kMwӰsGu&wgYW%l/%l0-E?{ɢςY*Ccgugnn_vS~?ƟO >MőiK&unm5E5E':"vM>LS2NZ]U]ռ]վ#uvv[aӮv `;wt&ym]W_S1P9Æ7-.;,M 3!I=mӆiON=m{eszze1˔=l= >2 7tH4;e896'rG|)#r/Oɻp?e+Ҷq/":Rٛ{i[9]d_;l})Sz^ܳ_ߑg3";$1Ok\/5k8/Tn=9|S) .9 1Z6ܤlzߩopKh=8ȃGߤa.bp_|\1F`Wy}puλ2K &xer鷸\w9% {<fcN繯8qh snĽGCm1qҽuOt^[fOp=11\ k׵{n{7]{^wt_1mL8L_c x X? ~uk obݾu{&m[Y樓_ߋѺzVdYɺ̺=Cn/Xϕ4 _nouN#'4}doclc)ݸ; iGǹ|)gT,܈Xp/:~^_ؖa?3v>nvW}8c̼=>WQKDc-EޥJ;dZ9ql*?WEoަM.}ߛIYw7x0}횦dɗgC/|y-Fz=RϹzNgOR7jn~Z{wby ܛ=ܗ}eNNZ[:%]&^8Cwǟr{(I?=+S6ϵUghf<]vW3<<[3mûC~5c,`K?[^)e}~#ouϯX91Pu`b;aŸ誵Ýq^{N3?!$ιL%^/&Mwo'$K(XmOnK_w,߳\|{\e8֟c}s,Mp19"O2|`k7R K}3 +YLf>,'Ef97 wx_8̏g|q ?^ ?4]uOӑLw)t3)|k%f-A l#؝nu G_`MglQUq(r)ng:r5#8r!']g;ur{~lEӮ 6GǑr&-(mNQ#6m#}y}r팶?cIЧ<-/D?l_My"JqǏȼY{Qgb/?n&]UDs?׺Z'_Ev%GD+Wݾ+DYu~\[nmAhk8;yZ0=nf6ϋ_7fW8roE e}uFi3uvy*3|a?p[&fce>{v'Z/+M<7c#p;(J܍#Don#I7?)ks?+'pe4=8m K7'}GofSQ6wlqts$?dpȻJ gOp(l|^dwۺS6{o;t\]Aq?w=%rac^pKN1Q^ܹ>y7Ъվ}]^*X/{?S4e/P|`ၺv@ 16uʲ@=;|@Ӻgݲp'y Yꚬꚬ}5N:YAW5 ]Ph+9H >H5gwuvAa GgsYORo =+AzAzAzcB^.bLm4qۧG{=<zt=Zqcnkyֹ? Χ빫jGw+>Xҭ:Xm3X̓ Y ~{'Qk_Z{r @ŵ?D5ĵ ]zCGϗ}wn5/ r!BKsCtl1ƑU6Ѵ]dS݁!v޿C0bL'q<E"n6>'$<ne-C[/-v('eSv0[~N6̳e=ZpS}}3$:׻Ye:2m)6!S~{/kMýw)r672 :4Gމw0@p3JQ a[kxUavxp{4R}߇1L䏇=[nu&4͉mNj?7<2~q#s(8l S,uG&dƱGaִ*h8euwt=nk\uaQ\:B/7b%#taD℈(ϐ{Bq6BQN}(7gpxl/YBrLU OY.J"?T3r#\{އ ߗd)#ߑ%oKWMtϚ4gM%hȽ \M9?ڗcYJp?9Si ϼIҳRR"6oi)p+;mӂTŻqE_vfGfJrYo=F^ga٦H{7ܻcԁg پ5Fi_bƪ?R&ETK} wcLGnA>hM{|.TM\5V=fmu qc)6RreYk1%Ѯ$ؐwLq.2h}~UL?_1*'RG:qX#Y|12\>Kྒz搤K%}aĽ̽NR쮧؀c+|}5FṠ2>ۮxV?<&;#m}z"=1F=K;~yX;v,v73p߱LD.89X瀛SJ=*679}j3ȹnF27 /s ncu536a,_g׎{r=EL_Feϔe#g 3ބ'aF^L) ~9V}o^Խ%oūdIǭFG0KFoR'K3Lye)rNG^5Z3.O:Ѐ"/ N^&e *9g{'JȤ'NT=uc~$ܷ 8M4-2'D E'r-#26讇5}=y?L[%a 7^'m_՛ EWޑ:i]^,D=[b`$dIzYmpddD7]/nm&k߆ۭDN:wؓ'>9k5'2l>]։'/h}hS l^,\ʮ5|ˉ;ড়{4GD 0mmP YvYѝ$w>g}D̼3ge2vD&~97߃n=:μ}tToLTY>g0ٿp"VUl<(Svա,cS+65)߇z6q]AX/8O(f;]4 i+/ԭԡۆ)w %xvINb=cRL[*eԛKbLSY玎ONsҎ`\ i@pe|%LTʺ˼w'd<}6Io'队($?qu[7k&tz%9Ny C?9ygoeyroQȖÞ)' NSF~óSo?/Uk$WY+n;Jl ?K7i(em;X/|x\;^EF_XGGks"]8w y9r@&8׌O\Y ] whwwH ^ö՛ܡEV}y;ѷ`eYp֗5X_6Ss6Bl7Ŷ;tq:f{ct===l+{Yޞcⴏ"xBs=s8iMѼ'5g|q:_ L:),22|KOxper;p]Mҹ9q:.Â{?w@#=獺s S:Siݴ3)#>KɩHYYɳ !ͳgsN<8S2 u-:6S9 0 cr|7Y~7k8 E/0 5Ӗm*r P=5Um[Ș,22$ynfUΩMdLwLd.gm{l=#p=.1*=,b,%8c<{ܐk]x;&vOz@~2hH2ձi\{q^ocŎ'9i`VyN!'ObߍcGvѼ=[i~6y=vqkfL{9PGc'c1cmi^|Ӭ%i?ș:v^W:,msivmwi .Ӯۂty~KOq_p.Net %< \CoVBgkI:qǑ9"cO2'Cv8~DZyE=l}4EMsޥ?:<~-#|azjDM>u-|}ί8*k)7>k[x"Tv:CV!yp6. 36d{) 1e\BCS%^WW6j5wO׊׸WO킬@}K|6nayOptn|qB҇\4Σ7ݶARt F3!Yŵ 0nYrfNxI;+6wrN>INna ޫW% %hZEK5`yԮ8s*}Z9q:rݽO h 0wI{\PqiSCZ/*ȰAnԽvM$f6R^]ad x˽}w1M2i0b&dh73Zwb%1#um'f^d FmɢVWC2w=FI ֍2O,󅏑7k;z<)ކSHXn6YtV?ɲ88.IN7'\Q&oײ0,y{LAF]~7y ~jɶ˚d&°~&jL-3Y?,}%<~iwk YE:Y~9e\l:fZG5O1ֳN #{ >i;':YiOsH%,G?S+2>;1?˻Bg)sK Y&od^F4yf珍RgNR7D]p"Y\Tp! O[O[,AzzYW+PYW##niQ 0Ҁd)/2r[dceGoOz×?cCZF ?~SհWr+x]q̓4BKм7~Jм>@;"cJ_4oԟcmw=Zαy9}p&m$c51Ny`; xr|^/iVn|,nLд|*AӺWs0݇yof%Ѽ72}xx衲VAty_K8~R]sV̨NW"1x.\oCڽn[/uiqևٱCw/rA]QĞ3a˚ű7 ys>և5FO̵g@NN0|K?!#o'"_$)!WI.$f|3&<2.dJfo|Z̳kx̺ 2ގ]t`v&;Y<򑽨9{e<;Q|O3GK˳ޚý/32!g?,z42z7p犪ϷDMS,sYcq|~lw:wg !5~ޘOXzGͷ| ~[}2v~fg}v>la̞ϽegLzoGu}7ۜ7;,uB5[_?̻ =+rn9~Kwd/F9Cye#ȒRɒ%40HFKȃyDxgˍ<#/ckF^gh{r-<^Nv]x0~a@0}l^W,}wkEH8Ńu\'"+nd9$2x~Up=q ܖ,8%^ǁny'x2XY Ei[:"&G6E,S^Hסฬ[/x#ɳ ޵{ $GAr ?rlp+䁝N1n8Nϱ?MX?]uKڬT[Wvm`;N}5/-{:b3>b;y޾؎w?;rk C?R gdtE2ʓQ˃;}t#}^q^'^]+O,~EAwAK 2XHn^FVL-{p<:9B$K.u8>rz 4߶$bu?'"Zs8٤I4Y=۱˭m9iuW=w9Sm;ŸÃ~#˲vp2=ѦA9"ԹuOvdbuwGֻipcf_ӾMnpoqa}=BXfK8a;^kINȍ}X" =kJ>_ǰGnN0PnG.{rԙK7Ώ*ÚG*BVhhBRzNd }]g X琿B +t^ uW T۱J-7mVwܾRw_kZxcacؿR_Wj$΅{DmJ>#naմG?XGw{DS=(nv?`ΣnGu껏> WR*}7Uz*}7ëtlU:mUn ӹ ik1}7p\oqR\弎qmµ>濾 Wy]\"{@\:^k?\u=q:OUz?}ʑ/?ѸU$ʜ3pm 7W97})%*GU4Wc5)\**m8\u>w>p*WYf} Wyf))-pOZu\+\H+9뻒?p=뻸uq=A1\zO^x\%8{Qk7qVudo||eǗ﷊IF~ x чdm1/E?B4sޓ^hb+{3 g,xɏ$woKFzIۨO/Q%^6F{Yۨ^6jFQ_6FJ^/b^qSy^W{*;,XSݹ78%sMpN~^l\IFn[jW캸1/,Se~j;Y>%$56w,Ӆog6=8UDrGpr' .K.^[{{G{o?F ~*@'!% EZ7.^:~m@P"v"}f_LfJث`eLJZ~9d5:\ }n͑95v߄&ɽƶQrws1i|s)W1uu vHW[c笳=رSIGB -ڡΐYµCKvHƎ'j Y}u=saKu9QNǬ/⯖iL;(ѕy|Zc0kQ$bʞ b |3?M_Xb[~f[Ȟ Mx9$2nƮ´-wUER1Zku]d `g:& a< +7CY.r&9x(^F{ s?9r^G^sG 2_U#2<&9)Q~G.5K]gVZVZ8{zqOlDȷ`^c=V2&&7#D^:y:h~iNٹj?ή_gqik]r${p8ƯsUSsvKd+kHxSԭ3;ՉGo՛lyk53hߧsyZwyb'K3#a' ۆ*G=+$/u΅u6l){po~˽ƥziroI״Cf\)ʎ>JyW*8:|}p<~>k~k gw+8|Gާ3vz!|.o ѵ{yϢ?۾?׸Wo{;3[|#{DlSN5и˻o;'d?'Dѷyc&ok 0eO F>Ђ8qW'<ʈ2Y~a'm}o~Ck_Lt'Eeܤ$֫߰{]:i-'m7 7A`_lkx2?mz3B$xy'r;Y\,vH)C[/c5xS]Hx }燀'WƸ8mOƐF!sOX`#6,ySyx5y&8(Qb&3@ݫ(vȆ7y.x̫}x1%i6s(rvG>r_ &SAM3eY`4)mI,69GX9GXogWΣ \|?ux`G?sd>ˑsimųgN|X rs|f&;_N<#Y9nxu[%<;ocyy+%򼙞rtC ʄVCFkۿSy QFb#.g= 78رkO_ۈ>Q.c>Op5sl1pX~#.hAn_hǵ}D*8X?F[gP֙;6faCKF|v^㧬xϕ2ۤƼ`bm]gj>',Hۖ70n}AyGX3&;/8&?OC䓗}toɞ xN~#:8vqo0-B6G, 7.NnwϜqd޵'Ci`$3 4_07ɞ&}{r|!&7̕ &㖱d,%-iE/dhwx%H ܚQɘmOO:fkVm`sDO٤ nR?rދR9ٖQk{LQbwBܓ\i>\s wtԱJGCXK wO2-fOM}`=' Je;+89GlsS9̜=t- ~~J6=wج;L|,uOmzCe\ԡ6:4n=@o::-s.yۮSĘqǪ%iaʡպΛd [mcԷFfx2"Ŏܢ+igAKdmsraQlbsޛ~A:ͼ4 gܳglٗ&y~| osonl4|<^ -\O?G~L-%3-\}%%%|OȴpjwQW|_o1f*z[1mo޶cRg\#ZsX|\'uSr0sapy|΍J9G9meg39ӳaGoqq=ĸɸѸ}GtcӝbmΛJ-=0=US9|;i&p. dNCM9>.B@}/{֥O|y:7Wt; /+(qsƠ8[0Ƶ}Ľ{~?!뱒y)\ ۖko,e[(yձ)G^0:%3lK1{iX=:߳bE3wLsX;=Sh}o/ֱ4j|CV}wܦM;f>mOm8M6=BZTJbf2e+Wb9{Wcl(Z%`Ϣqe}}}W0K5I$Y?q`QN-h楓^ѕ{Mnml\=ԥ[|6ԇ6pUQ/gcGÍx{Wǧ)~ڋXO=$U_㺊XɭKC>۶Jj3^3 &!˚,s1غv{@,c wqPӨZ'PuCU?ap:An^eL7F9/Fٳ#~|< +;اG|[gawٞ2yV.2`IkvN*v=/NkiiV&blpl<z]î;8΀ eMNg|8fx];RܩgI~A-c7fˋqxN݃.-5U-52N{>ueڒX/|vr ~{we9໩}੐)oS߅a}YN}8۩󟓶 ۯdO]|25p=B]K}4:3v3ٝr0Ӊl# }gvޖՒnuO|mן${zx_{W?8#.>3%_'"b{S?{Fzƴk$Ώ^ٽFsMx?/zӄ-F}?&lxn67>%L8l?cu>KYRYWd;D,r?V3眛{n~J/!c2E ݣgK_b[XO$Z1~{Za{]5qG \7uM<{kg5e<,4fq=<_;;״y{w̄TʲH'幸ƕ<5oxos3l^=?FeqrLJBVzˢ:? 䵄Y!y>P&tO#YZp!_c̣5?5k~R?u轞4r^kM?/nrxMXkY%͟heTV?_~H>}|^ضo}uWp;I2ǹu2*e&wjXYǬޯ!g!o?[l4x ?u0B&afςf>Y J[d'oI]4b&3AۖUf=S'Aې:Oh7 a sc& u,rLS_/ܫk.~tg B=W{,u&_;jG꬯Rg;8S.AdqAk&f:ګQuNփo!&?hlV v? 3bFW_ߌRD%>/9yiwXIKS/-9Di׍{Qwۆw{{ˎA(:4?p8u(04.Gٮ?r~7M{;ș#.?%?:]~Oۑ.utSo⾏kGorfhНK+ˡZ+=KwSۦ> >z^}2>rO< ٱٱڟY>Ѓ>0gI1G,CguDd|,mY?8sovDC5>{N[K CN?)NN~ϡ}U&(z#~ԭ?\P5<Ȼ宺] Wͷk8tVĽf?MRROd}R'MZ-_Wg wyg*x }lDZG:7=\q=n}Hr^ʹs&Fw&(G n>scAs{.e=B9I{/l= a|aYldYW\\8.?(m;e~&rgw+p{=Bf0kk( ; 6v<(Kc_,sU'l2bv ;Rv\?PK b_j?MK[,qB&\L#Qng/\?gwHџ,c g!M]Y>K; yoޛ L@U_17MJYڟ迌E ܜ,O4ӿҜ:{SR%yMuҵEFZ/q->%l9{SSʼVu` z$|o(a7|5pfA GA_+.%{[^z^SYe~3WzNy a _}E[kAYMM<3^Qt29ϻL6OeD@ʤLH&eD-cR&2ْsR&[qBdkUHlù )m2َsR&'j?TdD/˺Nvp%9\ZqM6i/c?،nKVȫn6xvO6pK(BL/!fZOdI/vS>=+k;_H_}VocOK#t3Aoݘ7$ugސփyC[O W7$_ļ!7oeHLD;I+q%ؑ:-yr@]D>3ӱ:;yå.py+:\wMpmY2/7j'2͔]ON pX}/pa6e\ў4AK,Cd1/">\ψ =(/ mٳ2c&oڧ2bK~##I8 ^C[y;&펗s/F>w҄ry5U\5ObϬ4{JD}C}G:y2S>sO{JuNvXǠǬDzKm?5yg;e՗)zrMja{ |l /h\J'Z/gxmyMF.y$.Q>%s%\yNpXCML?vse1?uHmu^%p½8JZ烎qe9?ů#ĬKɼk8CBfm2zT:/]yج8`װ]K,sj|OmR|Vy/~a7np-xIhʏ }Kwѱ4{C_q}5f˼w9]61kkGliC#`,eJG;6k/nFwd6]CƱ5B!D!5d|Y#;sפ^'3Y$^U.=c9a!cʄvvNkس:,~s;>?c?jtSO.pr m6guvp 8,82r||'9La~ݑp,W.f.DD)4e{ʟh"RkIϹSwȭrÛ$zﴜ/H蠽&A.5_b[SiԗTwA&u?GsN?no\m޷K6пר'&:2mt k;zPd˽N%]Ue)|γbҩg|m_`g,sCμQh`dxyv}^ g^Y9`RX8l;~.~K=du[R˽if搶o=8_4׋ wﺙ}?f?4g}0 tJdy|gHl2]r/y4@q|,cѿamsڛpY‰8^dkQ0s&-9 |aqzcd7yQpX%haI0by~0?< ׇ^GYrIʋs s|ϰSLpEp  I}iC:G=u;ɻ|ϵ^Q Yٹ/\IjZpف󃟕4R;a7%;y$poHRV6sD[xd33he=B1;y# |M|g(|^ QU/⛂KzAQ0z҃wI>!y|5[đ,^JK?MKyTی<ZB} ( G:{?Rf%Gm?W22#c^iAWL`طDZj֓~j%-}*c1J\ibW|N,z8]1zsXr%ǟd__%)s)MA#XӒ\swȩ>Ovb.:y I:t\QJcoet*a 칆9Lg8,mo-\՞f9{B$<Ȓ\yq9111{vO3Nq[~>WzcE4&K+ G},'ƃPi{ofgKZkr_ͺI?1z1o '{ܞpq xo:ް3گuIF#'?g&"3 3m2ػK&CB\Cw7k]2Im\?#90|h:Vѳyo{7wf>N ۟?,~ٮ> .SG? P6$;˕dʒ%2oU5I(~v8Լc1-yǼcZN;r:F^@N?[_V T,d1cm q:Q,swK#{ZoqspJCE-+YЦMw i{8DŽs~նPce后eK!>{ |Idm+n^- yXg܀r)stMZB86 K;r譞|ӂ?5̡H>տ$~=}Կ,cޠ˿jMS~_7oZ:[Ӿ]'ӽioo~W~Nkn]dwLa}w 5s{ ;isZ${yQu:iO棏O̟p-ɸcştF^ƽ{3g4hܗѸ~F3?4eи7C~Qp<hChSԸWSOO?5Oq}qymE7ޣ{h|]qVsY ó?/ڴr.|b0|E\iq3'?{[Ǥ ?0p%!8ltn^?%m"lK]qwYUy9-,ntzQKV.|Nua'.f g㒕}ɆK6d봻"164Ɔߤ9 t?_t+dO_WoOG:'oGWs#a]_>&]]L|)s0ɖ#>b/EթKY1EӸw:Tm7^z;"<?qS(r'_v}E2qu9ϑO.2C&my\%p't߯Sz[=᜿ۦ+ʑyoto_4/z2J̋r~'+&㕿]O]qz7ow | DƁ?Sq]`.3V\sm]aʾ(te[Wll s>l늯/ۺ˶~^A'/|kxm[Jm1bC_;Feޒ,ΙJ ^H#setኞ-Wg_QE%8+s_dQlNel&f$uh'7&*Rr!o6VxiZ֣L8)D>{e״ 2 %lyl]-Ԥ=!{IzBf 8|ȃվyL+4biw1πv_lqs1Z/ɫLׇcc|n`.Ȉz`Q LF0o#$v:=A.P7 ]nr)p3[2O#Z6S[Crh nC,c>shdRQvSyVl6 ȗϦkFE%fw7܋zj!{k8mM9=ZٴOǜaY) ۈn#"_r7tdN;mK5ywъIA8BW/ e\%7DcK6ύcHbƆ[W;VW%{^[Diׯ7]ZGɷmϝ 5sM],LGG)G@ߛ~疙7Iy|GG1;" 38&9WL,< w1y-v ݏc?S'_6,d=euFe >H>g8ϒeO5I֧@h_s .J6؟%g5+wsEexm +gސypt>~2 3ƨ%_s%cd#z,sD:\3jKv Yɤ؀WGb5-,ENWvx@Eyw[(:D<,dHN'Y䝴|x1ط ɾ〖)` ="ybI ^"a?(a_$h4]yI8 jS+RWӰ][EtӴ8^*YYq~pV`Z&^qj[ıN ,yGU*s Gq본u$MIfpIOoU毇&igJR H8B'cwdz>yDyී(v؍{ɘsN%o ]_֜4# eO1[{NI׸$ |YIAD₌'E%\&Ee1U1^>#p=9.&9ɉ~$r|;9<C^J ~ )g;89Gr^ 9?/ P.RpA.nI. A|+xbMT՟cڟ'1VK}%*g=9IE33ȡn{*yR>{7e/8}os3űsͻN #{pn2crM,ԟ5'gx/O˯vb;ܞsa8:wIx.ӽ1z?\^t'm_qt v*Ó/?5W {,vXrn skxjF3d'"Cc)J 5skޓ{ҽan;^g܁^A-g$xq9 \40M[ ]ɒ'\L K~}C<Őv)Μ\?C/sa'z˫\'o |Uqp*l^EM}#=@-{:_neQx3! Cl59L)/rQ؞{]. ˫}~yм^Q LMLߙUE`]ʫc.WymϔJ]r] kz 7պQL9uKS=6O2gubbWy^Cz9q1~JYK"ϯ5y}[ڞWϞOc .yWIϠ{:O*3 c?cȧDl\א+g:ռ>6nOWJ(_nT֭s@{ߛaM^XLpZ>6-k[BY~NZR|I}8>M.;cgO91F-4o^L)/Rf\wz@?%K?@_ Kj1}%[tP8'J*01ap[FE^ _{?;2o!x{Hǭ>[<&u uodyTreyQu7YSV4R"ym2C;ras,{ *ƒ4҇,A'e,, %ӝdYY;\$˺W W =mGW !Sc_ GJiV4vfT%M_4e )uße#{\owӽc Cu0am+:LO u7[u7'RiB/܀_cDgX j:,@_.1Rg%\V}}Φo~ib-9&&u"%;ߏ<ϔm#oÜww&qGģzUsrN).gw͡yv~7Y!L|&{gίN Ϫ)' %y3R̳N_N4q؊v|fjo% /}'/u.~sRz] RF{Pj)#LpD[ Ng GF&O}NZ&=I=g7׌5X>?ECKg N JdIds K,e뙿J֐ik >pKL3]e#|>RFWDd;G. N.0%+csg2w֗,yk$Y˦_F2_tq~h&#tx;e51~{'ش?_O+!HZX*p?\H!//֎)5e9l_YlSG4:H4,/j#^Ge$K]ܞ,e?Yd)wO3@y?8.e=/Y2dϻ,R?E?xY!|a21#]0KPYiUt1ݥu6AF<wez.ex,sϐQnON_uc?;OTб[)K_9*y|Xޮ "Zȹ|-:aFw4N%4 ?6Ex9tWLSYeM9S{ޖ:*CT d)\'RɎqܗJ_/9j0֮ \JTwZIVeLؼ+ǜpS.p_g@+I|ŞQɆ_,4t0dTҼ!2V ~wQpr'RƟcVA_wg 9CVImd|w%.`%.$Yt~r nd#g!Ykn!d;/!1́W3*9*>"6K8aw)cU{Jýt-{< qSƥQ52Wٖ,.dI^St宑 3HU캾dKNSEmT h'>דYVzW?b6W>Sڔb"\T \~ h .}'7UeQnJ{<,ٷW KǪ5U1N5qj"˲TEpj0uHHM"Qjv>VtacepjZH^j.(,?ێe}ָ7nAfםʜJdf1TK5;ETXq}uFVS.Յ7W-2(\zf5{Θ]+}[es6{so/Vc>Z~rǿ-];羓=?j0yYĽuqZ.k_.7sl?aa)!ߜkL(VQ2 7$k, n߆E9gYHG'k$oLo7" #Y5X;{y8s:k'gǗSpؕɨg9ld$[ac?_#,~{>DfC>Rb+z{Y &x|n{w373v^ϧ54?>r ;_ ](ak\3䦚Vjs9w!Vx\Ȁpk5"8,㉓jr<<_l?R^q]m̹E΍5yv<~P3^?G]?)kD3XLsgzYߧ&gQs&kLl`B v>~I2SI ٩ yLWKƯUC'sgpf<@ދnt.a43~ve5,x_?ϵɶ3VgZzЎ#{1 aBlofd;ocz}uMC{|﯆zX|#=H#Uy`i<?6湻y`4iFڟ{ږ_7Ҿ/4.6.Wc cƚ5mk70ml.\oV:_pX9~6Ȍ O73cfY7pσ>+<.3p(䏒`\$G :?0./:G 1B <+cLؖwKۺa~jgI79S$F{3/4rϨ#k?<lcp|/&R6+jü$௛#pdDiRn9NHQ%).uZ,6~NishK 3PKyRx\HG!B{`h-9.ςKt2)jKQ۠,XEU3kTF+kC}%v;}d`GWz֔KsIGؗsZmWؙ̦Nm/D]yģ~ydRꈗx j{SDyWy6xD^[:>7Ǧ iu 7ƳQ/O Oc/ҟs"П>/oΤE¹ Ҭu_^ x k^{ u/Q}ǣs1ǣioxI﹆1A~E} ڣߥ6y%hazqflsK|wOQ6ȧDžvxu~>b+w$F',I$c4Mx6O}Yr{PV>90Atx;;u`q };X« >}'y/N >|31ݯhn!0myg,$!>CR[+v4i|2<,?轙 qoy s\ercm(64|<'gpضy>m6G;>A;^/u8?qсnGOkt5vdCmWѮg}b/^GhXj|g]%Dgܴq-XrϠ|}O?+S4kR>SRy?30vGi>ZSDe*_}GSo@ӎGlԎ:,oa2UvgFe]-Wmߡ싖׊y{.u~r.ұtR?F:YWHgK}'A[:|csg<=NŦf+AgPqާBTw8^jFSq7S5ٞ#]y G wX'G1E_ENJb#N3"ȟ2҉sc 3f<=}%`]2{_gxy=Uxď節x?1ʝy/x!E=gy>0ʠfexlĜ4_OZͻ4.ڹ}/ws}r-]cK/YNs܁M4)G]N׉m`G?T~.tX8֞ ?: ;ۼJHN/&#-\Ϝ}óh+w;s~JcKv]l |Dsؾ }rq!p{WlJ6\vޫC{ᯔ1_4:1M] >nj?x@{5=xO/ :uAj>I:.6Nթ_Neq:AW~uC:W?I'/_=%t2XA q)d<=ús+֬ sdƷ7):Tfx?xWx _\F <`Eܸ {1 gy !A!!^ЏOΞ3v3@t djў _= 52E'H~b_26i,2gXֻuzԋϒO>2u!c d_y>v/9ρJ #K>kR,S:1:hX>ŀ'~<‰xzN&ݭhsfLF 8L1c+(EmYasFSƈ/~~*I:Ha˼eD&;Bk#l΃6w _#,?슮Mg\N?triڗa:]߫reHkuaWTפ(71=86wi)#qB x{2N􈹇xvV{62 80auQ:2#5CSvu~:؉8{tzgV=Af5sڠ^v0\QN|S5~g>'g;9TE"JwW3FyX۵ue期e4w1/0t{voYO*48(>-ee-Y>ԶA="l4`ϳ\s?XuqeT|+XHlـu+stgk2}*ߙm[c :>cj!c:'(7 o4K|tUoO"ЇYx PEn jn7#NdsPxMe<~yM>5 ~uCov>|/xt#b;<~,^xt̀#H R\!ͨcQ c1]L`zT䰟GD19r9zM.ZG:doњGωdrXx+c!=y4<e7=8ϒ&2FCrNy9O~i13]d_/A_K|5NGk{AV]yUgLXU»Op~dc_k|u*6}mmz b^#yAWs9_.ڤ17&{T 8J+W =j3֐cN%]}:'W"/QϮqIz[t:t?m?[Cv;ajOn^і6?;-7;вanW|_|;|O@&֫TnFDed(9 ^R</y +#ѷ^#τoy_ 3@?p45Wi>9m?qfmvwgo  ?اh"6/3^Q[x?ّ{o.||x[o|T?GymWƉwFMume60e|>2I?T~I?tW>釖fE?q~䛳#;ّ/g|[~9#0}XgVEȧ+c,w *w ;&D̾-O<#1~2nu9t!9>PgGL]En>˽:uy(+sG>]DZ?4n[/0okbVo< K s j ) Mνc>^eNL:ˤpG =b'cBsSHp8̷>ux cygx:sy\^vs]w\]f;1"/;]Q%'tx]}_$tRWgKiMf=:vXn'SŽ3? M)@xFM+$QH|BґYHrBґ//$ju$k.$Bg)$?PHÅdQ=g3Y":1Eo&Q*1z !l*dVuWDEԿ0p^.&gB ³ /E,mpc! a³!,p@x7OT 'ќ)-W!,vGB <L.P!.S!1Aw\ op,Cx9@80 — LF: !7֥?BɤPLŖ/.Xl:d/LƺTL U.-:TC9n 1{,=ܼb|Y1wI.T%%W/!^QB{ wRYD5 JJwVjoعhlD mxӝx3Bpo|sw, {\Ѩ)z#hѓ11δLY/&=9 4xx=tWGq}>v!Uw'.FNFF Wxv%e%goƣm_`9ܜ?f.wE{w]6ɋc;ts/gW3F~b ϘW1N(\oulQoҫ[* >[RA|lm ⳝg+h?ZAcJWe$VRWRJ*ʘ^Ie̫22^2>2TR'WQݪUƐ**_EeWQMUTUTsUTUTƾ**]5ѷʸU0sid5aެ9jt9jh^\Csi54G}m ?954Go_Ks~F4|Q-9\XKuTGu::_POuΨ:WSgSSS7SuɱzZluZh]R@) .%6к_V4Pۭiki@mk]|#]b#EvvnHmP#ݪFjFj62\Amv%U E O@lF@7܄d_r7$EP =EmGdAf Sut sNR]A FM FFE2Qw``݇.l!bC;C<%<e!چ!idJ%FIMI〤qLQҰzF&fBυrCQo9/CQ]((_Ceq(! e1 e2=2=2}D0<ք!a2!Ӓ<G Gc‘pp+| o˨/#_G\@.HژH󑐵HZf$d&+i$dL$ds| >g ' >(Fm| hG0/>Dj4|G8 51 `t |b$>1D |[,|cx4>D @],|D,|)Haϥ6)I,Kq'< [YN0iCAn,^/)+u.R@Wpp ٷ).EaC Y۬EB!p C!/0n7 C)l_\Q(hΎo c)lp)G!MUS.gSx;+(Ga_?uOLd~Qd>>2_5282Cd7^d>2?.2 2=D2FCCOC/CoN̏N?_H̿,262=2?*2?+24zIW=+W%WD~"_L~wޓ}Cv:@^y]v ILBC$DҘ.qP$i$A^dk]$i4Ig$(WdP2`J2X:KF$#uɨ7D2bJᦕf:QV&4c ZrHRQI2%~=RxYhon;SnRѲѪmS0{O_nyO&y̓V N/R$z/uoY黁Z9[G}ʞ6"{R/_Hz1?T[<`V9q ;R}L`v>1OtMHP)xzGWR*G(}e.9~ #E%ty:{C;l`;ƾ揩lT- v; adt4W `g,#sHK9R788_qH\^5S=^ !ìZ-k|ˍUL}}߫0dhOՏ<5&븎_'p/BV{{? :۬Z9v#rtzS2,F tƧCz=pNc32]LH/ /uTΟu9"g9 :ڙ^JG| Z,x|2Cn@_7ígM\F~dND eQ56m-zi c۵nehKe{Te-g[A8tX74 ozW"x.?=}_.ߏ;=/?/ ݮQuvy 2!Sfd )BRh2ц57fb|ܕ `&g!Ng†%S]V!Uc*J)Xh&GhVP?qJ$YcBY6c\h&o7jy˲8x =cn*YgAAOl1wYؘx=k oƞ !ގY<zFc{ֺ-6;]vezv=4ͺlk:I:<Ǚ3a`{ye`B>'"B㤿no? o=G~̖sn)GS#v$O; 'x ᳂lE g{抿\N| 6m6 ѹ q99oB| }{.y:ywbAȃ͵BViyXqNjCMlq_,?e.<=ޚ%#|aWYTreDIH?7eDyB#rR{ |ʣjɐwo~fc~7leuK7cvSо΅sR'^>}.ۍz^=Ri'*7E)z*n' )z w[7,=~R-*Q_TD=_VWZ%{ψyM{7Fex c) gRUL]ZߑݸӇqZp\+XZ0*D_ZM\9M۾JO;+Lׯxk_ɫ7?ZUW ۖV=R`Tb-}*W0(8̕wLO{> *}+7 f4P0,+~@zQ=gLzR[nYByK?v]Y#=]^7}`UƵz{﷢5sA|Xgנj{ȜS1C@.jN]u`U`[ wjNI w5#UuNNN:p[ӣXcOsbm̉5F'w;kǝX3?Zw-{bm|`-|$#k>kkS5Zյs&ӘZ}cZjQ/?J^F:Vs-igLZ Uk5}_,wy=V)kyn1;G0E<CZN? % eG!sK6çN8!턟׊H#Ow|p_yvo"_'Ë7mz~/~}vÜN&u0/ׁG>߶Cvo Yv.ÿ~e`+n9!F:M]z'~?H,t6tvxPq7Jt᥽N،I`}`Ϟe D D rtA2|i=N؟2zf վzGӱXҶwg[|Kť.m7si{KߑȶK\]_|i[a;봏WNg~Yu=n;V'SuڮeݽPs4/uyuv3 P70ͮw>^^zsφ I^rO%gyP*^ūm4v=$<]hgm(޷ Uɳl ȸ^m6d0fQ}g*M\Z|zY/ (C@|QJ܀'h~^!C乃bj  ǬY#\LإLx*ڣL T:/tw L$p# "s"A5xOo2#8O$;$"y5;暧0;kZm{ U6\½7JDT_yPy;· =GNc>:pGgN~EL8ÎhEp6Cxfzf,}f7,>C=,efDj:B֔ |&׳Kbf~%D±NT~Ƙi)vC_;ruWUesn T(0*3vQ2 bQD8HKHoi HB#>@}kw{}ݵ^ow~Dx_,?Aklz Yc{`w+uWqi9NwNŕF]t\F"-ʌɄ[1gan 8hX}Lb {v*?֕)v±AoqP+_14Mm$3lGt8c0*Q볎Oƌb:wupZ6 V!ŶS-jKSq_gˀ^eҴc__*wE;XY'h]nƦ%pJ )v{d8=Gxcz(.#<<ջ`&=յƔN}}Dgq FKs}6_s}u9UQl-*oG;!A s8 G<[p4ፂ+G8Yx@pwGR2ߒ*_J5A/yCfd}Z1aE:yk!)/H'$8M]:£Ix4SN7^(8 g(8pq?ݚA@ㅞ帝ɹw'DKQoBEv]#}qzNR%2_ ~_%لg ~<L&4s ~p=i';_K'~pW%xAIX"cO ^H8Q' ^DxO :#\$x Z> ?Lp[.G _Gx);j8 _OLuN@Ӥ<~y ~_¬͙kelS4̈́ ^G,Mi]iˈ~R`QrmO:pN"tE8>e>*^J랎R'9G9G' #,xvq"$O;F y$nOǸ@Ӷzۙ#+(]Ʊ.CqB;?%KÙ?Dws8M4yO9޾|:H&:~ YNnWh+L(ouf^GyOpo&\0yAr4q![ܝKL{%ә LxwC÷Tm V1e=c=Gzw-ث f7), n# !֭wc{rŽU<"Xw˂'-e{DNr'm{ءX)#/  3>\JxTvV-oBxng;h \5ki{ç4XCS73o FAoI{yo,8M@x Z{ Л!^E5៟ߘ+{Io%w6`߁7&0+6l[WmѹG)8A—K^ ؑ2iʪ|1w,֓!ԝ*Vc{d40}—)c~>WcpD\t9ijp=7c3gg e왁 82~: Χ70uXo`xJ2d3!6xx۬Z 2d}28qSO zޡ5y^N#:xgB?kŜ}큙)^hؼ4\I-vOc 6꽎Sy>n7ՅV|,Lt&|CA7$xI/niL~qZJD2bpy''U9gn["1(?L3QVg_~~]&Gaho:3l$}({936U0}[đ::z+tbNL2f2v9"eR_a#S,w`>`޾X0{00DIr0?$3aL/{S zLޙ=a\jLs@L3mA87[F럍262el|6(cwЬY0z~qHp[G(D{ I8S0_#aǝJ;Q >V.n ̶{[RŜ-i}SnX[Jkg9zfm۴^nD3㚡э|#~?GԌkt;G7I?.mZcYlG1fۼ1h)> 6 g̒ft+6C棛;[:^Q>{i>+hՇgfد я/Y5&ER<yfuk]KswoA7'GA$o>^^_`ͮ.sos01l[m\.ΓvŹ9kr1 ˽8g;9l98gZ.sgb&?(bpxʃyH0v}\RKOĘ)=G;ϓE<{ևP +gu/…x1qſ_g繡3ߴG]cH(Ϯɹ/Lo,˖۸>'Y1]FzU2fUyٵZ3 0 ['gmBRo?#ߞo]oA=gOޜ946O۽guqŹ;s^OOh'<~l)!'~£ ? ? ?OrvO$\64*s/q*cZ`M|OxQ^/'?[]Ds,l0Ela}[ <yV7J!zg*\ W*zrߟ~Oѿ<'nQE`N rmBVuF{ DXc_\r#e]B6@_>p`NfrӃh+@r o,)@\8w(p  wqW*{>=5{>k!s Cs B;Sh> ?|͛L+}04X/S|ogY*lmr{PKO͊hUL]ڵUj^Ptծ=ڵ9E8kq# _\$0*m;MWWn, 4[w&,4tg Jxn\_@x]Q&栅E{E_J0 KFm S.9߹m_qooT^ :!}9`S}䈒UF7)\p… &ǡ,&$x6bqy%>T Vxžv&IO7b_sEAʜ?юOdcܱiž%x6uGnšMb߆W:=oLŅrMsv66Н0^~+-1ol[qKb̃/+|q@1⛋1)Ř_Pqԫn轭bX6_į)+~]Vȝo%k\ ϕ`n;se%`̕[<|WعOm)s%VwDžy^19sG_DR|2:8]G'J ~}pQ9o4\9!=%S%s'}2J1}틷}1sy皹|R{R{^Rxl=4FZR/Uǻ?z6ѻ9pL_o5B?SH38[ڟ q2}_|BW_8'aU՗ݡsXgy!"ANL 2cdwf2Ա?arM5#0Uyx6Ie,yM0M$` $` $ ݩ̀3#} aF7n<]pse9wwSx_i% ?ㇹs^cz5ͣډ}YAdͬr}kvz,WdpS;+IgmmoAڶr۟lqW(|“ ϫ?Ϩ UxOΩ>̳ˠjƻHWT7$?FoHdC5W5ސ}k{!9_\7$̤!YRc~;aT4 $32^2ϳI>b2!MI&c3-)|MyC!lR"cE2[%lR`yJ&&e5lR&=&/l٤p|&Bf ʤdrns OȌ"zl$yI߸^֗M ]l'Ȍ ⽊~?)̋j7Iu"s۩ީEVY-C跢n3'>K]!'r`1lj]{NuW=~TYu_WyfM$|!p=3{`gLt7x;r5@ AgCvA].] rwA]wn{n{nwnWŸWߘ=K{{ | |ENοE8_ Ÿm{ᏳtF???OOg~~~C5b~'f0/z1NJ;41ի'J}_WOo|^>5ꓧ^>k'{_='\L4~{oy^ۿ/u#=꺑Sq9c^9{u3u݌U^}N 4aض މuu^'l(~¡"ޚs5:{⺹G3.GXtA *Un7(?zܠ|Û7 4F}hn eIc؛.l67),nRXݤ.`sVmjYl*Cr&J׵Q9i{MW]罛T,3sy/ĸVdqO8e~w$y|qMN3n M;t2>o*3˔ܷ<ƴ*tܻ}#=eX>g[tU`xp_@L8DlMֳ&*p͢- >u¸N,c uWsAƸG -p`-sA2 q/(W8xSq޾x-=Yu˼kk-sx'_jAr=uhYgh6=´*Hqswe;2wǵ~{ԗߓ,ғk;᜔ӬQ49OÿW\kST܅35'n Pg`26a^c˹qDZ Rlh+wlM<.ų>XNNm]6ZŋKⅧBD/?^']3óGz`Ο~^,qD~WN.U}LJR1 grْgʼn.+ǥ ?#ⶒr{$|u,(p5cwq׼v퓕.ק)ONٸ8вDQ0T˔ gV2w)O ]'IO .+ʼ_}M妵˽Y ; _+~.gޟ:,< .~`}p˾ļC]{&%{q͓v>nf_x>j<U.}]W?3=w/?pSr}K {&nnz}߹)2p_Z!8~Xt߼'VQ(*=j 9 *He#?G5,<DਰEŸf5ѭ":?%3w8ɬ8y\Igwg-1X,y` ?{@yʋV+'UoL]&l _݇ = _~h/+^>0>_xH/^p1k*Q2o5-}R}}^Ǹ7L7>A `e/mHncwO5u9s9Qs?Y)kXmW4ru@w? +M~6]_t<{&w[)ϥ ߕ'aȋW| kJoaTV"t -C_'"3K9<2eYf;pZ(3.m{۹6MmSxFv~^*_,bkY}ej/GGaDrAvmc~O mٍe`Ӄ/A?Icߕ,Ln+iɜZ#rRX7,TpĶ6s!b+(Xq:)~MٿS eY[}dc_\'*gӿ ߅f0i6 f2Pm`,D{`1Qw! wPN}m.6^`{󭬎_ '?W-,?̐2dz,m/_ͼ5#! -p|C=ꊐ0t@F7^Lr4Mgo:}t2۸abtYXK¸ n }Tl_ps5ײ5wUf&[T;GWϞb~E1 74 "5lNWnf)|2nD?n*7f4hFS?(|e\ 8=BM^ 8'ZU/UPM2Pۿ|QhG}Lw[.jduDFe"Y3UL=v?!~RTL&-V|R )+)\awdsY_Ny!6Ux&^usr'<8튇6',MRξM:F[x%pzeOFׅ9y|0|x֦ݶ,kQ#cIsS}ʯSp[L=@;X( 3گ/|me㷍laS}8L`/ a>ٶ,p߽L\)θǗ1'TdE2e2s!nm|K̦RY!C:wmZC P {vX< {eU=!n$l`K|lb.zƨm;_ ޟnފ?~1 a߂e:+ pV_n/|<ޗDG>ʖzc.mdRgf6,FvBm,6-jQqc:#u$a?ɣlwPɏ`W9=cco_N%w֟XV w؟`Uw~{\&;˜tCOʟ@q~Ə7U..)^rcW'7˼#r"gASD&4Nn_o}3VqfxgqE#s:?ef8a(r&Rx;Ì^A4*xnk0ywHTְ< c_{+W崽~ee~3NrNX_I>!nAa9XJ>io_Fc{(֞㱜Yp][,h1Uɿ{gd,2m>{jqp WbSN{MufPQ?>޸X3/8,A.}l>ss8ͳdL܆Dm̏8 0v{2曚~wV/SNLg-X-eMP5ƐAnfchW;:#l=?I'G|UU3p>Kۀ33^8 Tfd\/o[[,{s2mnpm> 8{Ɉ/W9ߌ; $Tb+R\ t!emA`7NJ|\-},>_캄Kh.:8w"/+WGPx->θN!%7FP!]¾l }KL1v.+ӗ9zž,b;$c37$|j61Ƙ}c>v~jrA&7g{ Y͙Uhd3nG1}C~mg{LÃ_o/Yxpܢw=È2m;6" y^\]UH۹ ?oX˜Gc^g&i^ pYn1 `>̇2yeL(b9W`{,ws"|(5cYM>ঀFU9/ Ƶqe`p㘶QJbG0,AL& &q\2zǣg ge{2OuBfuy ZoE.iiXǥ#˭[1.s꒍/ 4=0ЭΔ4|mjt U\ gV*~Ma~{cVP`w_`K٠JJNe?nKF:ؗUc&mp})J= .{Tθ c{.`6;zmi[ZvLܢGcLۂ{T_ YdF9SY_ &.۞H#Gxe5Di ahȸ#Fn̸&2ܔ@ܜˀbKc|p]7g}J>c6qH(J(NytYC|/FP9/הIqE'8׮Qi'y=/3acËsXէlq'H;伦NMkex)ymnMÐ0ij$^xD^ۻ1)z!ִ'YbEy&#h_J1Mƥ-3Mak,ǽiuu5'c7C#|u}9[b/ S#{/+k}.ù_5|&3;i5 1}j3qy6 C| +$;IIyqO#<1}(8~>7]{c$:'󟧨w>Zѹ-dr>PJϯQ}1{8/ߜ$9ݫm3o&|J:𱠇 s 3X#$6[kgX3c6{.؟ob\ 拧+s9#S&!z+߆aNr>AUyެVIe0wMq?,@7=G ةd~I+끷*Z֧& ^DsQv+8="|VAsnk2yA#*`NBӯɗrP&3=A6 gLA3vqWr9ڄf;3m3x8%Q`&[ݶ]jkW\ςNgYOs* 7[*#o^a{!2.h{ec <y !}B1c(A ygg udB7J_Yʉ[A.KVHr2ݖVBT7 pSL cLq?i3f}$DC w:W'UVT"rgeSxj<0y ^!:/h y$ ,'&B,DO&]&;1!q} ʁgq [НxשKaZ[Vw`;x"~Y,xi+j?; Ϛidz퉀%p5/: '.m5༅c9p|pX-N*L)p/Ɖ ^+ָ|V ܏v ;gۜ0߭Ư S]aO-Lm_i>s0g2!r`ϔ)BK"4iVĹ#j/>`}EhyRj烉k&o/Pu_[gD} WPgYE\IQ[;YA/,Jq¢0xN Rpb+s-|Ͽb_0F—C=EM_R̷?:0 r\#Z1_oXsQ1v A7vDܢ5Bapc{T65_qۉx8ܷ|xq{_<f#hL  S x0OPD/b|O=A% OpBb}_x7c<-t6\=1֟%L֟Du9່+X֊`c5ϕ;)1üq")%$^Og#|7 Kι%q٘ge Z91]wOuTf0aZ.A̺J- E"(m]='~tjmAJtwB7LJ1 ܊1e q{z8 8(i[+0!Cdvߗ(Iau;6̙9m@W.IgfT~Oқ4an 8<0Ok9pl ? 4re,0'V UNf3xՒίVM݌qƸx&c2">ǔobGRYۆ̫dbU`HҶ_k+7s0 g}^H)2:`:p< sNBԝB>Gq[E'_x~60ƻxIǷ\鱲G<~_6m 4B9h( \N|F.'32wGiG-栮eLpIc: x4D/}Luh1}Uoط6pN#kS[e9lp#m':){Ja|Y]JBC\ut(0N1@7S<q< pzYnxSsacn009L|,g6pr\~zXV^ӝ 4^S Oܙq22cWo0BPm;ԯ%)2`Vy^<-a</kec#֯tS,up4\_1Lf'1 S>[i^_[ 8I"cT0dY*p΄EaFg_Tcv <ÀsLa1p ޲`,2 83mVzqό0bV$\ plEN*r 8 \1K~N|3 ?_<7ʼnWarY< GF<#P0S787[8/` 8_ OԺqϹntё?񸖂|g7;uʊ}"Hk,1a"Ld"-q 8=Y2ǟyxsmysy޿m8T2󿬕NJ+Qn^ {U5БLG%Z]VY Fn*WɄS~oH//*|*Ѿi\C 3qMU8iYu&("9+[7Fu^&Vupqsy [9غrB91(1+3܎q1<-+b?xsR\ U%:^æX0'TSggU<5)SziT_YcE/.pUț5im^ɝJoƉG0N"S-ӗ2j}Y-ӏeL?R 6S |c93wV "|_>‡>(bk~(c*3~>cL12B"uF>O2').)'3ƶzɓ/Sn<n 8 x8gG뚹u"G!ʚ_@>ƀ_gq 8Cޏ<~_.c\GݕN}1߀'!?S sg>\B!Ɖ+|O֧:XL35xR/B>oωD۲.|^P^rUK1א87cpkYp|8)~\KŖFꌀ]r}M^S}j_ۏ{=1҄-_>O/W!Y|n~}=ѐ-r|[ZIF`60>ྀWD8"i 5^ +~.y7U]JߵF,Lx(4x T:LT8O_Xq+"B¡iaz黔:ZEt]Vi^%fJKmjjCsqMCsqKC>e} Oq=s0)*]&Akh/~'O,xc^_zsCsrCq1~/2(~FFFT6h-n#Z oӈ“7-ޡG}DslF<Am{~~~kdޞ q;;;Otw7kܙ[p9k|l_D(׸%{;aY{YŵAИŒgv.9O01nŤmƶ7m{7}/"S}ƻ~F{j'z6k؏llK>&AeUrv\ژBnc*\k7u9ss2acW#[w/i+jX#%v>T; :.{a61yTy@mc;1P=30ŻxPD8nh .G?FZ>R~#.(7'GӐyBU:.7W94aikĉ|Y4+pM 0r~cʀ; ں A_r0g"GCn`̣Ms yh78}~ <_~xK`_&} `~8Fy`:{p./;^ f}Q+j (2bJȗ]f ٟʟRi#cDn JH$*ʥ#>"q)òٟʟJnHnvۀVUrQ.3*,+˯JC~FH~ 1'*u8@aؿͯ7PQs7`7o=1o4+Mۂeamgo_rՆev`SO~wo2{k͜ѯ ggg4zM'*vOCn?bs}ڟg~J~48}WJ~ G `5Q\c\9Q3<'AqFQ8""ps:cY >ԁRee?OyTomE˸ ^“-ʏqh Y$ls1Y/ol{a\i][pmWõsp+ ɺf]tjk/gt;57Y;u&Ciz)}% =CtU;] p鹹t;9_^U7Z! OP± Q8ZwURa{O_mV٬)`'oqOZZWj WWxJ{*l)|W; mos P? _Qo/+*—ή!Y΢pfΤpΠp )l+QX-ڏT\*@5=5埣eOwkN+iݮ PPnI$풬mLo7Y~|,4eI; 6oFL _TضcKr#0#ru:—}秘YcE]>˽Dh1&>Rܚrrֲ2D7oN'*QrДu3?ٓmWrp([[ OD[5-֖[-1'K+n.L_O!껥d3uJ'~qyueu$텗eg^xܼϩ:O,3+0?.~=v.s@5bw؝:yF3/*Y}Zqcr'.| O[._;gq'{RGqC%_o]apWU]h#m#/s?ݗtY9K3^ krN/)Ce?=W;Uq[9P8mZqW G-+jMd5\.4k=&}W$$|͝磙WU&̖P1I쮁pM5rļ&;LǙx(,t3s/ɇ8;G[ Z|gqWm\|wߚϸ ;۩sc:xn_g^gGve?ayI.^`^wɼ.^2ўxO=zۜ { wWx"C;Ksm ¶? |GѮ4[7x;Ww޵@s6{I_A(/G0j7/3/YQtR:J{{U~|uW~I]Ev&]e>@y#3d݆,c3l,3tsݦ{rv]9׈?\$r}OxP:wMqn Я{,] .ŴMadp-q1]M߅Bg?Bg1/+J:+w%yM. f"7ݭ`s{WuWSWҋsI+GM(&.h`Nb+II>]2S0<ʛ.Lw̫mEy (C!Qm-sV߻ޟ w>DUo9Eak q$TeI,i{\x}L*ߏx cѩ0V\7(u:QlG4[OI7߷dO&K>S#w>sߖs4ZCvgpxғxi#g3ZcB΍~$9 V}iefa|Pq5BY d;E~؎ޞ,_^ʖgW}e>ݶDP\R4N ;S;W6[8 ss4@fEv/߀Q9{1'>E$"l=Rsn0'X;Ch 0r˝W `QlGPއOO{S`1엌e|֓VTOrm$yRyE0S̬0܃W*INdѦ'!6*I6ơG3xZO+|05vW`l 6dP?gQo8:rJ+lit(iMHa\.}hiI'•Dc{ p6jrvt(<(_|3fu28pax 2z2F33pG!zQIgKa}^F!w1w=sls|F1==ؾfMUeg: 2N|Qz#/_+Ƙ{?S2ktuO=cY1VTr i>nct>ZqA9q&ݾ]f~|| i?=}9|N('mO|4l+~@ޭ8D.2pMmQK v(2DS?=t7nhJW8ph?Mu]B(ԟ0:sTMu7 K4O} cphj?KM"667ƶm?!F2F}cSRiq_J7l<8Ot/Ѥoȇ/" /!{g[IX._c@;XՀot~MYbyϼaZ IwMy 3wo3h ufd8 =ʌ -,'e̓We=ܧ0o)hm$֏ֈwe["e~~ YpB zg[ s r0ƳGu9}#]YǢ\g]gNwGߔ$`Krt>i2u371wğOtY 8%8}gܴYw܃ c{o9=FP7Kr+2G풆 9z!Ks$(lc-J\Nc,>*pWle,o||w֝YV%%_hFxnix8+B/Ӡs5@ŋh}f Mld%9p癸U299^ZH~H~?=> tz@ZD%˛TVc;&Q}2~]s}sѼGĹ5^],ǀsf*=j)Tav@SiQBxÍR 4)t/_ 2P^IO7Yat:Qi8"Cjn;ݝ'}e,XvG?#4Q~]x"7I]EP wwϱ-e|I# ]GoY&\yw6q9O}Qc|3(GQ?Bj9C"Іwoy55 y?~}RteFh(ぷ_Eʊ2^(`'mh{ߘy 9b`ʉ`x]!ug~hh[|ukM5p>_zSy̒e85r$^ȉ}tt{*2_26O4|OUSG建Gd/Tr}_ v%ڃک_ +x~'tqc2w幼Ǽ]Bٶ+T:8Y^Y;W`N1 JݒIy,2ﰦ_4E1ue2Б$r}gVr`~.hJFy]ۚqZ{WPިw Axv |xvgGs)UZBcj,3ac1? quƜoUBa{j9 !43,gW h/do͍Boa3JoagJҾzv4Nn1u0ffP wP c2g;sv30ɱ uvކs7DN3{/KC7$xroC{2XFgi6q)[;z٨b/u6uze,Mצ|'AF^؆}2{U 9j9]gU0AoZLl =A \g_[9mkyL[{.zY:)+Np;{Z7bYZweֺZ7bYF,k2߿qu#nIu#nIJ֍7ԏF9M\H\#{|kmwMlo`oct,*KZƾbN̯*g36?}׋o:IsvYؔӬ aȺPmf]"F^mt 07/:Rh 8yu\>mօDꏕ`p'QING G$MC&5{y zr}h^wYk2o2aee{Y4䋬bnҰpwfi6iN9ȉWy1`g8q\op;P.n3&nn~7_'8Ý>IëéSVGS uP(~A(ڨ`:x޴!^ S<=0fd^N\|3R2k޵D"s=B9?q0ciY)}stY<5#_Ϗ,a+A{: ?EO;+y %{jspLq }9k_o}Gh&N7,k7o0➢À}كAaYsxixޜ9ĺ#&y'*eW; u _V*Q 7oHh{ >N]>8y&0ygrZ/F2GP[F45v_Ƿ8jo;#Kx,Y21)ST|c.H'Kd|6=?Řq 8+bhncl8f0vO=0m`ٟD6pnaA[jt|_#^X nrp‰fo#ñno{͹Fݽki;kwU*]dp{ٌ}^ ># O6uì_ays'PIK471FIHuI!#i7n$fP|$[q3AY2Ʒqڑٗ'#\ ][׊6 FsmI'\K{>dfe"X e{e?1)7蠩ȸROVzCQ|fp:4'Oik 8r:>"mH{g2=x~vhKꩴ8q1ɱ?3K9 w=G1Vv]<|b e){'ehs}hs}h_~ԣhf]hhdFS4ڕ kHe ^WDnW*~%TΓs LGmTsch}V^yu;.}q)sm(_򾋸7 ]C1c3ge?ȍcηci"PPl8T}X.31G.5Q1G"ZW4 E14σXf&/> :̻pyCeĘRHPpug sg bYA,3e}˾>Xgbc > FmJﺍnפmf=Xe U۪="ngzqpq=zzIwzE*" pYNlE"uC}&YCC}`MƘmc;^1$po>>mnF1^:9x~o&ȇ>d*f@u:u3 ӊɥ/=[ M {1E Dl_~Koy13?1`9cm+n>XKJ̄PWyS-ْB1Bm#<&[a!s2nsA{݅G%mK#m]?9eT].e-_zUܻË+)~ϝd's+WQ}^A}5Ni~VB˫EtT?|^/z&ֲ(q.qq׺#':yuX>[Ë[AUu]FL2u [~.tr:(9]<) 9uI#=/ xOu^9XWgXcKsci}XZ_`Lvcױccͻƪ{@VkW%>?:%3$0aqWgJI3Dn9/{8qQ>Gd?Cǡ0 ȝ7kl4̈́}X.:Cq@qw3j2¾`YOkӴx~FӰ,|a_NZI6 .t>\2/}..\2/s!}.IJυ9'YX>,g9U^85Gko%wp.wp8^̙9(Pѻ{XQ[cv {m1_ƛ=>f@,9|i1,c:J?i @{}Xc`?f2|[n\fX^@\epމ|BbΙ\^ enQ~zx;#qopb xM _O Sxn[㜅ĵ3<8ky~1= l=ճ^sd> F]œY=tFYVnP׫'L/`pƇK o98>$> Y?g2I`ܣ{,k?w?m3Fr>exD+O  ˴OmI}[}.t4w/BΈXy;`NXyΠҦN$+DC50< ɐO&8 p 8j'R*P:'n?]b;1uaN$}n[ f&oyMI=xO p$\'Dq/5jj8 DgIk,N>&#s$gv,I~dLa4O-=IɦOm6duLN&Y'm=#aY[3џ2.WHFWHF+nPxƿBY žWQQ\}1T7M NwWݻp0qόer.*4öc@Ɯ< 7b8 [X 1c_NxW p{x'?q0q y<^"~A!%|o :0mo3(/P/l>gwg,H'x\ -|X ‡JuIy"g<кY!>g" /ςqJƆ,{&LP[iKXsjF_ lׄ}㟣k}㻙ߺ}/9ߺ<Ӽݢ>5Ӝu;ӜuZY!޷q6<\?I14o47ӜEٯ3Y7 9?Τwf1_p,JXygeyǿx_Ntrքe(c~MT|Yssڣ%<ؖJeX.&'s4I.|vîdQi&)a'`縸l^#Y&x}ÿb'eOm@tjyL}sgT$ cww~ߞTc92l:l5nf}f 3qE%@5mNm^δ `)'?iy9Wo?~Kȿq5GW# `o{~'_ Pk `?+}]ط`1@y_-}+6'y>$"c%BsWr{eSg“N3YKt צyeF@Oc?^cg+0,C|iǘ ȟXV gUnۀݖakuee5c6(cF}zwYFOI,yFN=|\ȯH1ROI#D]x yۥ۝F|e\r0 47w%;=y?6nӛ߰Um7?{,J8^ hcM'dS*h$|Rޤ= I>sVs.rN@ϰ],g%4Kzy^ ,1v`5_fz;ʿ?nwxeE"V\Iw\fY؟kʟaz,*H Mwx,6*^Q$3^2VJtȗ3g{XGwqZ,94~ Qd?0I_:Jqo(ȷgU1>击@ea5ٿʿtqsN,??'2Dzy៻z1,-=U{\2w_g[[{U)?'lÙ_Jt[q,c1/g?M߲,{;[I[=xYOo5o432wd~ZQILqןգ,SOS?rȟ$l5R5NG:?N~Cя7S򛧑nn)˚PL#>2V6J~4H~kS˳,˿@u%[*8mXF@$O0 Otqӝeb}8qLo#l=a)8p (38}K [gl\Ǻ XTΜwײF=y|uc= ہHzC0l˭im]npܾ-r33:Gye'[_gΛ;ax::3Vy1_ǻ(!py#! ]oG7cK~TT<Ξls*&'qu w?{On4MI;oMSu^>f5~)/tT:^+A 4glZ*l)8kLz\p?w"4xI_}MVouZٟT8W.E;.y-kc|c5A}͑OTn> P> Q­UipK[(O)\f 7UnpC(\_z ?p]pmk)\S WWUpe+)\Q We.piK)\R W )\T" V8AB T W8N| *W< V8FhΥp  :V#uP+' nNmũt>5 QF896Rc"މǐ!X- DWxb4Gxdei vɑ{GV|ڂ s"@=}!oy>Ko)ȕ72#=,}1v5flCz`\ˬ8|ö{ -b^Q21?n  &g df`~x7xQ3N0Z2*?<׏UH 2> aomMg#P?M&r碻ﳞolA='ꖸFDk^?/S9ż½nrÜu[f]2lWإNμ97݇¼e{o .)u9鶉ueJԏk7?֯7| dj4A_s1\żux;*ük;p (@F4apx)o%Kl6zsɯ]]Oz33L#rw㙇:ڽR;p|˼gbpŷUus 3?Ep"cgݡdwpf慸x5S'|?c@cw?[|]>][m? n+luhLzumG%Ý5hc1WOvd%+,qA/q?})|^a)qX }] P￸F[ N~HK_lmmG_{_裡??]eRQ ڊԞUc}-wExL̻ *, ?4mM+f05̻\ ݮ1?2)|/Q+Nooaüyf-dݮTǰ(1ߎ5acx~GnV—~pmTXo\5m"3צ2ٜTTphv`~J;4e?kL 9~Rno|w'@h> OJ%1/>?3`=Ee%ʫ N"Tx9W x4Ĺ"K;=yĥ y4$'/m5~/c[9"Mn`~E+cv 77< UR1jޯۉwyCƵį QǩHWVy*\^'= /Q8?&Sy25Vx^،|/Vs'i~!ty>%k;ff~k%!o 0E]XD/D}];W[7MgXa~G0B =w.`wq(/<1c-zC1G%Dtaw#DGF &"W- ncCtdE')!⋎#yD/r=FB?H X¯؆e4/z>dǔm&tݏGY ь܏݋F~|:݋k(ON{'&qn'~cNf'ĝh=?qsp/N<ч'Dޓ,oc~'9iDGX{\< .ѡ(wEqE- 6眞/RtEȰ/Hkvc':4Efcv4@yY?3cdz+@>xCtTd]Žp,WUMaޚ_ΊY7+fEϬ蘽+sߐKrnp_+z~EǯW/UX{IEO(ĢXgyȷ+=ν`iF13g+쯌e,E v5ߢZtOEч-EקYy)E7̥oOWtp3pM͔B? _S.:1DȻ=/,ސr(MD蟟vL.wC_|ћ/:E_=":71o!}~}~/`9YNK "ț gY7ꭼ o5{փ oP?uvһIwһIvR}a'4s'Nz70];)I'=IV]Ԇ$7g켋Q:'}ͮ/vQ>+vQ{f~nzxƿnZW~LV00>`EL<5xλ 0}=U LqV`}k&m &k'Λ{1:1ư\\J]/8Ţ)/rExas| F[M";JH@}*M(GMoB9mzʑoӛP|ބr&#" 7{_2. c1XǼ|Irzt̸%gNs|1#~Ƙc=c,`:ػ1<~1~aλŌQw_8XpKcj~@k݂e|y֯&8+?^ul?'wc:E*2DXJQd ;){^ 8‚͡3F0]qr1/2Fu'F9%' 9b(7r_x}/(V^[pԲ_Q5+ ;^>a5˭XRo{} 7\q{׏`;~쳏fw]cq3'SY8]rdO~)ls0 F=|Pa&Q+ r/5pȼ|/X% 7s70+RwLbC [e1ıo0\c|ƨ΢dUe:c(?7]iޏ #9[矏kOH)"z]?b;%/*޳=i]dJoFKv7ܲὪWdd9hd=h){Q]hǼ A9}Ar3y5rvxoY.tyUƲv#{ī/lγH5^_6Yʼ&~M+m<<on)+|\ gIo| v+aZbwmU;\+Aw-!= 3yϹo7E_Ι`79.;)~e?]CSHϾyʼnc`' =Duw+4u<ҫ!.F{|V_oQy&Yatdf9}eeC 0o+w?D|]|6CjӾ5coM0H}y#UJ/i7F 4=ߒKXWwѸi^o}:ۣ|1$;໬gD)k|Q n#8UN7]Յp 43~ϰ|:ot,m[ !-2oY]sASU?G砒 }ؤa5Csxm[϶U0 \v7>Kv4\NS)VJc ;[f$o%>nIW; v)Túvi"z8m3vIj~ļiށq#8.?Brۏ7wnp^.ks\WmFC9sķMjp} ЭlngS6Nm,瞛bN/ɒ"yF-mu kLL8N0Vue`W4z9o>>/)sI񲲞*w|+{ 3i$w?_ɣN8WξR'}op%_vy3<@=~~Γs?ྛ'y)w_l۳0o~; o3!W|_uwǤ Ağ Gv–8$Omv֝%ë(W| !җcz~>oCsR'}`wzj3/#Dٓf|W'M?<8&]9;eK۝~v,ϩI,̋M+$O/60|o )=NS&k?A'.gv1?NRaSضIP;ZX\/l c;@0a;z3f6كW쟷?ǿӁ?Jg]Pva,AҷߞY~%K},˟@n*{prt=mIn_%_-*?F~QTnHv%7N0>#TGQD?Z.h,?3a9gLMy\yb)^^評$Lodi),wJ:/u;?.Onʲʰ??ǭ/yCxdY-ٟyʟf^\f 2_~0N)ѝ͹1A%*i,bƕ+~1w*VH<dž!{p,QiJilu ;m>T|񼭴oq98w`?~:-Y4_GnHsѱhG8烋__i<k_HY `WڀWQB6݂gc8w&C~(u~5e춯N+lUؾ:񲃇¶cS m`b3v [R|1P|qۏWV~8quպ*%Rg_o8ag:clRnTu)P WWUpe+)\Q We.pi(XF 7T U8Q: V5u_ͺxacܷWiGuL^o# 1F}u3b|% Vr~ Ɵ轒~n3 ? k G3v*r] p ep1/טϼ]cwz*g] =uvU|~*]*?.w|<)ݲ^$޿VxyE gyvwWG+wP5>) KYN:bރs>{;V<s|*4W/恻 JY Ehm _|jbv:=})CXJ?p>K~o{mϖߔ1/YTo3/\9\<71Ÿ"pIk)%,뉭;oyu^8Y: Xv7@dloڍE i %uq4\Sb9ETi9>F՝ N⿬p'Ke]|%+*N f^6 ʦ>=}{|w⟸xI*j̫ . WXʑ?]>0f\}6:9u%%_C>ltZVutlh>cs OeLWkUo@fe?.ݵwy+n'Qud^y\m |_|Yoq߾L7^i˴}2A_:m.]l֣G)7ct7l`^{UМ73cm670K];A{i~ں?qW׍y]}̍mo+$O*nCGLMj^fE98;ï޵)W}>i̻nF?{߸gmf~ cH|q $C,E:UNW_n=o't/-μA*HfC*Xfݡ?yqo?!92 o?ǐ!Rc\HCy؁ݏ 0c!:'X¿*O7%?5^47}"k2g)rMy6c?u_LlξJ~Hw'U7S JٮvSی"/>p[_#*yQ?pH?Pobn||v/E$Cktof^3H]3HFuuax!=X-mt Cnר{5^kvc9U؇n95UO=|F1 ͙։35 O]7A\7A0F ׍~qx%19qyusus+|L\1o^*o*]n*CtxSX8=,?'Ǹ%679 `<p𓌗n<1lx-I7|_|߉|?|7D>[,p4DO0N\q2vX1^b{3V>QxԏD>̲n%'83qpGۜn0>x'cl2Dno{ɀa\1'n8po%l5I 2~W-k>9);Gw-4'8ps); ڡ5A~K$|;}^ݡ෠l[Ce\[ 2N2Ó^88lc G1p)3ƃmO3N??ь8J燌Q!_]{~g\1&|Co$O7ۇ3^x!^a(/M{?sA${ԧ?k8 G}EXQvvڥX0m0K|fk0C f 0`f8_<ߡ<0ϣ<00m03}`f` fW0%B`YH0fs0^Sǟ']}ܥ@>.?`c?1_q2d2ND>̏2|{S˒qnU1ޑXY cgג1γ:2yVo&|H<0 _̻r{9t9)0t.̤a&nt2YSg7ΘE&/rys3Dy g2rkϿ -3zL{-ɻAȯܟ0Ʒ:=X'1$؋&QeKƘ1 +cހ>[aJ"yu͋YM^ 8+)ɻ[y;k2foc\89{2|￈98GNLY{XV ސg)axW+S-C,~m[bPkŔw!6秸{-q@ƘyMW:+q/Jld^s]"?c\^89&esy\^|p,k>8IO84kw.%'8pC%xBq~~cq^ߜ1Ӽh,4rf6+gYi>ҺF?{8lѺN?;{8ӺA?ͣ~:4_s9is:I?'8pZs.i]ӜGМGМGМGМGҚÏ5=IsyI{:!yYIfeYYmjp,k5`ZQ6jcpׇ/53ίk:ίeײ΀kYgX۬3\[p- :εe]ڲsmYWLcq-)y̺"e]x˺b|^׬+&5늸w.uE|Wq^/+_cͺbRZ߈5Tc:UJY>t&ƶ=>YcEV+^zUYM3-zoRYg{z/އ~-3z{JYo?gǙqf(ގgvxO3Fu?*]R>rɈ+Ⱦ{ 33c~bEWVw%Z9cο7durvmhǕ*l3[ 4a7PNlA޻*Sl4vk ՝dZCD803ƳCǞ[Gߤlρy`M Ss'E۔͌o|q1";Ә惥pAc:ab,ճqN?c iqSe ͸ch=ô1;ah=!<C)S14Fӎ1TmO?ψf?)/!R?27e.N Rᝣ8_Vena~ݔK|/:m_kwlO=ʿ+.7cgՉubc:1Z1F=|o2uXNoǸ'Ǹ0AAi;gsWDž7 x`|Y>1$8V%`qoA#|aAq|='+p|?'c4Wq[AKIc986.HoRg7>Rx)pj~mX=Gf8m!?Lek1]=N6SN)ηl]fވ:O>gؼ2dnۂb^9éHm(isپHctg  2|9NCY0oLlY_ 75D[gp9kK0>Wh-Tv =ü{X-@gm9cu%>w9e-YHqkhDy )8'ZUnO3PNݙJipG_g^EHc; {s')9?:IfSU~S7 j W;(V Px+Gqh"U:˜7x==":$A='nzi5nM\d: +\Ca`O1>υŪp WX:KDV<%_;6)^S/|;`U;iewZD8a| ]I_B7SUgtG7uJ1J4͒e_)?3G=b~j3U^{>1F {X26Ř+Oʬ!ڿ,o4O}'cL1uZ)a}*r9F79p:\| wsI 6OұGnAk伣ﭕ}d |G9x_vdoMk!yh#2[J1y&u<%vTq2wsO2sUyq e̝\_قn7黣 I3ɒ\މ=Uχ><f7,h-BZqt`:`bzL~;=&$u ̂`~+[ `9<8w&Y8LNU0+.8J`~E0XZi+>șo% `vt-yf0Wk0ahbse@5 , 0V9  &&SH09 s4_"9`3S %09J`N1`>`s"cs2o +S̩`~40π9̿#0gP5 /sl\0z9\ B0烹`.sK0_|C`0y`&Y+;`sh]uQL} kU~Z@c* sN{ܦ띵*t)gxewΠN3لIAnVxsa3 G̙,<1ށ)_3_ȾĜ_>i~C!g2eQ6]|9׀EQ4Xv`\]|܂4=Ey6ԕ/c2)R%ؗfr8Z(?%nw.*JH˻ x8ؿiuNJAI<($NA9S/-iuҐ9?bYoyϔ+̳EZ\eL]Qs^mw˻qR|K\=Tr/_OPZXo J}E;Gr_WD,}Er_WD}E *b4@-m[?NvgWqg`} }OV ,!t L s Vœog )A}I^x> 8KйoYi=c[ Y,P{k>$ߜA޳o֕0m^>[_~k?baN;}֓&k >!ckOd8E %1lMy?_YkU%)~{k{IvӤ¿*%MvMR,e.R>3& 3֙ъX(󏊴n}ׂe^06ů]+v;C0ۨRqK]ye^c^J&:/,%0lgѰż.t%t+Qz 6Lo!Z=.we'3coߐ`l)Yn%|/ "=^ pOxܑHpXCѴr}L+\ӶKhoSCWXN?ӎsP7a+n29cgB9|UWKV }y*m!(6} P=0C`26[k#KDecܗq_  Go| Xޔs\0{jz`^~h6 LU(L٫P]j[E75İ]KPS38pG)<1 ~cd%h怛otN2gS~ nğUɼ4yqߕ`qG92%*Jf9*3uq !Jfs% Lv{ ,)PXfz.2T69R' p1' QJN)^ٜ įl,TuOct)Wg0~\]c4Pg 'lp ʦ ̨Tsy^䪓~?\'@f^ǑU ZƑMƜyX `;É`6Mmd+[jkT_VSʩgCv^yտKk'vrQjN:ul5JըήFjv+jTWh`5ޫ{ըI~HCЏ̝BaՃe8̹S7W7[ɝLuJf0V݌I7,{+|d  a/*|[aΓP05w̃`f.9|_W{+Řn(7M?kP|O`nQng+ާEk ?T8{M;_\{fv"j~Y&_֫I멚T>40xO6}m+cn([\hkI ;+!G=|`=n=9.SX(e ucHz$qʭ߱> *?_UC+)ep8+8 7铿Vwܢ[|~_a^#ߩN`6Rf/kשw˕EJ,)Jy[\܃ $_HRnQc|Q%Lw|2#S$+9(6-x֕PNTm'p sQY P%qMaK'L2[~|۴8_CJdNޥH+E\ Wx›>#3'Ck'R;Co$00wK'? 'K3H:̿N~릏a~tc%8濐N~<󗤓_[O`t0~:Ř3Z:%9ON',󗥓_0TfN23Քg?8?\הp#~DAiS拁f\ՙ@O2G|\V__Sq4fYCٟ?O#^k2g o0 _tCz՚e??<.eY???q`;;j·Mo(|]k _UmO–=S.̜ݮocY2 R2Ot0Hρ}4w⮭o>fQdl3eYsim, dU^9'"SbWYВyo³ޢI(u+ V+)|GWR9G(<_ Vo375]+mmW}{,,I& tldvZpeu{^{5ko[5kd-Yk6}>kb{7&H<ZG%q9φ/%)]?.JS'ɖ]m30)pBx>)窎WN]Tt0#+8 Wojh4\Du?G_W YZ`{|)9J %$cܧ)sx܄Gʎ$)Ġv9{c?XYU}iom ^;.HMA:ϴsĽ [ }ݷR>]A:4R47zWw5wʵu9WjNUٞ"O>Mç̙Or>]a3\_yc|=e.{ܹ;Sc<1^xzm+Hm8ݧx/𹂴71c'f~kSڝe9q`0xgiMm aG;D>g mA0 㙯 6%fH]f~PmqOwv)?|ԼU['87Ido44ϞT>M{gן -hՂv ݪ-hoy ւƵs[7A :-եtt %-ݒo܏ :gѮ;>ΞxKJȑ }\Bci8X(C)8p3.Ӟqg )ˬ̾/S{|.jʴ\XªܫweG5}]I',h#q_? a^W[;ܦ؜Jd/ikdomKm"GDN9ua7x p.-1 Q~yC Z1o8օh̛pBC%?[Ƽ?WƼw(Dc!p} ;1]: 8w;hq ",(U3"j5\lʜp]oY{]!idz~ǹGw q:ܣL/Vd@d}SFN挃|#_-]ٟ[&~ug\ڇAw(5œ7x_uTs4)ߓԓ:* 4Φ) oDwݕ|cK%Z3qEGf61Ys&R٭'3~rf ۪~Džљ{sh-IpؗmtM<э6 En/_㸍~s=p2sO6 ^x mv xѧc} ,n=c#6:?yF:qn{3FmZ;ߖ1v4'!>mghɹuL˂f8sRigig x 8vtU;'s!d9̩WgRF=O%~;-kr1a t/rG( wW5O5t7qզ; E4䈿#RޟIi(tg=>S(.P4Ios&~/T=~/v4sԎ{Q;9jG_zά+fk]܏>O==WYf-=~tw_Vܥxz!7 rje#N-MXao6 N&rwT-|?Xhi,7<|Sfw 9ەイL[٧_hތA:~pOHkѤMX63/7=>Pz/7]8׿=чoƨzZ"~?wڛݞ^w͛ZsVT6Y޽si4fJ{dž{{i\5t!peHg@rM؟H쑒 s¥;-LrvJNO84"ֳW *`Ps(%#G?:[1 QD:"~g(؉rk?!4') /oug˹wYu ß)fV̾oNf߷{'? Q|Xof`2¬ˌ쵡9xa'ڷ(Da^3ed|L 睷9wOvDlq8<J8 p^Ƙw;S ZOǺVcT1k9޹gICԷ(i(/7L|*Fg3u3cc;d0?JTkߤSJNXaW:LH=YҤ Ct3a Yݍ wPx³5 ;Tx8ASUTwʟ_{#My[lFf:SAÄMz` u=sݣ]vWG htKu]q0Kf̕e~V߀skV7= Υp%Lcqi\'&&[vOs%V\f1FY]zvܔ{'PO'޷E=Mx95쭙=ܽٓڅzR' _SX_{Rp'v!*ދlEg3[]zљ!^tfk^/:a3[zљ{8Mgzposf+ǩboS^)Nc{S8mMqۤͩ޾u~aPլHF0MiqVosnAo>_|#G]29w݇ޏ.3VE{Pq;npo1|2|`9ZoS{m"n߾>L>T6Ns_ 5Nۇf6u{.YY=91}XX_3ӗ.u+i  I<֗Wc3Rc~.^+Bgo8}'\%*ݾܤĩ5|sWP(v|%<ph?@"`:%9 ঌQ/R~| Mq3_e1^ 8/4.ɮߣ~tn}x0s5uUɀs:޺+ !pA֛|:Zc5ܙbe$~e~e&/XJxclN3NkK^']㱮g(cLax;3uvy;_/[fh2=?@f2~zg=G9$W\$F`OUPp@jUppz x[V{ ܃|t;7 F9(ߏsf2(7~Y^şF9r^8iyϻʹ`~hx1 U*?Iq89y-c4*}͙3lp31W0x cg6=u\=ƩsHy9 \ced eOazw";/یތcL悛My7pI|UyNBő3Md˞Cşc\`+L?Xqn?!2)|bqV5?Vhu3C?D +[@,3'(;M,hd9]j ۛ42o e? ~a c +VA4V:8ƓA|e0 ]eRz0?O&$$s`s*\wP_ʮr{lq=SqBRNOH9b0m̘1Wf0c.tCESTS"8$աK$:kk={=>g=[InRik @0Lv[ϓFG*X o5km2]G0o)r/Gmoู:-bG_ _00}:K T-ٗ6G8n=0Euvda%Y8 /o=Xȸlx [!w6 |s:#J7ZF?vSޗm(a‹ax<o w~o̻V8c?ed7 GrӪz;%ۋ?}c[bBr: i?Ƌ?b],j'˹dO= %~'8M> 9Oo/.{FIG{s|}Fc{Ϩ0~|[v'~;~~8#c}H4/w`kmW᠒3g7ArplEf[:mG2-k)/{PeL_Ƒ[cܷ3Ƹ3_ν8F>d~ѶVp\T\r\LZSKiZټ->aB\P9)bxgN41mڝOr>SQ&y{hr p"Mji|:;ڳ1nczop 6xÊ./5-,s"Sݟ1?{1ӟb"`4/nuqܕc'2yYq]?1-eOsX82㝀c2i&Y&w:a|8<9h=IIw_kRy1fKY,g{G)9"ii +7 xt㱀9aWc7H%3ƶ |7$(?x"cSYYt ԱO:va*?1ַ8WwAX߆ؕ6FcNTpKq~^u,a"OrK.cY;VlY7= ]|T.߭V.&Iry-ߝ\N~*[6JT;\Wz#|xEH:3%[\V7yV{ lL|QFe>G@=볂5^jaEKxp+m}KbndjUKg;hƘL~ôح -G 6'kЙZ}p^ Kypd7 ʸvVCy5zp.{~E'_$L#L?saZ\y8qʷ*baܿ S8i{QxVc=juR|/MNC #3ݺ)a▨=.!G+nZNoE7c{G 5Ib;ɲ6x:x2|G1=I_: ;+9vpP GOȟ3}C_:j5@V>VX/pǾ1+M$4HCtHy4y#AM]3$ὒ.}vpo7Ûj]Ǽ̻?MYg|&̓C~'L] ށށE=6?8S^?ڜ#u?ռӁuCrvq̃kXfұF~lSW#{sL8|q27k&*Ϡ;QLA7L砗2Qo~c?gLȿ SUo ' ϔ!绑}o*<+gq^ez ) o#mu9Q{JgvH=9 ?^=;xӷ#?I2iia`zG͖99J8+Q+g˛ޙ'oڂ[WȞQ!s eᨅ\,rԧ3k&";g o9r/fz/amɿ/ ._LJoLjy+e?s/Y'6[7ɣ[F+57^2+Y s×MPSLt =N635G]et}hz𼷱?|UUj UW+k8ߡy0I ̼7dݹ.k/N3;>^ Gc;Vn=beĀXy{Xy{{Z xEk<26#7`2l#L}>?T``_gL9y>fX&H|~p?=<0E8W?@?¶ $Rz/~?P}w*KvDҭ tGZ^N>AV%cïqt:m h[^bo|H?; 3߷ '2SܛfNvwM ߓ LO|x`&a{Eg_9&[0Qoz&Km7 b(Nfgunma~?~q(#{iT|W޿[gyJ1g}T։8QqxhXaE.8_}G '}.lv|̄[0vpY/ +[693=pd5:Qɟ8ÔvN(ćr9xt#U Yx:&hVMw/oD<nCog!p1֙?rYh @Eafup?ph=r\Yvh$cT<0kcUÔ~k |dIM-5$2f0=rzD3wwOmx":"Tf_?/-}e6y.TW'YWȀ^ٞ@lZEd%򯅰˶ hMOͦ9LdӼٴ`M6ڔOJѲfn|]@<OG{ܝį1]lc]Ugޕ{=A Ve DX 7zs sۑ(_ߪ!~4i6qc/<|sifEwZ ҟ>Id_17"so`99T~G83O.PxyNݍL> ߲F(>J?w0H;D8!G+fߌYP)@&DY1J @9۔t(6t_G7U' bϕEJͥR]UKu՚\7\Z[ƵMe xuG?̕ ϕ <#{8aSnyhëqZB$`fondФc'=O˛Ʌ6[GcU[!bS܁ ̻,.+1cpkX>+g@,yRUǜ~l55eHCCxw}=eƶr. O|OOQe!p|&q^v +Ns'ӹC[|\OyO>: y1ϦQ&ς>f~5;;s!ꭣ"Q~WȔ399eN#d`ʇ]`Ώf9.yPdqWOZmb p~p:)SZrV'Nel՘1yT Ɛ`}چuy#|cBŠ!~#|wƐW_ +,DžrusgRB(O*+xSy_bw (M1ZL~*=1;8>Fo!NŢf90ыEϸbsb3n1X0JƼ_0Ik5)<TzfI{`'pm:g+Hj O oT*DJoR[)TJ~JsRO`R_cl%~ץr%!^ԓ2 +K?&!ɯ}AA%dKH:t:F|By-g _.)Sxޫ+d X*n|R>3|Qŕa4~r_Q0b$L}D FvSu w=Ձ~{::2uWXœ<I5ɷ~.|sld5*wUu=icrƖzeG-#I5Y<0&V.o36[ iY uqD9dx:x:3Zv puq31]/51oo9R^M:6:2.,z[3߭*"B^K>p{;N8g`Y4g`RI:?F2xr3L{]<5ˤ6&\uv;ˤ.8pdEpf=qEϘ&b7':2 ?^ 8&WPfTPWH-dYoU+{"@e{۬᜝C*[Awrڶb#`EuPݍcw>xI5t瀷,;ߏ\sTo#ƻ~g9189R{7dx"9pJǶmhhI=;~*={&TR *)TfVK*[QI}vHNGϧGX {R =5wàFB~rUUc\AU$sT:dϩ"]V˫Ho[b>HhLb~ZS]4RGVeS9只AƘcuIX_S%ʓנܡr5ap xs+Oj{-CZ3V˹qնQi,)YU*fC!TY0v2;>Q)b/ZS^j_{7ܛiV7\m܉݁1 IKjmtS}`IrlCnϬZ}bb1-V'e]r`ҾǗۏk[Nc7і3Ea@*dΫ4@OqEbMlc0RQ'(|xN`b}`+~qFk6am_ӗ<ǷQ`ngzWy4FOC޻>v;x|+\+qꬰa뀇16[y^w Yd#bHr ;Mag<8SX81}-ung;>Ve-1OvI~r }g egy;$2ϻCL4ԍbxؖ3r+ $jc}ˌWE4Y7]ZsX~ܕƸYvȔL)mΖC7Sm8Jɗi/Ao3727{0+i}$o{z/,}z˔+;dbfluj>9nP9Qºy9Keƿeu3\"M{ޣ}6z<"P9`#ㄗ0N_=K_rM49&6M\.M;~|c>jgA]eVȜ=ſKaiTq-|I?7-66w+HN2Ow4T,4i{؎qgz ܎08S*:QON({sjt-e:G΀dz;粺OLOI&l)}Bq4ه4"g >J/Fv~S~QG82.tc:R.wн+=d/3C&ܼ"Q>0mU~tj:7RLbz@C46Hݙ' o ۭ:o`Dıhb")Ӂga>X/XGg$K_;`\0n2)R `{!|0nAƶ~-_q1q_1ƱYh6c{[CD;p%Ab RM ?5`7!~;xNQ !J>_ ZZ㕦|r_!ܞn+{#O_߫}s4_̒` '0.mxcosc8c<njF%Yb%!^X5YTgg= 4-dY8mC_Ʋ+X3<<xNؿ;f.rHӡhDUcKlutYg"\3@-k'(v e2 ;:LU)|·)a,^,1熑ԮV',"i<&or o62%H‰)i΃zYȖ3EkzQ6ïc6y2o茙Mݘ-mʴ ʖ°l{c#~kt-z Egބ*z>;"9hA*)OpĜ`)7q@}'t?0IUU W)g+ {oQpr[qClo- I#Y(MvrJSM3tA>#-T炛T$md+~ qV<:tK$ F)|**Oa8 ܛT=jXFVW+a<9gevOu>>>>||0~L|:؋Χ9t4Pl1Y1Fy01Aߒ/kwS-i0= sNe>MN1v/"ݖWoLwXI 08֝0O=U KC<37kA>%<8C y%0Ʒ/8\Isɹf|=BwP~/~;´2 h\>+|*|8W×OgEOr10w}m'*(vbjWr6P각U >V@چ#7\TG5|&nO-qwsWپ>ӻ~6|}nwZҔCqPANiNSl Ɋ%<.,!K,c]/wa|3`A<YG[LґP"w/fnT .xmz~72qu+PGwAy(κۻ,$FlWW:f k["Ϩ,*f^(5%T"xY^"Oer'74s :f큇"78[[Qͯ685/'V+<1GK$))oϲ;1<]ܙOМ;c]KE'WJmy4K,z?RiWOr}5(gBFdIS}}c 'dmſ[ޯ P}*Xo^yӛų'E1~拊pzI`z (F(ztM|F^bXFD8|ډjbGZ>}LoHgx@_rx.>“쯝 ߯t#ݯ)vfuvpGj,9eH{rVBlsCzRYD uH!+t€`sf'<d;d\HПG3{>{7{u$yŋo8A7s}%7W􊉕WL$}JѽF92kkLxOr7!TY)scy\J{d=8Ob;̃&Vep#O,KFg%H9+z H6ewpLx}|Syy+agQPWĊ\@lGnY/rY3e'"L x>/g[-J;JX>Y\g/wm{6SwL46t3W&>ޘg(7.|C6\P+{|mNm1Pڢn]Ud^t->ͪ#xC;9Χ@4/jw ~1?WU\b3sW5]2D}g۪.Zp3M0Z1fbw̤&4wv11N| pt1n0n57Ye‡?9&|¶EgGDzSe72/{1{!@~[ bz w}RWSx{C<΂wo%|_v~m|wJjuĆo$}vl'^'Yn:,3ǼzklLzԑA|o1_8}Woim#&&HO}sӫw6?K<{ p'NjƃᗾMh nM`㮅0}$n [s`^(GouDoz[}U]p9u0[:csN u}:±uKN+uc\sW0qhehO1_Ť98쮣](SF)Չ}Ѻ׌ql_$NuSm˱m.y|2b%?׋=~c ʫ+לMWz|_ꩿs+> W[\zQ/v3~rSw~S^r[= <^Kj}p1mTo9I8^:YO,7p} 6P}O4>y b;7"V^t 0~v7v~gk}{ s^cylYw:óA r{^ظ6kMظno6 dG&|_o pFpk~G;7sZRՕ# SˡSa{U.+Oҹ-L/_+p[|e`֤cm !ܢל1 Ǝ^ d$-L3t] t#g@7B]|!: uvnc'u#|׍u.n䰛WO{rw2'mKɍGd^#ċ9B߀p hgDΘ@]fޔ EveMxp ==}0fu1=GeP3{w*lO wrhs?j2-a0QGX/?Ke}St=G g;[\OA߫>Js{Ҙsjݽ^gZ1]hb%~\!ŗrnY~b _/=q*א+L%Z&iN9뿑sO"1Ծ'ҺDZ.M׉?32giQHD’hĊ o8|*Z!gS$aq}&`7peJF64En>"dcUX;gP8nmSѣ=Aӏ#6<]i՞D3毳ځ71YJ~x:Mb7:ֆMS`xY/E}$t{0{w e661o쳩o|*Ùj+_ފ+J{Rit2JSiFk[h/Jch4+eh_{i #l k;x <0ǀ>E:)NJGK9iQ$>ERCX88]Nx5i6[ Ʊ1*)Fzz0r@Oz<5Ve2j5G!T{64^puĚ4_$ր+3@or X x;߈O&6验}:w9ө}N}Hʹ~sn}6QdS=ay;=Ny@|X8\o'}$Ɲ01gNTDIA 'N ̵Y|h]n9KezߦQ:%N}I߈J X'|Qoe:A> LW>՘:ӂYS n{l48,&gٙv[ iG,OS6*1UOsoY5ho`zL+NC ;p5-t 7Hnfg+k 5MQW q@;@w^C6P4LRz##W6b?:YE۰{g2.ϭ6fn-t!P+Wvx-'@ ga+ql]c=g7rn`M} \?UϲUuC~eE=N@p޼qdG 3{=׏~F٧y& NV ׎]ߝ֛Oʸn*NGy{VS\:}S;Z^&vë́N ӣ{ه}#;z/c#xߖ˸Xkxm_e\@y| N^L@nӄVvM߭^1u̽8x9xM=ș!%so4dsr6(ikS,CНy=v?fp/ i%eՋǷ0'7.[qa[d ;l>& 8/0=sM-8u !Twh4vlQc-@gnSCxO4qB 3@B0kxZqO9QP9D4y֔S@bs\r=t:ܕCo ӑ@1{86ey1X2iOc ]1sޞPy ~RU@ƜŽz[C{1zG{Jh?39C<6sΡ@*>O|[(t!r߁^&/lnKWs̽_q9$7g\M2Ƿ"Kg:5y t\Umlqs\6 J31M:,5nCY:0pD=q<}!쑼RODwx{)rhvѿ(+ӽໆ L#w2ss_Bcw9@oz#œ'[ۋLsm6> O?@ѹ7Hn/g&0#R{]ۼb@13* դu#=y_ާNk0oP^O'o>ĸ f ͔.*~gEa7nlG:/d³QD7,yt?Wj^Ib/bvU9/*Q=KL}4tZN5q3ۡƮ%:d^.n>kwλ~􈳴˻{,]ۜ'Y۶9n0S9,|Cn9&Ϛ䰸T='GܸͫW-=ane8c?S۠=L8g= }h)k%bfZ9L1?O)ZuCss~5L>s6pW3w \!<͟Λs<sj'tp MTTEu[<6/6@5I/KRe+ZPl!"LnA~|[k۽N!ˁ⼴ֳ+/q1L[(̯|xB}߭r]˯|J D>|jƩW`:HN Hr y=p:݀vbh àHccw`y3yǃq> U' xۗ"sfH0Ɯ>W`俠YS@ ᰯۧf(!gx{H͙t׎7Qj|QQ;}Ӏ@7dzDyGĘ5N#G ͷ 挟 PMgþYLО"z#مIT__7Ԍx1akBp-yK2qƘ9g3V*^,<`\x3_"EZqN\}GaG-9d\ɺ Dw\ 5Km-l0΁"_J"W8xxӰ~3k)Н9@b^9&.wC,6/Co8l ׯr =;8cވߌXnV]Lv-zT1rL1eSYls.wmXi}oljbw㔩Wbbwcq1}y1}}1}G1;اׄAqEZrS=FWvqgWݻ*SP2 3.2,2( ]Aij ojD.C{/{mgp5F- %F?TY37:. ƍ%fO_W fqpLw;wQ{9έfJHp |m͍DB<&Xkp?^10qkw_nsn]O9oh/r}opIV{v&O(ȼ7g-^)lAQs7ףԼ8ѓlmXǜՌ7Fwz_`yMR\ESjO䯥| *S̻ҟ۔QeQFasYh;_66+N3ũ`;}!„Ƨ9j-"Gڸ#:]c6]^Eή2s(gHΓ֖:Xd.׻QqsUyg)%'OPo-qa9ȸ 2&.3K(aiy%YIHn݋n5Zhbz.Pʴqܳ5Kp2aBTMI׻ tD;Pvra[m,ULE0"/szؗ_2-(Gx柳+L<`Gp2,b|uey=VNAƕq~;Rw8wn1U&_zހg7!{p). ^8mŝ%8Ei)nEMB -Z8TEޙgf>~o+<7g3>'叟yϠp!K+\E?~t҆7eKw%.dI`5wa^JvC;fr+ǹGTÉ4?2q80 ' ˫+9r؀ϼzxݾ| v4/h1 F0{Ţ^P8^yM EsYٍ`N;3A_zaxp`SV_p,VbkY9^廬^˚廬^Z W#^кa7?SGe'? '6pb{_O *qeb~x$mrgpC }TX>xen$%F/K~7M㴱Əttq:Kh'M#.$U 8;)ڌK.Al 8^zߐsb| HƗsfM3 g|q3юgO? K|k.aS ẎڎCżpSGpoGpCMqco>eR8 Pp ;+o/;|=_d{e 3!.9y'/xW|/N)뎶TU7Aޑq\q(eO]a>vcۻSϕL1v;PAAǻ&z#'#|c-/#ECó{6TK C`eg &?Ur٣gJʝq[y/5aޅ/̳ [`Iܗ߅=s]  sRl"N ;8sx('a,Rp SoHƙ ßPw1M]8%0yC|k}~2ʲfI+ {]cMN0syP%WC`4;7.4}"4#R6zy\QuT8‰UڤvG(<K:;븶/,4~6):݀1<[q 8Ƣ2w쟺;?A"W'%ĭ3xǿyQS8my|ƿ%U,F"ֶ0O ,/[84,/Dʎ1fG9 KJ ݿ`9v1#oOc=8gFI^sWUVpP W5iQ̭NPJ939 5 ls yo+;Bbߝ/{̍ˌ21AwqMdq+}Q#yX"^mtc bm]占mi&.Z_vc;\<#DP/g1`bw>1) fKL ADws )v+%(5aΌϏ0gƗnvnXvxcz.ߞFK=mӐ}xO<i-@b0W`'x9M]㳲S>yAw=pJ8kE`u'Vx.9m6WiOl+X֟\/jQz*ܰv%t􁰖e>]CBR_|g`C3eIr {6[u M`2EfҵfaeR`$\ BՇɾ,.ca4N0[fw3${V$܃ݕNZYWQяcjEwӅP}U> hߔ1G$1`o/I_6C7njyzca268Ot.^*tݲgX+}>Ib_+CӞ\Jaao[_ڰ?&}iN(o/q'q(GToRQmu*mIEݩv(~NESyw9+,iw)  ҂RQ#ܕ`maɐR"5b,Z9Yt8UJޥ">NDuS<2!ǩi<9 V]j7m)~薚>q e띋X:Dj31úoKsJwU_̙xgGaPŹ`_^I9x;}{O5&*s}UթM S38% o-(_&OCGyP AvwHϕXPc^F$cic;lvh{^؃k2NZ'GZ3.Ymus;_ {nJiyܴ:}|ZqejZwnwp34]^;u]S]q&I;T]ga>aa;//8p:j㪥3ktmܾ(p  85i1u?/'/Bu&)8?ng/gA=X~(ɇ}y=.sάSopD37tDHoDžOIi.wI[ɯEΝcXf?藺>̾_=ՓP7>&猝+YH>ӕq9(/Y|%n%0of3c{Bgwi|YBhyрf:gs[:Hw#1Mb.Bc.`z01M,4^1͈,ܾğL0gY<{t?OFGly$^X[Ŭ; @qGܖN;ѮN+&jpYS'd?-fO)ڼ㷏uc);雸5as3&,̯ݩk!@}:ex0p~|Idhɨ-Y=w;XbS]elj q؞>v~W𙬼 ` c+p$jƊCЦs9;Õ ^r!Ҳ"&e3m^lRl&5滗lJuz\aݟ)y U +Caj}vg37 ՞HwfP=xy{Tx2<} C /IoB:eg!G#yD8MYƹ'}*#yPd1Uhcy?&gqJY]}YviX#~N,?U~}`dU_:nIa95f<7/w5嗁rr {P¸7c'?~[9wM}+P bkYt^xn!: dBO6 6z.jЎ4Eaa {/(, ;1-Ƹ_cq^TM3/c/R1myƸ_m.X/+JsZ~.S˜뛋r080~I, Re֟psbU;l6_pfv=.]'4Tvjs;LT{a)\f@gS•&P4pjļOl-Fwsz_p)W97l/3Aʭ ~/Y}UegþB(\`}]Ouδ(/L,/y7c,/%a" o8ܝd +{~<ZJJ6ɺT?B寒 *ߋ|,wU>JG9#~1(іYӋy]JJ2.vL1~)(7ZUcX/@gEX޽hR *`Sr(yF=?ۧy ؗ$|1j'|ae /}¾OE#OX$;ѱ=Y׊/;]{7f/If9>QNNJ'EM9=˼J#贾w&zxmOHDNJ{p|d&G:8 8ē$V)s?yۙ:'ᕺJ)~騏u^>vlo#>.~,ΡW?dxe,?ܸ)p?e{/Q2%~NVs?ǼSxwwQ\EH~Qgo9BfWB枊B枊J]u > Ѯi[2a̙뉌Q,<̒~@'EvXPa6mm!~%NRX7ؽL_эy\< Ϫ̋0<(+:>ƱT;,OK퟼r/qբx0}_4)Ok ?W8E*\^: P"f>ώ[yXȋo(,A1p9b=!`^J ko̼0t+hew2_*|P3 .Jc{'/^ջӢa5InwzB&&F,r=<'RzN°b};(FgssWd,`4ǻ3p܏kco?qBZҚrwmÜsӁ[ 凶z5Cgq\T{;:w$oc)@aݵ^E) Sp*\K [7U(\f_RsaLCH)K Sx»I{ '08np*v`- [00ojG̎|:૯?$,?:{a~dl9#β#u̵ oh6̛ʍ Ww)%; wRx ^.+np2 aĺg<m*(d+N^ч,]x۠yN1J_=ܿىkbeM*ps+ޣw-6t]1K <\jylÝ@0K=q's^u.G}߃A>Ta^)I3Xw) _.NXq>F?{ 51EqG؏l*n%!$8/MZx}rC.&Caa @q UmGBƇ:ö)À߱otc@G3/ cy `7{*}#,O~y /;ۉc'72pu "HU`^ժh½*IpGr=q= Ie<={*3~? 8_q^csǷO'8_bE=O9;؅8.sϙ!W Qv7jjCNQ8?3ʞe$MyyQ44Tu=# 9E ף9(:"LmIܓ%"O*M/·:KG5ErnwK),x_,q>=4Tv㹮y 1zIbF N Z;:4o3i͍y4o;)4oV|&g agi܍Dc#}xh>s7Og;g|F;͜]ù8W0%q Jf^{5sp+p/Ce r1_6H?+9ź!ĺyrw rӊ>L>¤Q>L2r`݀Xee,eĺG3e]ڔw^#@s;bMVTv )ʛ-K)_2/pI#.cV8c_pG++Fa͑vL_ڇ2v&EYG;7b^STnV X9+o8 v9onVv)VGh`[ڧ9x~ ~ڃ;+cSW1np'(H SN\ T8Zƌq{ʼ# )|D ?T8Ɵ,+Y>Ҥ`O8cx[q*;[23L٪&O*p3-k;9.pzQ=ۥM "AL/Twdu`0/x#yx o_``*(ܛ[5D[X11kp1~ ~j|3p 4KϬ^_!{-bxV{Wg|$ na_/#ũ4ub_iJMB1Ao6X;/|t/Y0N#y1L{WC`b՝y Yv?}e%e{y%jV7`?K-C[.S%nZԷ-Uka-/^~_tZ;g_z hLs{?V;O'ym̬oopmsN Vvwvq`Uq:TRCcX5C?c 9 : ף1|E'աz/k~NP :T~Cb :TN֡ry˿P9B.4gߦuIOw^Fԥ}s+{ya^׼ޣ8+)n5~*{a96xP</o?m~H֥H͑\L=r2w 8T _RλGߙ4ݭLֳr n[1 N9ΫyH1t;99gqy ؝ʝK?tuz/z )\/!:S^rz)Sj9<4#Nncۖ9˝|cק> )V^iJ/g0gok+vI̜87jUor2ϮT(?)|ZS )XȮrf˃@޿{aK+sNaII1ޱs]g ~I#g$?l@3{_I +`&c' Y+qێyi7/%A]?D JtHiϤIְ< ˱CsQ;O~^6 c^XfyNL?eyn~X5XlH|gWڔ, ܞ` +i=@RM M|m$ݟ_0]nH}l4{oH^rAↄ/+8N? 4YU[P@ޒ1AvWXܱӼ'π3#8W%~ClDjM}Ea=ܐ7纩m)pYrYq#tcZC.;}xgVE7rFa j'[1%ojTޡ_q m_")O$=S5_ݛںwnՕF(<[ReMagNj|ca;μwK<˃wg^h.s7YN -\Bg!_f$iBN_Z.z0#G~6wUzFZAd^7G<8_y={#-^ur[9cX/ü~xvq :XJϛ qu3o(L_?<gc,_GNz7/?ơOǁ<4L8#7>9x'~l'4>Qaм3 KZ̟)d:jmE ;p/N7 -kN5gssB{u0o55genJs+EY:Kd4\V4Ԍ&nh@h- =z•yMͽTZ?m&m͊H7]څe /Ux=_W)^^ySon 6=CTnIS 2n#;[#Pj4Hf v 63v~=Јy$L?gN Rx?WVT:HvWY^<2T5y eEVle\oQ{2̃ZT-yi`kAeϿgso8'B_W[qz:tE;;yx ![yZxK&z->z7 \80oB-UځhˌIb-g{X4t瘼^ׂOc,ƴ{B_yPQҹ;W0([f+eK ̋Ie^,ԅιp9e~ p>GœvYõn5Z+,\,c]YБ-#cQ,}{q9Gws=Wu?e\*[y|&ӷ"{/U8^ ~)Jp:^,*i4{aЇe~C_?byB]mX3q,E,SrK/IK%.2/yI($Hʙol/C/K{K˼zÙa~zGʘ.'3Tk߾rQC< p)AaD Xswjmc-IwKzNoKQp6gUnp/)<_ E*9P)O /Px>]=Qb P])<^ޠ>O+|WaWΫp+=E[ɊH Wm W8Jf Px«ޯ%)p/(V _Vګp]WXj"]G(|Q ;py+V~ Ux«ޮ/(®* U8R Vc)|N?vu28E{ T*G]{ ?W8T8Jw<*w~p&A]w6 U7+|\[ {(wT1 PxKޡo pPWU*\@(*MXg(X T( P +N\ V=J+|I n[ WQ^O(|EG 08np/(L] #Tu5QxS^W UA ΢pQ+++m)d*w RM$Px>+t SQ* ^ WVH /Rx¿)|S 'lpn#pgG(pg  *#g+*\ OVx_+|\ pgSnp/(H VIc Σp5(O Rx¿(|CaU.pmLRFp +3^ao)¨pi*L S*: Ϟ'.:HO Hӄn+7D'5oh~p|.lK3s?bxh ϿLg_=i`,1ODbƫAygwc_d=qa?*p[ѝxh\8O{+Iv=xĎA޵ԇi_8 TS>pzc7#'fFĻ#ChG8ޤog0O6yx_<6$!dlOsBY"¸/kxWgn1/2muI<=qm-K: &NF14턏%0.O'ooi=Miz8]0OfmY-›;"G0szӚ`gwġq}2y9+n1q𯍥|pboҌ#^0+o[}7[7WďОm*oNeo0exE"-W̊+?х cOO{Fҩ߂tox*q }쎻ßXo`=*|L'xxqg 3*C(1Jwpo ޯ{cۋy5e|k+9w`xuf _OR'Bl`^Cvطyc0?9e=.˛?rC7L yK`4k淬Lʼc|29A-@q*<^0蟭*L|Fasy]1< Whx-vfQހIԼgg1<{dR.{-_eHJ%N{~1,򎖋/ߑE1LM_Pݹ>?TuݫfIdoe㛖MQ>WhEa9fF?N hQនy \ޓ2ş;&ITw/gID?Cs2o3c|oiep뼒ѯ9Y>QNJ*gn Rx'd߼'g $?| ś_sy IxESyB(w^aBGy|ȃ>!Ry+q9/įƬg?#zYrK>[?CR} PM#&&3.n9A=y6xx&~aHobV+;A]&c_'RZ&Q#Up*٩T[QV,C]S Yl;+63yڝ/ qdd{uWyu="K3#MDOuy#wP؅?|ao&:x{wM=S讌cS |&T[f<+IJ.9M9Eܜ9qAԇGp ;~#qFnhI1GR\m3AY#)~n.1དOLX8$|0* "9];A>Z8GӘ1w|tmxϖgcя&Ƙ]W|na~cbzeg;_~KQO{P9<x6| ,o:K")BYNwga>[tfPގacS&ޯ2F$R@Yf76 0E20x7m{o0LupO10np[>v>&=v]FϢ<>Q:`;Igb3o1!3^73i~$,o,Umj>tE"]fųjLxpCߧ#1=&L?O"ݛeޝvhxO)­7:4vkOwawɧQ W% wS>qs}'`.f{f{6f|o pI+w1kTW&\]gWMtͻZ&xQ{?Wa:+l-Cg9tϔ[ Ua=:-:s]EoF<%}=c =*Z^=W+X>t֙ unc9&Ρ<)zPDoWTݘcki+w k/~x-ݭY 9Y~ Y~Tx3 Lt|u=fw *w "^E'-֑7 wC~1C~ wa~ o+]Ho);Uz˽A<.ʰ% zek͎WSuű;;U Y_JC ~5?|C~ XfZ,/* ^:ܩú,YމR:iȺbٝʝf7a9 o- ެ}kWW)m߬z;(^HgƤ |yEt;Bw#B3X p #Z yp+6 yTV[pkqީ닅>ԢyKLX8rmY *? W췸xL˜q43'Mcyy>sf?р]AxnpRoC?pa 4㚀1?pEܑE=74n\q؞mdY371"{SZ8:Ʊג(|_bFgZe%<I-,+,[AW'\ 0v[C:n8pdly x31^h։CG ,pyի]e ,*#ORyFw2Fwk3Fw3FwcRz;z;d[VC 78YhXK>l ><9oNl$ug4ؑ`vmnh|kq~΄p%{:0MQr?2s1>K,3iwIq)@츏{]mzaȦ/z4 =p=mE˼}.kf$m9:;d7wf3mn/g{1fԀqrSGL7kVx?iD`|.;_fd|mpC~"G:5[m!˚ap}oj\iE;_Og&-7oh950οZp|r^w\sʨguESᾀ ?an!r3-y@9طgo+lOښ`w?NUL\9Lc77$,nzy޹Y O3G[+8=/νLK@_q4J+ |l|n㴤I׸*K$dUa`؆ v)sz0kzk٥ _ۖ7lCv01c0n^i|=,ytT^a??do,x%/JZ-4YIkW̚ެYyJZebޮ ]EcUm*;XWVVњU?ٱUϸ_\z_hL_Rԗ% < gC7;ڇ~8_I9 \~(ߦpC>2JH{>O_c 4_uZT7>:%tuwS4o<3Ks7[K3C/iu+NF_s}VnKeGƖn؎S8.ۤ ~rTnXeZᇪ|}QoRO)|W; V7u)WN˜@`= )VإK +L~+k!2/台P&^Mg`<#{<)ثvK7!R6:S| c܋?AY.|+cSau$~ұ<+%%7%Tc]ژy9JvK7Nd<е`xDeP&o[>[@^0oIix8㯔vI|` lҷfߴwO#;c 5iKm7 븐μ z|ۍrCqlo#؛uMO`Q&+<˿qelxy۰VXۑx- d[|!cdl }’>1'A{ȼxxB2˚:1Ϥpo+k_9ݓ:`~V-6ُ/{l`Gq蒻`DNK'lVy;^lkܑ;1),<繤>- K}kڧ 466m9omGǵ4fe{eL8r(F}*KX#~OWXI=t[Oyz{^/[?˶7hm46O&񮟤nֶdoT7-wu;ւMV}oCL+T +n_&R#%j4#^}~bn{g %c e-_Gd/[NAiU~*I 菶pfJ, m%  /_N)/ l?żm;)x+r:m)|Kk>?΋dG%݋8T&a%Aޔ"~@?g^҉\ 4yv{v/]eX^ё6ˣxØ( P{.:ۂ8&a>k,\ʼj츫N˜5ͻҥcE2`ǻ~ ަ=b{w.sUg]]&/d7O7v.S]c#$M _wSWa7}o6ajvMvlwn{^C}Ib"`Vƹ9tdns Uzl36ל/gmm3a.CֺXnΕ/6Z7w=v 34Μ~\1c dnuP2/!:o>3t+`9 >qo}߆)yLR~ e?+ǙE/w)^RNsɸXMq\Uqk9/)s?1zG`W kx1cT^DFt/ԕHwoqyN/<-cI#`_w:/k:a{kG\Cn=Oؒ1{)<^q:DµyUJ2~?? ̛ˇ:p[ԍD{Q6\)gd^,1.Í,9| 3^ drU/^xUFl։s+~_(˧9| )iR'?ZsUUxc1䱦q-/cPeMc,{O] [-}ϼ*n$}&=+HHj?aoh?-Y PXsq`1˷833/YՑP>O>Xo՘޷cu|CEf{ە;9{A'v%yˢF#)SWu=]v>@=KS6'Ùrwk ՙ1 U/ $ 3’w[ޏ`g@5n>9໦{+KI U/[\p!]d3w,۽"Wsцݡ78{~ f_(^ S4ˎ}, 0\$am wȻ~*f3@;.q W+eLH;;NRl{X'ardCCDY_<-ds;ooϷBc;.4ǎKQ|nO'Cnv< (p,˭T#JUɕܞCoe)A>Ja+3Q:S*^Ӂ&~«, Oq() 8b6{*uݗ`cy~G\$~v;|Cˎ@s̯BʝWEL^T* {q0 :L:J ;SAkȟ6,')3=3 82¼Npc~zwy]w̔T(/>Fy1vhx8lGv7i]';0^ei踫XӛT,w<(\ppsuhcqܼ9,͌qJA;uYGCL 0X>koYct8wS^p$9l7[W(ei>7+UZc9lETS1\5;RE?N'#/`]8#<7T+Kul8A7gu7'~yE_Y)z?Mɨ]{ZW}!g,ǀOrˀe-9\rN,$'̽OyN3uo1ʞV of_3Vxr^OP}㽛:,feK#I!#iŏt>?D?_ۍ,o^ sT'wO$ aiɸhNo`9!z2oa,Ue|]:u"0dڝ[?QYsu;[w@^X5aryי?eLy* ެ`l?:D p:~TKH3:BG6 ˚cJ]/:|_ 1,_ xx=K˿|B8Noq˷8m"w3+Ȃ1JYe<#g4d>-q&O*Ͷ9¿o/K_;i%r x#B,pYk*we,ueYYr[qN'Id?I'M蔹?Or2F#xcN1D!l8&,hWiCc{ƴwzv^8Moyt(70/=l=Kw(~Kgy9>Z>3{3<?_R{eE9qݧd92 ZUqcy5-f#\yin^13ul^_+~d9OXl#=N7_{kIٛw@pr}so=G9/~t^.4ƫs-LK9a}z?u>ops췜y\yq[ys>̠=֌w1lJ"7;c9/%Oi솭y9߹_Qoxk;S moo}l>k(*(ww۟@.p } j8(w)=]pi=X\\ܘż[tVc3tUa=>p 7>Jnk7}C,zcc ;=h!p<l}`K;h- |pw5Sʁ_wTJq8ŏʏt>j}p$_WVPCx|~mu#΍~f8 pѤhPчߴ-kma='w(dx^K&/`Naw%~0ns\2q+|ш%7nK] aQ /QFx/Q5Q/Q%]2u_pIIt=dX˾M2շEX)ث0Ow9mBPג5)l3бISgO>.˿/Ё>W̼X 掤d`.9mY9KPy3wyb~ez] l!Ygɢ6pc7F2oMbWh܆A?tt;JGY7H'W̘x-WWx>5 ܢvBh臤\V_s Z޵W]AAf}Yu7p+E- p0mmpalyI/v[DZp`=y[;ǎXNnUjXĭjWo7iuӳG%oC?n:b^?.7[Sb$vm~!v?i֋DsXIoa:C]b¹eu:\4`d~H KZ6i%3n̽Iu$`-Q:Yq~ls͸_(c7Yy >Z]«oSJeMrRH:vަ~Xܜ?ncMmT9Ɖb烷ܦB (}+4=m33Ɯh;&Of2C'~xewp?p&[ 3w(N=p6Àv.gø?'1ק0Gw̾7q7;qݵbiRfutKw_q[y"Aѽf]p)@{5kK,vg5Gq~a ='g˓MuϬ{GrHG=%kFj<30EñÍv)05M{f/v]Wݣ>h\4L8pޗ C?0/X^PWe4vY݌O} ϋeƘp+DqJ(¹SΠpb()Npj*f(*<תDO[̛8τZ0Ǿ_5;tw~5&vmyPr_gy-G'AX^p? #OğT+3m&ނ {c ]F2 &iޠS[5o#O9cy+\`ylwnDTo{4A<ơ b MRe]F w1*nCG׿LˤL#=go,? {!q' nCR侎q o1n9sXV]8gݑq"q% ndr9i} }úxnE{q{E#[M]5w֟6sVS.p%)]P }F{is:6™Ϊ'*^PmMO,O.}-(CK?kr'ҳsQ+}]{^ƏgUn %;=}/Ma[.Ŏ'_/X7/U0_(j{gܧ Y5  'X=zvɲ2=  HWu.LJ<X_kMnoނa(4(V6Me=Ʊ9- /77Ief|?};_[0kU'zשK=7b6UſŻmϗ|$˛|/LֳCc9voMvFa-5qaGdn%xDe!#* 9skL*HUCa bc9*<4;$9fȼe\׎dnm~sB*_{ "COeݔt^vO4UU3hZ.a^=AXg|lZ%Ͽ9#SvS[<wqdl*wT ۻ[9I,~%9@dGS]uቾw xgGtcMw뛎N]IIWōW8 i?1ٿ硫gsÙFYXC&~8^9>\u9{ H83e^GTae}ؤ"<6˷c/) G'r?ғ=!'C:a^mi4R0Y5ȫM*^lRb~d^^zG2?)OL<_xbOL<{Q15B# ;?ȱm(ɼ*]" s9Ez;cn:5)Or#c]qC |}y} %<;=i 2 ?w cPu,w;Q>_ ~IY*3_{EYG6wњ|pg,ob/t O4k{[Vr^ &󻸮d@k=FUT1cT:vKF&8ڿi^I(E^c'w4Ɔ?Uի)_;?m<᧰W~0SGv2̟O)?]~~<+^+2=q$v=A$ |?kyeB򾔅zA͂(<2ўe73QUqڟ9o# j*K?4Sq*ّECe=iqML-ߌ={욇̭"@b]2w,u!O _3cw~;_?Oѓw otؗ9XcWX==g5zG`Z fp8=S/R~>P],+@~~OxƟ;Qg y٦]iD `?tI mWs-eEH&2弄'}kJVgDQX'Uq[d. ?V< c;<pEF0LNhxvB c]i3hL0".*+~Vn]PnTn=`n^i[_Ҍ1GUC?`5J V!pߟޫ+:bbo6=:hUͧ+1 Y o|? Qu3)aoJ /[I<,׽@R,/H2,r, ,_(6-J gP8YάkL'\t:D~ w_>By'.Iu~^A{^Je/ȱҙLLclmGCE K4WԝyUp@0n62: ;.2Ȯ0>q}iov6qS'{&4 |ԗX*V /UVXǿ~$W;+ϼWx‹M8sqUniK$vϲgrQ{{/=u[5=5іj?/i`۔_̅ul`!D|M9XA={Z{V2_܍U?$>o]q .Z-Ü޳ jUqCR{gB8RD?ߗ,7zʀ!oz$-hObT>4n!g'~\A_$ϓ<^_^ıu>9=_r)Y7N,/ usY.z)4puc;/S*O)vQg&uyzś3ԣè={\_z^&U3K`]xgEo!^K0ϥSP欇[y {vs`oػ0eWM:S8ԯ/H=g-0opWV^ Nep}?TxpWU>'& `^* `^# ` 9'!+N@5g?1f@c]ٝMʝ͌Cթ)c#%c7f5#&w*bw=o/4>XWvgrĽ@c]ٝCʝ'ssT+uyuc~T0H 1T}p|_wZw KŽ@b]؝ʝ_oWu\d.+AJ$o.lX vrġ3q#wn)wnAޓpuD{JC^x]]uubwQAvo5y ();r!9T9t~uoew_(w_iw@ cꊲ7O[p(yB^Z"An _x ط[$I+H S1c+Y1g+ p}#98p蝴!â(? gx q_3Z0nDą2U8ʛnx_+g;I~\qSDy܇?y>^yD[>N⛻{O x)d`p)(K 9˜ Rq=y T|Ǽzt?T84w9r[RdEPFi[d5_ѼH: m8\s@:@>@qc܃\P¿+? Oʚ܎e"=Eqe= GdLdLJ*-y=k:ƞGU*{K5|̥c{7'Qco2ƇOw9woRlkS1k1>$?Zl Of"%V"$Fҁ3~M<~YYƞ7,@{-k]7Y׸A%xF<CB^פ."zqޜ̼MNJBܖB{d 0=mX$yUve58¹z5Ɖ/>N$Y)^cHU;+UVsoiMJ~UY5(kGVvUCZ Rg9`>#$x fvԥd#\G;+L,Li,Foc|UU:ctMru٧Y%>7L'c T~̟f=Γ0<ۿO8#m*e9Xp֤ד˄u‡ 7cf,-܌`l N>Dg^O3Ȕ} JvL©C{x[0kPn *11ƱƷ_?C3ǯPy%.;r Po9\K^cVeF bvsӉ1ƌB:yx/tJgԃe^`qmt,.4nkz.#[LްT|8_EQ>~f2/OqNyOJcϗM0ylżt*;mKW<'٩ibÕ.adAm{4rDžP!5A~ܞq>OdI_,<73 s-VJ6ۈ żje}F%>Oփx'ECc\$2%j&Ro+}XWwe =.G s"樫Qa2ȘT^)k5-_V<ەo} VD4xpKn{|'el'sZ:ߴ?lnӇ~uC.CEn5[(9)~5E#?2߲)0/a1[Xʍ#)eE7w>VؕϿES^;-τ3Zam/׈S@9Ϥ, Qopa rEc< M|7SS'Mzh,sWwJ&JNw+n|9 uTg /,WWyI70/9. Sx)ϟ2ȼ#/-:ϟ-Nh~ /3Uc^QGלw.h9;VEoȹ 1F>f/)/.pS_Mc9 vHhX(uu%ia$w.HpUgۉO `/nQjx-'B+̫SYTrSžBBī gk8JoH1j]KjD{%IcsmZ?+Bހo/WiOUoIc0dỀW%?ֱ~죽9'o=⾄W/}}LϬC`*W{qWI\RmT>۩ɻJ_^_^⬧m4.vk]zHү&kS؟i}}^'A loklj}͂|A+)~OHYurSPGUeYeؗRf^7]/ѡ@ ;]JW_Y1t4(bڐEL{1i>-b",m+⛿^/~d#^7uccA Ɋq`㥮u?[E?۟ 5ӢTԴ){4Iׄ{ xz'bᯭe)N;zn +﹗}<-[m;Il̍r?w0'I? b[oof)>Q1.7`gj1SnV6`v0Kߩ4C)[GݻY{6mvw)l''W}.QO{<O;npR{iDOfjm1jNWohȼSDޯE$~3;#:z{J&>~v0|땼/t%p ؋c{!!̗"?>eIm]ܳ^wT +'NM Qi*?;ʼnևJ`ϖbi?ġۡ?=3?9*>h`gY`o+'h a8co3c2#qN6/ X2@}Q.ψND' c|Ot~(:!*C`2Z 񐦌7I9=V{2W'8*+`\8J1^wQ\* *k% )lNŕ +{,Dqe(l{(l?x(lΟ&Ubw3*^L V8™WXE2Oow UbAY|sj d^.`^ Jۇyr2/|G1/{ H|W+E<|ShʽB um̫ }}xyQ)0 "*1OqWQJ\r\c ,=k-rOաqzE k"X)nY@ ~'RNg^#;(ǴX&yDrn˛:xdys%Ǵ3eVز=4߇oa}ʑ]+gIboi?_9H_t%XOr'!%_yy=v;Q94.|ߑQ wuSxGpP`Yw137em۪0SxsPxŸ*{Ŧ0y^f),]8h_3#w;Η/zb[ߪ`;`B*н;9+%:%{OP'ɩ?rWv,l ~ΰׯ~ ÞO@*OSG t/|)%1,阿o[>Ǫ0bBnpx]+CzzU>L#9* wSx»CTQU8Fq gn7]N.o 2G:3ɼgu9hCv:A%sVGE܌.x%jK&v[*јAlݹ/6ю:q_%3Yss82m_>+c£HG(p?RxS' OVx Wxal ֨sݪM*IK_VrK{*+9呌q/t1 } Si*/yX~/WwNʺy2?3o&{=Y~_#<A>CqW9I=ٯyOq*8hI_=o _R3˙yȂ0˺]}wW!y3+,~\ s!%^-[+}8qqu)cJ$_=m_ios؆H<*KF~>Yxe&~vӿ/c1@X/,7e[kx7#o/7 2Uu~`XG7^ ư3 㢚YkJn9&SP?yq4Dw3#E%v%/戯@ǸP.ըN?R S#ڛ y K0z y <;Զ ke*16L$6e~ҫyquDekysWS&B66#ʱL9RyV0G}{Y^3bvI0>CH[C<u’נ4;0'E*Y={ Ρ~&+W1o:VO7j? ƴPҲ|_;zX4˧8;0^(|.|{·ۛә3LZk%_,0?5%Cx4u?d0gbg?Z^A^[Y :2>Xز*}UtIu ΪpIԥ睡^D/'M֥y<_Ơ9|#l8p0R1oL񀿭LssoLsl 'tO?{+?T<^agZṂ~Oi+x3PvoC]Z N#ǔ{8]u1, v$/Nwյ}x.zu#oG5oƦ`b_`s :hD \n\^߀ƚKB[V@pz ' 8lзl~pz|ENգ,hêhRf?wsp*рG1<nkQϷ83oWo%P7wΡCўlAG_ϳ*[.{Y1ގ?/o3TB{BknNIOmM~"0?1?S~Ι}m|w&,cvG*|wnqm'OI+ɼ{Sx_Gߞ)+t\"_a`S}zPPpڛ}h Gn`*feր߸t[n˸ _z&2_->q[>3q|_ڐCJR4t{V sw*WCXCpTC-svߐ-XJmUܪw'j#չ1Ǭ'dߑ>Au_D1 ]'g[)(R{y. <#<!e/,exCeXՐ]E;zI#cZ RNթ }K >1 {}!yW/;w9({bFTs5]JnnXv$ۙvlDc|~ S~ H\<{ՃPe pWBV1qq(8w q"(v?X V}\ƾԶF|F_p{~`.{紲m0+sW~hFsIX18M*XS;.ױ(O>hU(\Ai. Sl/fn{q yc=kk3^覽R]wr! k0OWw&B"#8yuo99]x6GgVrp*- rx.\{p*Ɵa<x u%PBr5 Js~Mф l W&d7Nה*\ g8JMfYS"WY9\Q3r8I)7(Vv2nԄ85G0p63,+;$OO4 e%M_&ԞYɥQ'K*FngUarlx~xyE]I\_Ry^˧-).uNc!{E{Ey{ 'CR ̋Tq'gp ea4}3𷢉=X7cWc={sP,Ogh߼$~*ޫ8ɼē;J믮x~gH5uįߐ_ޡv` ;w,wW%Q_Cmc#U+\N WPe0ef#9\fE? R,oDw2{*,v'}oS $8gU;:EYvO+濏Ÿ*O]]too8k?yO6; :^Yi=G zYR^}U⼘2o]_-YVˢuk Q\qaѽ-+F E{–{?q[(GUxz++ޏJoQ&+v4'U.ID9R[2~NVfI A^q*eY8ZQ;% {0驌E_~zs2 XI˾7YzƼg`'u'N6TߕoekݏP\:H湫/?'WA{>gI' @~̏t8p^a^o={jƼKI {}q.;/tt焏w9J "HwX'/;BX>P544ؒϲZab`ŷLn :šO!&Bp%{~{E HXZ4%Uf NRUyoEkN-7m~ f]2fwp>_'u`fYY2wu^Ws).C)$~0֡]7~梵JOSA&FNyYΔex-;)3>f};6d;ǔgN`dq'U$PvQa`~HܳsKϙie. l6R}g_+9 aSF5{p?řp-S8[L Uv=Kq)U WS9P?.@ݤpjɫ0By ΦEm]cv6u,%X}Sbzv5yx^gZoCLCƱ7nB=x3WMQ2uلweF}833-F~!*ߢtxIP X [ nՅtv!]Ӻ]Hfuue]Y[WPWsk`v/+b]mqWyOY ۮ@#=%])qJUM$9Bׯ'~8Ev6wd1ՋĶB xfd=vܜfr1y [YͺқϬhDݮ];>][][m3j?DmNW' xAWBzYw路c .{}Q 6V8[Eؕ#aV|{;&7ci->dѳPxNH;*=>0o `Z= `Ys) U~@}*]1vRɺ;$lCo ~ iy=ӟuxO ϨIt qx2Y!u _r{S3Fe5[/N8pu3w)Nn0k3ۊ] Gig)O | O_t5>>YDsNOm?[Q?)l>use~G#oxqd6曻>?dAU cA=t7TgQom?xj맯"Q]J O^eۑrw/ӏr)M=L>R8'P8Xa‰U8! U n314βn5#<-Y6Cn-Ɵ0:Ю,on${;x Վ܊==۱;ڃzYnG=82w}#~3Fhw'RPx^N`;XΤxsk<fYC 0d*(7II AwOwy1 j=(1҉aԃY2^p;?X޽Ars{:/, 7W?^gy.Pq#]`LH|'yCUPA%迈u G f,'OdIo.ڞE?/&/Y/-$OvpLqpp^ə.= ]*’`8Ӄq,98aOrc{E0cf;gXd9X׋}C3y44sR7KlGt_Er_,e\mQg{z^+ݽ_YW&M8\ Yezvm!wCj8--Z<7Ю71to2q8 g}t?yoгŞ2ф1bǂ ObZ803әsg3.#(Zp{<?E|#}Oo|mra=K?ϓkXy9wB,ku5Qr1GZg8 ͯ]*!Y4qsӺ}_>27o0;՛^롵O\t;_֨~_{7ܛsPm>RX,}<@sPĪ8p>ԯG~[ٟQ/Y>Cqԍfir)n#jsXvyk Q>".,h^`T]"yʄtfQ7>8K7<mPHwpʾ"Yc8=R߀פedG_&a=q0n}ͻ5c1KrNbL>}8𒾼Ƙ9b8ai_h5#;ߗL@a#~p>9Ɩ')`0?R ͓8J3nMGy5d91_a-c|¢AX^ǻ -k.^x¢{wv(^b GyuZ}[UD Ŀl~̫7~kߤO/1/nYP,lktYS1^g-}/UQi@q*0 TCYMza{ee/FkJu~g+3S88?<ʼ[{s9 wy`i L.͝(RTHRk ;939+o ͕f%/qp֥C)q' r' r #m(_f;D~0A!K7ڿlil)/=2J̹=1:<~\(L_ݔg7{9?6ef?t=i/w^zme`8y&o`?B?=)L{?Wj(NclwK7@/x~o918Ưs N:1N:׃y&٫{/ϥYΩ)p)/T#o7.rv +^ 5-`eI0,&^Y.wQs>6TRLZ#TdV0X YCC;Cһ^oZv_Γ> f *Y޻~{)`'dKn&Bw1vς~NKJkA3 C[502bc_u q1Nu.% w!T"8~^ϟl\f~[;פqqb +lD0Fv1no1iRS pNؾʔ H (6b]q!2OY^{#!Zu~ט}#q<:?gs-縡яnļtmccyG0Prv3oi*3kexXpM;ˡ7.7!ma~CF r[ʣLg߀u\qoooT9zC~KWyC~ka{*>cL_;ȽwY۱|;*uk'\ w:w;~=n㙻qُX&/? ɆS_ `UJ_h/͢2Op}U2K+R WV G)\]j WU)9ǔv6`a.quJy.׈c }bՍ}O`=ۛsGy/0VxڿsA:Ɔs`.?Y۝/,c{`w|1-3pWzmx!ě" Wd>Twy.L5`X: 6bY[TchB2]3O~8wp^on<GWwdΑ7D,Ǽ27kHƓ_'VOci,x<֐5q+wcC|\@B $p21{eU/fiOUɴWrdwM=ߓy-wsUWpk/1FY(N3G/u䧰[(N6¢ /o>u]qb0B= :;y–Ճ}2u$eGm(ic[.LmNc<'.G'̟v,xO ;.0,!im4$SiM-$Y7~"'Tz O#)0"i-8TJITOOTOOzх?7wj|2xDEm䧯8᪎ ;L⮸9e6g50Zay, zN-a졿Lp~=ؑ+/u7z090Nr";*X?MJ!O82)YY8-؛1֠4MeߛuY.LT,kڳh7HHsĘ[>WǦxxi@ý\y ~<ty/\3E8iƱ2^$Y|E33{ o|/g|p<^Rm:gn:ovq"thCsg/dz"?eND!{y!f~?ohm޺EyCc\&#q{MF3DŽK9| ; 0\؇; j=ܗ~cDyFluc/+!3 7a<Q! /m[/T0xU4x?(teEI_JE4աn'ʏ>f ]=9#N9.:Q-q*y/0xb?N\#w$ג<7a~ WP=1*m/Wx?(|U+ S]PKުz2P_gc:oO=@ߗqwxݹwYkLM@W}v$\&C=WА} ew)ω~ۇU K$kQ10#Cl.2/>n[adr}E{]@a$O G߾OǼ;`~}Wy^$j{1?E>}h!vyԊPy>- s$Gv{z *a듢SI:ϯ8cY ܜDsQ٤Xר(d>XPrEmoy2='X(f2WLumdZ9ֿL>ydZ8]OnUgO6+]!g62E)3185qܸ0 5ٜOɦpҡģp㙵pH8q%| a[I87tȇr p)N ԚצP_˚>S"ť.g9U?4֙4x/W8'OO  p`~=d\~`5Uk2{cfT3d%[:f8ÇS} x+5S1g4ֿ>A|kuX̧ܵޡwiF|Z: %:utk+XFpF$A̦-jSŒFƩ4Q=Оl C}\ok q>n7-PwaӨ0xN=h4?xN=hi-in_ڂR6=fo)~Ka-5[ o)|@x%y]V/Opr$,/OU0!f s009Ü9]J;k`\;rz|TN񾬴ߍ#GSpcww }^aa5o5o\#=7a|kEJn`zcO7im/;kݭ9v֟Bqzps_3e_L 0Dx%o'L2 <n-2]V!+Xc`d/]9p锏eN,?[-ڊ~Z+3+Y}.k@}5o%Y[Ƽ-?gX}xYhh7F!ʹ&3y/يEzf_p̼!xEgwV8'뚲vSCoYהz&3Ze@澘'1S/w.Ut~F1o:N*fXKa>XA7*nJ=i-{gj+dtX6';,"_gR1N?9/$鞙y?|)f ٷ+w/{Ee/\ef;;jqnx?}p s$< HE'Ύ-+Sv2˦W~7S-;ϞEc8 uaK6˜ SN+.q;aqt'?x~,:7{Y?W^ ]J*X5!$GXEO l&'8^gApts{"y>{ ƨgȔM0(%m3ԙӹl19ͦ;L?ƏΝgE f|comW:s=ͦIx9̕2.9Kc3]P}I&cYSHr>K9Y7GU+>^ݯn{ShN^?\$Ρ5+#ǜΡq/K:yN>vf+}?څN`_t|ًva#vasfcxܓjhsC'gzYSGq=B: ^Nߦc_dW]U:@^ʩ̏~ӹf#IZX. 4ݓX=߳{2ⳆSA>ӫs}5c$Gk19{yv|1ϜպwI'M%=~^.٠9sXjL!,gάl6ϜYA~JSh=UGR+~EOpgş5мrY*=+w9rߑ\VlVuct/Xez_4Ž. K_Dyy,?A0O&{>+nE+`MlfBV}{˱aZb LC/Tٽ҇r׭{Jf_Bpڣ{)v2Β#0z}z珘W2.MDf*ʁ"AfG *WT7> [m}8nZ?p_0SH# ~|2?nIX%'0JotF?{Ja5Zl{^TbYn܇yT9qvR*龧oN1 7 OvoEwY׋Js'k h$,61 fP./LF=/~.ǟt?/W0%-BbXhYSM[/=1N: ;iZV8(n9e9sVR~:'[XJ f^0[=9=!?"?KsAGhN1/pz.4e ˳H[:?wZQiȩx?Qc. U**J /T rB*X?a;C{bls!qTV8^hFO(Bw\MGwm85 {"T0zJT_7@Ne>wɷ y"h=/p;Wd.,i_BQ6 cl:26MÌ޻{{)c.2gi8 Ǚ\3M^XCgT-V,0_8d[6g<{"so\<.~s*{8H7\d?2"f1wTu?w>K_'U/]bĴ=Bd'b3gsLoGߞ ( /j?GrKڬ1wޏxtsb)R 3FbX:c2KgvtX3F7bX|˩@;]x!N閻rZ:Zo+EO4fY+y]N32xOU7b 8- cke(^`&OW>r 3X/Vl'߂Uʳ_`wLo D {{#g x\WSa;xrs9 c̻+%p|9߳Ϙy s<'}3)WoxQzx]NN PǤޕ%3ٯމ]uJ c=N3[t+̛^UN‡qаi.uݨŒ:|Æ{Kc `,$`.3"y9Ty kn؎Tz6l7T|)|UW%}Yy̑Ra PgJ9WD$9 K{ryWR:.b_iesK J϶q\('*-)G=˯4iܐvY{^GgETp]0dX ǯ쏘[/t8tGew o/ϓo"=%R s︊-;~b`7To^2AWQ;L;~ gAGI07W EuNG>_lg3~OI 3WL$|c։R, 2Q/}cSz*^ +<ūE_Sv/g`^If^7pV{ip߯ ?Z8EY/Jf=+1g&seJ*N('a~yU[xMٽd!azg4]uQzNB P݆8$3)M`~G6rv[Mʤpl ,waVKPW QNelüVza^Gb^gI~;S8;{ c-z3½cGy׼?{_I&(n7w(Bs$Գx;,ldXsTIۈiIOQyy,yspi7ŏ?7Zfn R3rۙS7Uu6|ϓ|G곶>곮>R>Nj>N곆>kN6>Nn鷺.:.>$C]Po0S9j3 t.](.wS& 즾nkM2F CEl@J@Dp=- kz4wxNOL v 7171jO1wFs/e㨶',B\K}y=s:_ugH 7{~4Gri^$3'R\N{~1o{=>nïhebC.Kx;V~o;pf~$#b/1 >Fyq F2.;fZi;.♻cuTg,+O0п6Np* 3 PVj}?{g7o5*qp@9/ic{"?@W}$Oc';Y*e9{c/w8G(]h/m vv=o K8~WMz]`'dy|cϳTCRr$f=^^ C9ld.Kxɔ{Ld Uzp5KB_2Ks#w_y~`>q{s槳Zp)rS>DXLAiިb<XD2?ŗ9qU+!qj?7Tz{xq-dM8-^^~Rye߄Ɯз)?JUtyTД7yJؽңݥoüB1ab*ox')=L\O7\5|{Ou:4"_*_2?>~f/|/H׉Y(;+Xz=ީ,W%$KX mQq:铮(Rt{uDįi'9}GYؽvD 0Kd.eXUF%d/6~Fx7|c+6{Sp_8on<wDk rX> ~d&_R #Ϝ9*h_ [{풌 >dv%㨷`ΩcsCkh^ w.&2UpM [#ƅIy=vo(GFطќXt/P ] AmO=>ʖMgV$޻y+=82X—t_RrG?d(ӡ%b@ԡ6 8D󩺥<"S揰aQ>Kc1 3ƂE+&?'+'?Ə~5WR ,?_Y)GX[ׁ1+qtxTq/`>0ާ8Lr1OUC?}7<\k8+}ŵ=J "{#lRo$;y&!LKx>g [ =~E[~5ߟ?l{^9lͷ}9o8kq~+w==lq;G}`$֛4q6T:S! ǻpk^d~%s韰̠(#Hvr3~3s5 7xg95O2}s~1Cj /8ºuYuSWkUM:}8_~2_@Sy'8ܻvg/8.s+rNsZzOxj~ y,w9rɗyrTh3Gͻ~_Ǻh8bp8J$oc,wpӣfnW K2F}(1yg} QG䍛ߘW2_5L?*,~hYwcĻ'4̻",R/Ay /˼(+/(/2 ˯+Zi."9c>N3zG8x[wK$PaDQgXqpECĿJU)uY߫j}Fs1? a~,GJv on/g(PWO<3p9~‰g_=S4D0-S8m s|w J E?L;V3h)|+B ƞE@3WU:{2C㹒/+U^ڋ}*}K]c0H~: T}qq/ű[@v 0EvE07)8Mϑ7xx>oE7A'U%?RnPxp/+8M_6J_F8$qWX#PJg*ɆQIxv6Uܮ*ՙA;pwH|=58wJ;~;So}\<<ĝCc>n{HkN0 1.(mwNg0m折~7߈feF sSp:u; W.cN.p%Rpi76w9eڗ|/곻'沈Ι7W)̣Je)1}ؾTp-~y?s|m:v̏ u_V*im97oaesV2sY٨wΌ*3g/K=h_\M9˶tst+Τ$u9Gcp\gAw9x܇3e1}.2_wμI%a(MypÃWvwXqgu9ssNYNQ艴؎fHfe7H0df (n[…v}a%5ß7/sD_9K.k qPS:#=1U?> 0_jy@ʇϬ|_2Bw/3uPZ K :0T+\D7.p URꦔo}oZnttt] _ uԖC ѫۈi/RntL?yK260Tq1>;0>үS/3z#vj8=&f5OJ|U)}=)u-d^W\ʙ'eV[.}uF$1GV}v>ŚcNhr8X ~8o4߉^QmSJk?1ñB36:S.m ^_0먧9?/K>a96e8*UYB{9|?Y[,m[0)@( `>Ea+8afZʼ=8&Tx.Ng!/gn2s+l_y+ aQ/˹>9'g&25rv? .;10#S:*P?3*]WU5Jt / _+XD;7CmHK||'z_%l0 _%6ҏ0|H^TϑŸL"l<|6N2TnCo Of}xqvVz@׋QWfw)vKt[2_w[{h6 Y}]=_ފ{'{23)YvDпd<`v=<ޔ ͛rs'FaƋϟx{T ;1| 8R 9GQJk:I;x?{f|k!=WTXT ə O8ez`J8S5ZC}ټĿwY_@+4(vWhw-֕myd۳lWŲl B#к+~@>2k߰_ دMavJJ~JkZ򄫴{֒w_Wi-UZL|֒_`aƿwe\3/Ur cʀ' 17OpBSܴksC9}E豪՞S K ~c|L0?{]l齠9|M9iLgXiaR:&| dg;?$RE;3}R\'^+_d,$}33b\,/m]Ysvϫ‰&n9oӝy-Gx#WЊ'gmg20pl96\Q!MM7XВܞ^~t5HxPHeMNkO==uȋMNj s~]w~fpPj%o݈N?,[wjŷ4kVV\ŷ6(ׯ ET=* ӯJ1n}sQR5kKV GrV ū?u`JAhJj(_EuK]M9\ 3JW7X<ې\/gia:nPS K,JS[tZwZy)mD|n1 7ʘ_I浴X1_Y)!wio1›Qx¢+sO?żJ?iɟؽܻ["mp?#+#{]?zL?nԇw`^o+^v`^_r1?V`~K33wNmE}2 OKSOyrpLu+0\EY{ljE-=ov-͸Ecnv-qƶ޿hl/ƶm˿hl/Nƶ+큿hl{/M)bMTM7oڿf:0OM7C:L܊=Ly`fs3y0̷Lv۽y ̷,;`63;H0s>09T0q8՝>w{`/ !gGLFVs7oӁY̒`V<>KgpzY a`;0ˀ̲wiO0˃^Sh|SL3=pځY`6xHځ ̮`ıj#o2#b~ &L1`s.}&;& {`~ fG'`a 1`~`~0>7Of"`fs`o؀s8C0Gy̑`sG8_>0,f 0`=s,0ǃ9 `s"0ݷrD8Sx̩`f0K9 ̦`~ f?0csK >0gyX~f\,?`xC?bW)MzיOZeyϐ9y9FC9쁧f4H.؆g"`"*|qf\=oOάuY/1u"Qiķwp߃sʬ莗Pxu TzBgk$gJ=_]^1 A{ pn? GFs\WG8W8Ώ8]x(9101X38͸X-p)Ϟ~/[c~gDŽ1a?3)HPNp=3oeϑG;cz9ND'>CzVsދ9N˚թ8'rif?+Ae$IzO`{﹔4EN_ AcLqEL.7vlk-h|6s,bNucy(WuI9j՞M)jMaId9ұ@Rx~|bN`y(zJ@9t^(wZJl~ z2F*f]JKg;Z e5p,#u4EN81}S QǶqw!@Rr΍Iǽ9ת :l3}_処[uz'0wgn0<8&֯|nt o\%P?ь~gE?:Zt.g>7Q&<n{3ȼq`w}73#vƍKH?.E")g?I?^cnu#}=yoqԻ׆uQumk^J‚sJv+E ǏG+%X-H_[/X_PuU%kNʬu2HDmʲifb˞+YS](tI^=/.ou+q ,F(Y vwYG<#~.X=؀}BI$ ,,璬;ҝ4Hz'c祷[JAL,Jɦdω6.Un+<0;s7JObjxYq~P'MogSGhC2!a4!(-"Aotv{4]p ' w\4ؼu6b9?a7EȿO^'?X~He[F+|ڳ_aM{!,WRϓhU24d_Tڈ^V0{ٽ\ *>Ǽ{>+$2 WE9raPq}܆]TXpt|ހr8ϯüV_kn~D&Mu`4;%4Bk0/u^R:$ML: 񿳀nĤ6ϾLd!FB,Ҫ+ʗCK||`pL`!c=UoXoslƶnۺ?BX3K4tEWY>ƒ/kɉ>[ ԯK 7W}la#qq)կ~B{^8IhoV{(_MSGݼKP8P8gÊ5{`?:˙P<Y:Y f\$fVspG8%QÉ\rVE~<7mu ?QIu}Yh2)IGͬ)KJ{$=ҞIit`Rg'IixgR>%}ҞAd?F0tqpMfh33Fw:=)Τ=vxgG/O2_<%;:' w9b\r价Jz6<#Z-^duh{cF,!wǨ__r7!KUX"̏wbO4A'Ncm~5cdH:b>q[eM'YVr{yQV|ׯXwׯʯLۣό:6QHd|F#9ɹ'3ɩvLNu0&9ɩ.LNe}KrGS7T)LAuL ʯ)vKAup` :1&јhKAg4v3gR)FԫƫtF#߫0bU0b 0`s(]< L,zL)-;COx7/[:W!ۖ&Gƒz-%zM El;hm|b QxPxo޹I;^ ,=TPK @2? ig(xo^/z2Wd~,+zq<=? /(HWd_xlgnڞˆqqTm q/^aExD>7e(7`;^YM'ϋ'b_'ɔω~u\P؟ʟTω#CP_e-iؽ_SIV O:5' v%gU~9! &i [/K;s dgwCuU #U D9̋7a_c__Ąۥ Wg>iO)'>q))|K? +|M _Q—]=d w ;4W  OƘ2ekr(!Ak ޭcklp\ek r Τpqg_m+*mPV/2wڇDľRLk1?Bcg$u3"yLx]Co@fd~w5|;˼Y2_̓e,cczq^9~=">v92熚=!-h37}x&UcƘ{E1)'\'H?C`sm?we/iq^^9Iv/Q/1ԋV?OWOuYWSC‘/h3lNb_V+7Q0bR1k3tYh 1CULPqesi,dk'0~̟j WyӘ'gB>aeV)wڗy3<+0ǃ9C%_5Ë{;+=yW “wj`BWwytJ?pzxKxȞ/{w4#L-is(оer8jUyO/Y2U9~U8n6T78`F+\{d~Ƽ-=ox?[ɼ;`x,}Վy{3ެJVSQz%{~hV`~^OSrSwՇy4z xqH>FN=lJ;&g1+^&ˆwj7DOhy[:WO{3rR2!Yy&99?U oPx=#BL Q8Z 7fgu#{gH6ߛWd6.C{[A j57S0iv.k|[Sw22Ѳ6Jas?uw=^/#s=C'unsll/)8UGa@,8Y0'v6s&> !"sL%x{0)p"a] Om7Z8glk]$sEYWd/GǴޒ|/c8Mw#{lWqj},wW8wݓ9N7!/I[K|Js ۯ9|7_+;,( k:g) e=vQv5i􌿅_~KCiN!}-H} h}_[1W )\P W8yΣpY(\ZR G+%.pq8 >`_8}>[,!_ a:} lf?Qkr*?˙lqv>?{6PwwVy^fMpJIʼ9ypNdX7;oD>?l QZ5v?fr+G'TѾ2Q3)f x)'X׷wZ$wrW)杵ҫLs Ϋ̱z&;slk?o+.v.=}6͑lfsY ^8H|[m܋quh-&|7!‘I&ȧX%?qzRow mrc3{%t'k`ZuwtμG YpJ|2^wr.=ߓd9C寮rx&-;iMv OrLϫ [,V>Rα{Bp$L +HQX$+L8*.c^R+h)`~c^JMQXu\+S.:3? Q-w)~wJǹY .Lb"n"N+BV,B6*Bv(B+BrtjxJqEx?Ղ"f?%sW1g'sN0ZJE(E4`Fczf10QA54AA#4AA{4AA4AȢ?ӊRu>xw1~qj@?7F3 =+ sOTd'.ƲcY-^B.7{OF}~ U2n qȆ=9~|> [ 8ub'a ^Y/|D@>g ?G:Aąȯ]2ƼNN4Ǽ4!X wIY{孌pJ[rx݌c ۫)΢͹P<,Po1؅7|I⹮ȯj_OŋXW1(Xҧ&̉߫EcJ(}p|_1(nc2Ⱦ3 p8~u%(hߑ(ڬ;f%ͽ1`Dntۓp|w RywǍ8_acדe0QQyf:/ ~4p^NY&q{]d*}^Dhx 7?JL^~>c s{qnK,-2{ ڤgZlKԢ1&\T4 U8\6#17ƹhjm0`vQ%XM+`Q8&U"FE<^\Ch8/ghk\Nhw$\<ᰤ@aYRUuh`@hsZ?T@+*Nϖ!+RzRj_RIHM˲)#[We)ʿ`2u7SurM:QS;UOXe SEZq^ _1C*n Wp讍߈f~2?>=O@$w|5NdYw`Q8JYS9>9M۽Y[T~h>UѬa&T/ZaX˜ѺRѴٿJ/F{v<`sDhwc]gZnqdNz^3e '`DoU aZCX `?0̱_JT6<0PV=hkG%J_*=rQ̹Wxw=&"JMVy:Ֆx/)JUݧ+\N= Qx± =hӿx*{'U}w뭱':DqSZ'L\/LI^eȻr>hJYX>Ypl s )nqlޡ?VI6d-f>r={ޤ2O/uT7hn%623@?}=,c~&olE}/;ίQr cmEe)G҇2[J+c`Nm7V sjS;ʻozMZJ)wެ gS-O+U*>BC*ԆBRUg+aC}EOW^ØWxT1mͼix|3oZX/oo+ط=N]۬%Q-'c agJjOhU&JWEP{=%+W OWx‡[DWl$rye)ƪ&=3\j& q+31ۻJAGګb*=Ggm<;htt6;2GxXu߄ɸ[|&bJg|A=b_puV=X9k٣~CUe\w\q 0'o~ Mv߈[y߂ު2 ;&}T8"E V.lq~wg:Mü]׼2 Ɵo혷DWwyVIϼ!<dUFׂ6u5b=)=GTz2W!'NޱC+]ɟZ>6gWvvԦs(e{{3;kӼ,õi*ڴ_s6Enu `S9W|A5 *p;^CI͜祽K8Ư^^: 2WNq8%{ ,>wkv7FY\GEw:t w 4q>uK &F:vjs(m{yvvU!Ѭ{Z. oZeK*o`nf-Cw|}yurBtywT]*3I=]3.Ja{2^ñR]sZ/Um/ckUTKu7=QdJs9au)΄,E(xR61/gr]s<3Jmx$`eכ`t鵎TBY"Ny__D+b:r>=PORnz]PSlz 1~μz)mb|ϴ₿udu N+ 'df 9aZZIk!ʡefSW*X}v.cz!D8r8 Oj{R?tCVOo ' Ocsr8j됞IM]8c,~0g-ؿc {k}Su?r<[avBo{%fY$m4Bk|/Z`O;˾6ǽT (^nr9λ`:wSg r_PkS )^nË@ʂlWp+1,7PW9}~ؽG6s1 xK?Z0`q9.l` ~SzܳY$2Üt^=K].h@VK?'hh9S]1ױL[>l*8c =)U^T:6bv042Jg)Q+N昳a\σ{pv7CIS~|Y6)g rV wUUP^>{~_9mh/c 3!O>UDSe[Y{ :!1kִ`%\smVj|%|rur,c<}Ctt;I0ݶ5 eҰJRHȄ-K#6y8{ [2r?[Y~_aI74+wJ݈,w\Md^; piٽrO{g62w>${4~z15zxm(]^EΫpwYavlLaݥ}}_,Gk_7&y;?a~V~<c⡽}ygJOwu9g%Q՟`fn;)he'Hf 傿1)n֫2A}CDz&%*­dv)3( ?X9uoҷ=ͩ=0k^|w <V|U%TDg ߯8ͩa8h?+X=Ų=أF 4,I8Y/ As$ծT&Qk81nPD 2NX=ίu[ia?JxC*[:_Z-xb+>## -h}a Z-Rt^&]\ Ad*3 k -)1JCtHHN`,wjc[R[3׉*Wwf:n;[{J 햭îz1"j0 G82ol1q C+swVo[o20,oe_ʬxnqzӾٴ:”ӾiOB#'|Oi|P㱼?y&G8EbƜj|N"'os*WrMU+ o 'SalιʜHnz1qګᮉonW5ٽ=7JמÔ) p05uś{TvXsIrEa^{/e^&1'n{dZ1N h$ a~ F7 5sل]dR6rB 7x087 >}g,G~~ߙU(@pǐ(_o];}rEwk{9>}4L0=c geK2/q17+}tƚW<.t^θ;W.9旛 HTkŅcƭ=f62u{L)sYw}'Ok1lg5jmA78Y3wnMw TvQwIkH3'4pbpyn| D`|Vrׂ]m=q~Kq*?/9^$Mwgϟq9gߐ~-`OxP6>}z8㖼X 8O/sc+ zm"ǻJ1.:/$G#I[̣mLҬM`|}67$W> KJ:_ U,y-|c=Wmh%w g qmLxf)c Л؞mc6&pѯ#JS_̟|CQ|a7;%',k]yTٿ/̬)W+[(q4NYiS)cb7cv omk~"!'~37KE?͢>|Q)+cmgڇgf;3p~1݈c\ K{Ѭ+2=xkwxC;דqAI;S11WR2fx)ۙ8 jlW+E8V1c}u+gos;7ѓx1=-/v)~d)_S3yLTYYtIݞq? a~cy~]9/koK/ȝucz@^z}"(7X>X3Nm_g,to{.9wARDP(}Q0U/L[ &F=;9:/y/mFrw\i|Ȳ=%9,{1ߧPRW7o^q7#às]3CqA }.+;ެ_^molެWh'`ߝa8M |Vtʞ_?lO{(<f=yNs x  `?.!;ஜnK)z0<5Kcfҩc̻Keϰ|}+n Wt͒Dר_4y ~qѠ׆?m^v뾔L++ |˦9\oiu]^ y!M7#Rw%I@n`yoSOsfe%;s~OӮj׊I`nj0^8I1 ⡀/t <,fL{b[x?UM&HWÐ1 ovrϭGs>nX&t4;Iy,:1tu_K~ͽ'knee1$iWpکiuߥ;'Kˎ!F=2OG8OG{_Mqڰr_jӧ/e룬3- g}yt=~XE`M<> ѯ xrdGWmo"A#t? ~>\7πiLzҎ>۾i1Fh΀ xr+9? <{H=hKv=4Vlx̿I@.'L Lgo=.b j{ȇ`?W nxbi x6̈́no!c˰I7oѮigVVd\곘ȩ1"nȊ)h\n;V֨+`w-{kXp:S%ϟ.:{wc6ΟNxgګO֙Ɛov{ w6s%t{""E} a٬1zZ_:=9X9y?p,sp,1͝32X|ַ%~fWC=]cxWy>OA}x(<92_bOVYQߛy~0Qz8u1t]L{voo][ջoAaixnpox̉[nKuM$:{lkvq 'xn^R .|+์e &Zm36:|4KYZk AGMl4OEѧyS$Ciq9qC{|M{&&~ sg>?BcobNp)Ԙ:;ĥPWU)#19HfL19c38Vj%=J(+RTy2SWGN8wiS_J3߀$qV3f-(6ODO.HSqNr4A{p~%BzjC؃4t#lJla=>zls6l \:G@yb~(ߘ=q\ ~}sq~WAگ3Ccx &xLFv@>$'E>7e p~H^|lx0#R1v8%1#?"<p6U1?9N% pG50:1^@ 4uPk!7h{C9}>t>;E_Gp_:Ѫ/ݣ~k^:1o}LoO|ZM@9 +CҤ'&5>vÕ{lAw;|OyRļ3S֛c3j ~F^|<ڽ p=&9Eu.gU8~W^OG|gmUs].4y䉞~ܷ(,gv1 vdJ~:o)}vO$oS'0`o$)W2FJz>yq_V=*oz؜T:+ũR2Wss|vr6f,~̬AȹH]uͷ~{qߟ9ϩJ9Nc?7+~No.6l : z F ~N G?EUӦ:\3y~4ٷyw=.3R }ջq9ߋ93I 抡gt ?kPZ|C~14CochC14<Cϛ1hr7@φhi??@ h`?7TNo+i>@~{ ]ہ^1{K <gR>fYv@so\ 4god}4/>hޚ?\SfpP;(ܺ&yG%oE+#<+p#_{{~w{ԛ9;\`_O}ӌ߿ց>4E%OL-pQUZ||J) Pm%ߊʺS/*]f̯`%ol~WNaSؽ*pg|a6dV܎܁u~J]8_pn(_NӰ]g;.lUTk>Oޱ}jeѽsI9K_۝Aqwݶ{z' ыeQ}_ꝚFO=Qf/t=TvP .K8"lp5½PȵAǬ8'j`6aE=N(iP+}{+A-C?PUQqO?@+o=/R̼!HbPK)T9'~d(_wyo+u2'B e-reP-w}}jbY˼i8`d,F\d_ʴr3=b6C$V>(o`wYҨ̗_ -{~T28X}0}'OfAӧi-O;ӏq>݀19À9e?ۨ9Ye&D=ӱAT|)2zz>>\~ܢwjEY'%Vz1[<'\(дw`s8%/ ʽaMETYIo{m$7Yc~7G|8ˇY3Y _Q|{*=X>{-yǼ6/e>.|iogG1f,ucFi"~R&/xL+s;EFm WaK:2[2~ǁQ4V?z#FQ(ZEmQF{ 8M 6&׾v8״~kZ߫~M u`5=g_Sy&tiD0x,0`n}0#`B9_`]lhh4u) fZ^)ލFS?M90f4hJ))揦>uhJmihJsiєN1ϑt շ7>G1ԟUC` 'm>G11` sCp sC>ǁ1GC[K7H~Ǥ cƯ+1T$c!.g*cܨ%;0{%q£h4^s+~'a->&p $y p>-Zas<[㺎>rM*]7r*l gѷΩQQ{vv%=_N7ފ Mٯ10G6Q9qT3c}?έn98{Q986%G.b<ջeO.xwSk5]4~*ү$a >s|l8{ 30*_14@ SQ6%,SYTQw\M]ezHVU}~1nɗy;>|M. O ^-ƧQ@ƿ^B}){%zVx?29>vol+K 7 7'kz8nʄKÿ{2593Ơz Z5Tٽ=˘^=ZOl:5הRh-.vlH}>+,*.{+$ǛDV:d`woge~O+Do%vewчј`_M>b~_+='s"ih;|Xc+=.aާV~Lwg!;żQ2I'c)\6v>Cj4:W֑yCb"#,XFY<' #2w8Ciw)\Cd.9{̛Kx0o2L1*;ox[)00_7U(ao2,|{\}v?7E?fޜN%c_7GsG4o)Liepœ{,(}-!2}eʹU?w?bO_R>f9+?I_Iowx+ю$ F_Q:rYeJdluyGC𞰾owk/ q/R7k3(2wxSUxVY~GXvOtba=ųS=/_pb7a̻M؛M ߲]ܧVK<3_ݶ)vxyAݵI2|y421gD]i6+Z^͠Oy$c$3Hebq*\WaO+[wţ?Qn8iT¿y TqŐT<xN=ӄ'燭C:噗؊k }' |7z|yo[#uݳCuV su&sJ7u;lR;7`_f5V`~^+m~cʻgZZ^!ʺ͗y0+ r_v g~|y%-^9>M(xhCh84Fr |MYU)mWJCuY@+W`~c1:XLڞ,_ɒY%ϳz.[#WLevyլ.ؽo[#\sen)*N^Y0ӝ4Ңpe: %7\ -Z<3)+J[{1O3%/Yi+u1أL<#̻GVY5lnfgV150-h$Xr}FgYKJa;S7$7Вw.~ro +>}Of- x% yMҖyC}s+үʝ>}Y~}U#}üV&+kK6{7mLDpϼxGt2'Zw6v 3߄ BÒ'׹ahc޷ t QX^EveQ]*ě3"ˋ2;`=G8` [ybv2 i歱X6ym22ZH +k-Gn=v<lB]_ƾt71pLuQpϜv6x\u+w 7N-q8a02,&$<)X;z~f,U9bŤ#btZL:_L:,&Xnjtfbb:3b:tq1Uz*ZBg7YBg/h*XBmK%tV)v U_5߄{`N;O/f )^.;i _ hߠK 3Ks99l-,]nmf,s7Pۄgg {6h"8bj۔qKSLjC'&%|a/9;<S<p|*Q}љ,]c)mx>±XP'yCֽV) ϯ.7 / Ǚebxo+ _aQ¿Eu#!ߎ1na5#>ϯb@ؽrYCE;=gwٽ_`*Je%2^te2'`X nb6,_uu{dVXќ74Lܥu̯RS8x*3/swGjԟ}5>+}zZzL!_pFGlw)zen};hyv\M;Nk Uv W5܌R^1M +0ie+ 9Vw/^ ,D4e,eZ-3'I#l\E69CXU$֟`^++oy἗n>L=JYh1Gٙ)*?&/tʻ. xa_]c6uS67o5zX3}S(v܇!$y\c}TIk$O/'߇q WQz!Raϕ^>g*]V:’'uxku{w?7T=n8cn0{?.2BљAju*uU^=]>a1Ob ɸ^t25_yURe)U~5ͼrg@η-ф*Px~~Qxò {'VNgběW~eCƸo WE_7rqoJΛ@{$9hoJyj\&Zm : {uS߂A *SӚ{_F~#pMwwۄf]fV4vՉBݢb?YF~Gb0楰} 8v+1gqݷ&pm鉿&` c܇Syql3 J Mg&=Vfh*$߼w~~p2GS2<|⥲G~p6a 㵀[1=ތw ጏἚYt۴1-bY o`]xCix[[lOv+'>G_D#}j[O+7Ey&{WqHQh(FC>'eg&LM%M|Ji 3/!Teo?T2'z*i簱=q{k`3ͅKM0~73τ~c>SΈ8s3RyܶZ,CIY.]I{+\F&P|9rhr#-R%_0?3(Jַ`rYYe5@vkř<\@s|m;? Lov7B9d]n3MXOw_ 8޼[x>}nK|~z[/hȼ0:ݕyEr O?Uiv/ q)[ ~K" W2W^ܧn5ixktó\#hM=Q<'~鲗媃1d4i:Bɞc~-ռVK͇W>-͜K+EةZݗhn6KYa33yG~cWG; 8(;8m5?vR/~cz8E'mq@W'W>>'^B|⼴KV)hmX֑'{Tknʿ 7P?mjP^i";6A3鿗PvPqr98IHHH~aNf2?\Q)|.>'h;G;j ;|)S߈0Q<E~P#p%Cј.ࡍhL#1q|()C #x5lDcG51' OnDs6c8[;4ٰ*Wwϣ:y'IuNڧﻓIs<1971-Cz: `:d~7iN*KүedN}j1qKQ;Ӽ/#凜vTX*[>VO{?*sy1AB ~j 'iF_~;ųEp;2HZfq*cߢ|iw9oLwBb\Ԡ3PNoD`C[r$jw<仨_9ܛ~,.:Ìa(Pu- *_[]Mb;9/'M| Y}>} ?u~)ܶcqX[pv}J O8HxY&arҶ yWvQ}ge8sf62M,BoNMkb}XkQu7^jGو;<>cۨsMyXְB%[먚&Neijn{f-{pW˻t?2=eƓppWwd`[ rb1J}< Kߗ15{ָu2^:[`/+z/+eWd/y|fz/տ.{לs1f9"{|iί^| H{i aGHxUqaqkΝ ۙZ]Xxcp(> +^q0^:'D)/2"\TͨÐ!3cY 'KEz]1XR"~HX4S精Q}-?f}܏/.wf~$CZ#sonٻxʼKVy``*ڽOTـIxU=w96#з#}X..sek^Ua ƛ<_T,{:Ƽty8|zҨxzPޖ;rX.’|{#Ϲ՞^Woy]畱?ӸSڊ,I'\şa[Vy~j(r4Veyy =rwSQNuG-^oxDNH *,q~y;$ ;f^˜o֘bFu\c8d4!P9|G[ļ>۬d(SWc$忸K8^aV}^h<]w }~ܬ5.QO|  /`'xd_xf{nKn2Swu&՞:yp,`}Q')'?^_u^=^G2JuoKIwIqo˦KZWOS(?v?rn{pdm&Q+7<Je1ˤx҇g 5x8GGbe >,KI']/w:|KQ; Wa _P?ұK!+߰e59e廒3Ɯ = ^aRtS[\^ȡ Va]nv%xG0y62^>#~K;MOpFOeȝhv ~D\g5^=4tڌgngimi 3?.,so2ia= +:tQ3v<1qgr88oЗ1K2sBxMy}MLK.a̋ghgR~8wxߧ kB9'̿{\37Wj!_IV{ja*.GE){/5g~Ƹ&|Cٳag'>K$+Y#_Zgʞwc p0ů!, tV$ٵYɗ_/#͹@$"'Ƽ _Pak""rYT9t҄Y9/~ 'f<߈YA>A=e\üt'MJXS{+E(C~Sjü̫]a>?Ϯ޷re sxF>,xc&#DύU p?7 ٣{ C}[g>}M?h>oJ>c43Wުu|]|9~gH Dȹ)p^s \4`ÖB?.X 5(c*:*R p+\ le\^?{rL޿rZmȭbi"^;<%+fD |S^?ǶLzɌ(ܻ0e{xvp곺N[^z&7 s\nV9ˤ:?_ck3x-Ti<_ 5Yn9r?'3}֫?_yVE- k+*ͫϼdj'`&s:]bUW<+fUs|Cg3Us?W}5MN?bٯ=95;h_ktvktv5:?ݏl|ܯ\'n}у:XeN31B>P"RɢOo fY7_wqu} y:S12~3:'aⲟsqNw! !F S OeSSc: OnIsIL~G]sF} >1Y €cLwOJ}6~ _rx}[xC g*WMj<ǭW͚JMn R0AWYxCV;،7)<&Ƥy&Mo~N7)ry7M4aXPM-7MJt; xEI]f>O(gC*W6*kN# "ˎx]svϫܓ)/=rDz v-YKq>dJɹXK|mXrE97Ʊ{qpγR^*kLx'g^SËTq2R5ko5kYwM[4ӿKZ^J{qoUxob*n;WSIy||}rU?LN2? eN(A\v=_DCzd+]>U^i%1kf \϶DL$-eD5Ⱥ$De-Hpw'Sz'}Bk ˺c_If-@o..W>$nvhe/(϶nk57sT~ro{ ?X=A6Nlʚr|ox@v|t9-tf*g'k&-RQq:p>01#8.rZg <9:ӏZ|4Xk(є2lJkoН8o=|஫o3F>4cd>Aj)8 &ҽCRLn懴Ga8QyZ,7V p Cz_,qsuyhIxHdC*'R9‡+e7Af509L&^\ Z`6lP0s=fOL[0'y'` &ޓ_Z p'|;klD=2e##KjVeNq$a#z$PY䯫w1=0'͛s{=Vk߃1G-GTF>=)?55~/곦Ƨ+KP~ 77i;GvX-gkK@ܧ6ˍPʅ`(?V(o‚S?gxg~90LT|8n\76~gէz|TQUO\ޙ4S,n9,_+?ڷcܛXcx>g`z3bX$f~`^ ѧߕ;k z~Ƀ&,7ߪ3:<},oLw3|W[b fw1ŗ$=f?)ˁqZQ/3Ƴ'S893*TWQ}`IMÆpĒ9%OYe1Lxs*>/̻"[V1.gvU~أ)B8:a_Wp=C5M_vou}rf('rG`تqyOay@Ex1~/cuWm( IP'0}h31$Xe0 ױ6(/JE2/E]g^aZJ>>`/x2>`yQ}řPVWyCڂ{,x&cơ΃JF0/y3P~!u֢5]`g@WO/Gk{Q|C!mW{rK<S޺~z HE1h_@}[cbB,?WS~J,jgCmz[ᑱWF`~ pJr YgHCK?ٳ680if}yKq3?=VA{k0,V[ } J&0㼊 8OpԆ_wKz?< K|J6[qJY[" Ya hY=޹ -_{(yTBJ ig#=Ry{~OBIh M_0xzM?:VuX75gǼ1'(\)[Mq?QR8ީM'6_xC;> a??ooc=wtk.y2dg !JJ4*薱")EȬ*k=^g;WclKNT}*/_)"}k( /:F'UvS?v8GY*{vnfo$?AuU&WdH?+$[a$G\q[Rs& מM9UI<ʟ35'OMʭؕH^MC0Ene:oQw2 rYڋ^B~p=;<3uMo :+@:d,rT1b׏f 7k'WӞqB?_Bx0)18=3%n'6l[ʆ5D6;%8&_OL WaŸ)V5 ' j?RCRx[ެ&7*A/B4?wnq_~:O>;;?IvIp1g!CV?gu>:XWY;~G|,`_&0}#āGKmy_aSK]+ ".w>!JB>d!_\$!~=/r!:= 9Uiu r~i3h%h=0o>9}U/tΎ~E|{7G_xv猿=AW{~ZxފpsO~7vtV<|0lCy>"i"܍sٹ?.f W}O( zGE0tG~sO_lc[B[~63jj/;G"@ɟY+gqoΟH[}3`tOvu37_{s0OC0gM};:D?wI?]:# Vw~Wq-Ͼnr#i#-gwz|G>T! Ux£} TxdTx ߯xYrGAb𵗸8;aLL_8{e 79]WΘL7,{*a"ve//gG;CϷǥ/.סT=g"pA?Q{.Z!w.͇2T;$DadCA=ELrgM r6 ~3XqL <rr9-`.oqq|r ]wq,?DFbz[~(˗3bScs}cd@w 5O{oS!2o̼B!x$dmg|Xo{H;,$dlϦȦs':s1|9ץ(r@Nኜԏ]+ɬܓd|.EoIFғ=y~% ^~'n T|~H>wJs%<7WX*;’rJ9Vs$n|a!3ȝ]N8<xk ^YkSbsݏRĊs*Ne WP8GP[aJ嗵ᰵ:̋ NB-)l2Hk^wߛZ,#({y* ‹$M(߲ᩯr8y=?CAߞxF}G[שla\Sp{(< ElܽZĆgc 1O^o{;U7h["qNIރ/q':9q"q\#'5y+=lFoO*@ݜ2/q<{S'Jy^~/ S&}W1=9a^ ! EMR= \Aً=_=J'w[;)f/bExea>URU^bt9Uggnן[7: W$6D7XA WxC'i-iؔ!< &%|4䟏4rUQs\Y{xI*Zu+}M!_{Wq[@Y,K}߹{Kՠt Y"+zVWFD_2Y :xT|}_?C>NϽgR;9Àtɻ.ʳ˿%ꇴ~L2/'ROr|/>'w~HǎHeQ<_2=8p˶5k[59!6pYGύJuk](ǖ8\qegPz m2 )Ԃܮ*}E|kwdOɣ#'._G{i|pJ| _rh;ϯowGHYê.~.e捏{蟔ġ;86 4U$w-XiY r"āvW>gH5pu"yQ:>mꉄ/Þ鄯6 %7-C1)f?їR̜6`݄R}8a\~0rQ)ކ|Mؔ[?Ti{&S6@{ /=m )S;T2,eLlax5LJ2?UxX=¯<]miz=2LUx)0mK""xe{1 ( %M43ߎB.^O5`oҥ"qp'?LrS"^֌K9)ArĽTw*~|`#)n>mg>eE΢˕}2pro)O -ډ+n֕߱){ J)_9#μGġ|6E*~|;XKy> ܵV |M^^m|;:Zdꩰhx%M}\oW8Xۡݹ.* ޽Ǻ̚[95Uq{;r˙7kUΌú@%Ux$7nxmIf’N=C:(gv?cɏw0];A* #Z-Y=)#qM7$|Nz^,;n_2 T厧~D(߽Cy3='ƩD`q>$ %m-Tuh,oT}pVI|I~gRE?up?]M ^1+WޗPNVᩪxҧ_͡!WS (|s~MWQ|7]V0aov"I9_`{oxUr^8^hlh8;f4[*8-MNgs]A8$sϤ|zQ:s*XɚOn9tzĊ8KNGel)z@ ݷ# {wcr?Ϊ ~~tKG^DN1}/MznM/ءA~]~G!wrRTle{5{gɷml ?y^S5n<'ǔ<[{叜$OQJ~3^p ?d]SqyK+Ŷ3y?w&Os@wX5q\3CƆo-2bHoRx’7_~{:qAaIU8;gc??xsOI|eWxl.~'x-zc~X}6^U}~;.9 B*ܗđjfJjF]%^Un瑂f\n?A SUpӱI9ǃWJѼp/C/U {ܦoA<0]/!nfbO+K3 jv%iGoeL1!~8LLT7s<=s[~8ОDŽOM'֢waEEQ@bX{6/xj6Y5N Wj<}4}%?wAwxܳ!} i>n?_"ζ٭Ul>z*8zYoMk8+^ /UxGkro2#+CM4]25o,_RWs\ ^ҽuSYS^/i={ 浵U5m'{eû@զ{9bxVe?^fu&w?j0̫N~{Y竰YƐoY߃Qߗ|uO-gtRńSqNΤ֬d 6^6O 1Tھ-Y'ndev>XW"0#5'ڦ.Y^π7X mh1of}[}9ZVF5Efx9uܗ.CMt=MsrF _X/<uFx3+P]oSϦ]?:eRcF)4uN[S Գm T?`>_3/(KWr#)4Vڻ{uO6PxNm;_S( x}#P@˖w9m$=P߸vu;C K|dSiWZS_πp?@~Rꇆ#vCI\o)=PzuB<~Să?N~Gaa􊞯?M8[vBjSnG.xn+=:ҍw>7}|Q⏟|^x<[70ulg_|O\oGeh8xN#u()zB%,UXT (|@s 7p %OC m]󇛮nO\\_gUO@sϿ/{_ h]P Hjhܲv®,`Y;r)ga?eWa׼ihzQ7y~ξ .r;zR_g}/[?Jӿk|?yQ>Y8(>ד|_*'_ Z]{IYY|S|Y]Up.?F"x<.2xI悒_llCֆ|)7X^Bȷ;W&B_Oơ-bйwDzQ5-+nQʟ;π_o0X8{\u+~^$*eKcǢ1UT7{Sh2|?i OSҿ5ywtegy=$= &RpِBaLa{*ڐ6¿紙y_r8,u. ԽܳzYߋ2'BGr@7T8.ŕO8q*-;"][<;ޚT)½v}C&ƽr6e{'r/p=ŭ璆}T)WW&vL¿t4|r}EUٜ&F~1Uz׿WO}a}7؉yTһp광^ zXﰊv}. MM@SE2eZ 7Sss`{}?ܹ}Cyn6 ěMow̗x|[̱|IOgf cϷ?S{*ij`浞MFL{=~mpe(pf^4{8"5w.iN8ŮR͌]3Ƚz3 V8UᗚpPs~MOÑٱv/kkF,of||ր ffFfv?^-q]VgG< yqcqseQp?^gpT~%?f;(2憿r4*Pϑ5mC{w޾͝5h#blhH*'#~#M=%͚$~~?xv?]۽Jao&L9ӻܬ%{7j_b? zK0zk0zyS _x*kqRM&\4Ҿ7^kA/{rmC~;G4;}eww읕2rgWjC[^`k񈻧jr)-M=: _*~g4w {r;pr<${COmpGi79 -#7S릷L?CP}3)l*= ٹӣ89-^⻔q 5% |i;rѝy7J)ޡj^]jC~6ޥyϻ /Mw߮]q;GpkYuF)?7;Kh|Z+<9ml +g}v9 S=0Ƅ;~ngҿ'~+q^!rޝ'u #Xf™Dܗ^Fgܥ| {>faUw'Ksk:C:i_3wq[5l?]USe?½rEη^T0<}VA-۴7&]_='|D{<πU;YpoHw|S7(=_|#o~HؤI ^a^s9dfsz) pfşRi}f?MPg^G89ESz8ϟ!7.U)LF:ϰx Xq}uuhe^:ΎvW)|ű=U޳m_&/Ry՗&<˶KAgt\l!̜- ig2݈ܗV:gtVm@٦L:KN=*.}IcK6_לZtWxa8XĻpݨEcxxt1L3Go3GdW_jj?# M'AOf̧'G+#[Г7w5{L~gVzj̺y6z6K3'=ғm}cΘAOz ;#KHg߱۬t$b7m;9.ٵwx܍3'] QyЬhO7s֘ܪz~A\f+:ŇUݞ;+6>t;}w搯MnSe߫{;y^ Oxޚ~vu``yeǪ<}/|p^$F$z'D6Zl{3 n#e?weٽL7C-eP{>^y/ӇCeP2}3LR/Ӈ6}"qalt9\v%^ަ{!]{۽k |]{1ߠn1Xm{{0`OD?,+)~=j7od !2q8A~K[B:nL+O##G7qY}wBe:NeOP8 W8p5MQ oUh5L\d( r7@rCn=陷|#yf(id}W"?rIi+,!xwoHڰ2i3Vc9yFfe\)sJ{.Row9[_\=O^/_1i ܫkvy N|˽nY~mU5_@zOfqYs7ffTB~*c, 1cn8n:T3J:PՍwqpoj|e׏Q<r]9dnKCJoydO h:zE[69u˻Nv% ~6/Dq.mN=ۀ?;.k)o)c߻ܦ %\'*a* WVP| "'%TS~JWPM 1/+iEp wWR^gԑ=ow>pr3 W~&iK%;䩃D:(t-.8e5.(:+(OWx}k,r-X._O N`'9-/￉lT~s}+]m\t󢟁5~߫00*~'~ƿ; (K*mM 7S8E ?  U8P+|_~QR>Y\@ U1Μkg^h;q%q+ȝTu-[=?MJvz?_ٲ,]R\O!ۢt };I#q?6h]ե: [+E ,!\E#pu\Mڇ{Tau^]O7^\z{_w+|J84ȣpI+*,6TI=68?\dcGcz?{ O0ux.|6/H:{ Oώ1cxIG`7?T22Cx blX'{:+K 6jyIR g4,@6]oc{KF #XzH(&bR1\te[yVRJՋe+wke|F=;{QTS| GQf/R xݔ_i篅䞪'?l8f٢r<~o츼CeQ7=M:L2w0O7ooeNG y~*r?&\+>Ic G~?`U{S! hsF!3cMFۻ{s\OxZBQ-ڬ_qدX7^W6wSH`uedx?ɉ>#{).o>x>o+Ms IKW?k&HI#m뷐kO{LnC?8|\D8=ďk ?/L:qu[w繘yC&dNO7{4[ |a:X D+dg֌{ƼN|?-|—_+#'I0K=KaM ?6cw]K9r?֛xx`\aςW#N#Nx&,{8;Ƭ<gG(CnjG0lc(/=۳ yY=7`{>'=NIG`N# ?7i`9=ᤱ2uy$uyf `޿m,%<8Hϳt;\xOo~׿3>f*cƒ4^Y6U~RX;<ó/L<\dR⼽1fMOMkG^0/] |`i O< eb=84Kz&syɇ:9N[,@~_00R-#pJ);ŀy% cCoa'8闝2|asm<. y q~d`Nfpqfo~xv80==ybəCdZ_o=轼bO!\EǡͰ/Ip  T± NS}>Ig7Eoom'7qX?ٖ޷l1YUЖR`¯)H^Bݕ1Qè]a5/xJq(J/ *~K*\_v O7j~J=}!Fx5U Px)|D g~ WUdWEy 4^b" 61ge-I[ eEx0WL}$wRaP_$o?@LL[%]po_c_mi?~=k~{5vT`㵕ޑT) i"eM&5`1p< c!O:}W&9;5|7.|~J:YS=*|=;x/_ք?N|'{zN4e#9et :o|bKE M_؁q,gYINh=h=OO4L4y&$7$Ӷ%yW+I=n,R?SsG+c^By˻ċ{ڂO90/,whJު M{1W/6E=xN$޸`RaxWx_*u,s?+G*>&72<^Rwȣqg!ϔ!xh>M7p=p&/Wll[0>5s_; g2_1${|ewI&,QF__¼ga]kq~}qLpgyp8gӡq?Ycyͤd?1i}15;(^~'`c%`7Pͦ_3񿳙d-adS5ٶ)道Z[_vM2m)ڪ,q/5ø*L1$:^EU1olec}mt69*ӚʼoOuMgOubRsw)|La8Kr R5Ёg䆓\yWmѷSx oq) ߀JFw+|ljlc, o?> O}ئ_84Nᶗm\ ;[D:Ǵ[T8\sq\q*sWӌĽT6*O͕]/-2;7h%_Xsqi|+s߲aLp*YOZ[/q=v#-t~$3Yi4ȢqҹD{wlT} HSx_(N)?+BO1͔1HN.so%3YtPr}o>)} q߫<pʟz:mW w_gio5 QX _I_#n&93wOߖVQq)>oLe:C(nN3On׊g<4{az| d)ٵJ[>_fY;}5yn֢YOͬf Byr`H?:(3:vOgoᗕęVgVg6idTil2U—ե!K-SS P§> %{ۯ>Mo7Wu_\}{KT<ϳR~!1LIVghsMB}ʺ[A>#/rrs>_9iCړEؾL~sWI*xov߃k[H|c=w|lt:%y+|MZaz7kc^/NDwMn9+>/c|NZA6Z=%Q˦+j 2~S8?۞gĕwa)я̏,q߬3߉̿BolcQ(̊+ pGM'=jQx\cãa{] m$6rcf^oI9Gy/H W51>f=G혟"H8,=b[SU8q#{ĄSS+@{/|Ԗ>vHW+>/*1 )\^z R0r|d⚟|RY ު5v?>3ʻ}~v1^Kzޫp-W;;`Y2(ʪ_>XBx p`Q錾Mp屟N*^ߔ~~&2.} kx3iyfnC-sy{)3o)Ue[ 8v й >9 o{!%, ;_<؆ʆ- h]$]$?5l pjB@+wuˎ-w})לr޲0q-3.m]47i{ `>ƛH 6)|80ϺMtOXKa>%!;_'l<4zI並O׉n ܷ:?݋|m`b?Sxm &x´I7nPםG{bv:}͓vWwlYyظ[ݷm x=+[S\wx$ن1zm vu$ex(~6"S4w^'m:勇N_[3? ێ3;Y{+IV&%M4*̳}<>S-lɗ|FALo;ȝwlCsUMnf߃]Pyz rn!7i^-ezJF莂n pOoɝ};3s:W!*)M\|`k3?s>ex/xu#a$7MᧁSKmΣ{2 kB۟~eQǞS%nteoe.pk)bw7~os0A~?d+/ѫG^1|۾|C|۾\ROA(˰!z+HgKX4q2_NC[~{X WA齙=enSMN#|g1NLOqe,{j6s턓II[9 ğ(@P k:>Oqowޢ{¿~/: (9(ڎ;Su7^g'|#rO$]W"Ƕw;=Ħ/z׀K>/_Xh;^TĎH: / 6`=|e=QxN3x//Fq!fQh?Lw/ #<>P|w >os<<q&r앛 <-?- D'6wg?+Ey[{{/d_T٤痃wWx_X¯xL .쏹rw:a$WR_K?j_ƱKҷ5\}~|G}<:_%B~dާk>~[76NVq05#;H_^K׎ %z{wf6}ߒפ r -r$xr7] ޽CB/gŞ/6:Pgy%H4Nw5\K>_GZ+ՍV}UijwKŸ*pΗ-.pp(ޥQ*q'(\U VxuvI r#)W::x?43H~rk: U'_*$>{F''pA-1Rٓ-}Hw.h}S"5=J=O>'Lb_0pxMr~Ny8syeUaab W>flޒ} r-Me)јe#oCzCL<&wp\SWy<JR4h#8>:?ܟToS^n5^-jK/Y zr:d+f^W̹{xW|)7qކ3A2C^XB{&rURkjh=#$K~|G׭2֜N ȽNC.ɼ{˩Ky6Km˦Ǜ;⾆;ʋ 8fYY_3UNX|\~Q?tR5^7E&88cYt9oo[_Okeְ3&|WJRVֽAT9/Ux%pΡn@:w993mvQtF)<V,Z|Hr_\1~_ukkC=nX%[Sչr^"{}S/wd xzCVwyB~?ݏ>YTp~d/|M=P G9+fy^neYBA\IW~e|ݴoҼлm?Ĝ/\qdORq΢&___7ɷgM#k iJ'GpeNlYol&:%XwG{S%WfIkL'|2{nH۳, lBpYۭp~ï;o"+|yN߮?a:T\ 0Ǐ!|C%4//3w6(0a^TTzm.L|Yԏ9Z4K%.hOL,o'D^wm *UJc~SU!\E*+7?m)Zaʬ[筰#^\aGHQ2Kx?f~h?B wif<Ό6>*wZJr-^IHzKA-V^/ps { z++Ung$J%y详W<)٭|U ÿ9oί ~](7Og$jCWS_sp볈H&æIM[[䇆]o>MxfMӭo?-+P"o'm;}m;}mwN?>1*G>o !Qg*/+—RO+|N Q§>i7e3gB8Ye=1i2xx67x3X}̱\XAfLu*+-Y,Ljw?p%au"wt8SvImq/sw _W8\{=2'x/:qc*[D /W;/*̿z%>*#2USXK1tp +TᕫBm5մ1/3OػʌeB*d" ܃GM4 /ՈFnE-M7 OoT#'`m v$$`Θ 5B Qͻ*\#5fl 0dDbx} Ug߱}jcr>24<'r8= o /?w{ zGf-rxgמRŕnɍeFSȿ|3T?rם:2:dE fǾ^<={)gE.Ljj5#\l[$]?kZr'{_ 0X9+oy0g_7*Gű}m Q0Fi ? Q[zc>6ibBrņS~+A k_w W(ɾ !ߠ*~)SKaW6w_[e/@㟄յu ?Ba_-+}WUQq]W5,y7Nۓ1_*?)^.b= z-Wט3k3ZëDM~• Nxj֞IoJ1t]k^FgH}ߣГ7WZ+Vf8f/a_M{GN2AIxJqRϠ9Lm›m+W&>ۼ=?`µ^ VRRRRmo-M %+RmiS%+RaG?G?Cv̶eKghڨV 7S^[*BacZ[7zZr8^^ڐw!YxY֙+~ww+ Vx*«ޤ^O*|M,aUb߻W!N[(^iEh3e/`5F8#.Sag*w}dOp|? {Iw"/QG|՛a?&uEMI{F"pV robE(כy^pGw`ed0wzZ7~K'|oloq_⤥˜O!^~##ө@=kSAuWzp_NC?sstTyouL;;1]j%7*ggys3urujOxdw'S^e}̖ftU*Ea OVx/(El[fГv}Iؾo>l!ѩ'F5̞;fƆ_~_2Y S&[~aK9;Q{a_vw>5|iqajMƴ\\^cD-NUnt]"Ro7ZDov㐗3|՜G^1Z {焣ӘA%9EzlXtn2Uu`!q%S~`pӓ= *?`s3u;¿^r}7u܎N;. 82ﭞ˞fާz}/;ٞ7Ӆl8C~]fv65ەCvT77~oj$k'38V"7E0ۜ/1 :|?JF;{!$|wV{/ ^SeCۡWz81t;>G=hjDN!Md N3>{z}ecS3NLb|?wxw/Tv2_9{{MZֶ87B߇8y8$Lj8 ~_.`KoM-/p'po(N`$ڶ.RiMZH|~] pQiӾJHٕ%q!yLŷLe7';KotHyvXz =Zl,-6kҧc0[튱a|[L%zد ӷl:)s ?4دS!"3I$vgape WW6?+LJ xlj)ei&YҟZwr]IgeG}Z;CY1o ۀ49+HtN*ω2{md{h%=A Im1 7]z`Ms;P89ܔQYs2= [l'H*VVD+X\v^gҙ v3yױkhɱq9jCӅGR=3NFqދ+u}{iqԈд}6\4i\s )敍+9ayMx2o+˨YN{frS|es|gxٜryH'JEyY r97S:ߕQօe~%sx0| |~gb>]h|yNY}])|2sҦH^"B|eŁݩx J'by w<,_|+O[ij]DL*Y&klNû!ԗk0"Nk9n Z |!m9L7Mٖ6lK CqMHQ6K_G3[[k xO ~A& n%O3!)WYߚC[Zڠgag{n;d$mx-(;FlK玵1vnrT~TQ gJX;>GxzRx~F^BWaܞB $& gu%/pmyv?JTg͓1&.b~_MSi֗x?O'?NK0}}8ouЧ"&m%~|bۼ˿_M$0=;^ u=co>06g@}1f?٦(#'iYSf_CwNNQ|G7vIoLrV\/l?͠xh⭷) UxCp?9K>NF Txo(x=\qv;J?zWb ̠#?@ǕQ[2d+t[cO p2g8j? o%-f+9_rܟ?9I{l;q_NBwlp_5Oz诉';boOx(0'>xj0ٟzc!as9=)&~O^/z3^G3nc1Oۡ'TKxʫ#+WWiI6EfX/3_os?z_0Q{ 7b먢Ř}a=뢡=lW2܎eSLDw~B>p{ )=;{]Żn?hQW>S|md'ޱf>f-$n'kٟ_lٟ_i޳ϟҏ_bg!9`SQ>fx:N=e?jIP/ߣ5ѬpujB&g:hU!v(vU@<<0$-cLIrxQ|u׌1 D1LdWqhƍ\J%sϿo{cJx?\5lG!/m#?ܚ3X ؏Fш#yg2nQmVYg3Ff,kWEx&!ƤoGs FVUYu׼kdO8[ x=i#wm!} z#/4#~KH_W¼yr!Hf~χp!<χew۔vl}5c{KC۽og|n:ILޫ:*^ Vx…*ZFK1Ci^yjç[\]I'(~')b[%7)\'2ZGЦ~iW%V!/_Z SxNdBX8siK729Xm:`WNYLx1*8[yn^竟azvF<_)>l< /+쇝m~ ~ˉJ4fy=lvOΟnu>toX J?YܳA?dF܎~'?@絃{t꼗~Tq .*\[+^k :dqGs*r,~J#/Tc[BANiNٹrXsyNf~3+]g_<>C b3y z;s__ l<^]K&fK5ckX}Zv>&c'㐆}FqQd1/q-woHw، 6crͼ{fLp|pb ?ld'A1>ٷ =Ma3w?W>ǓW9 >b'ғǬZ~Ĭ[}zĤ#gye/&nѓfk^zc7c}/nг=Г=yO'sgqzV=j_L'ό d="npԄQ3`sfQnQ+9)6V f뻏yiok$b});܏q|Wio#QAU10rfMِ珢3y1k3y1sL_rL}sLtMorMrg|q>n0#1ˣkT8|=MV4%q3~DS=N;DJlphYNG彯ca{9 T(\KFrۋq# <=0g&>`&u0zfC+9)Sl~df2em˚ "O71ɘ/[卑֏e/cL6wS\EkɄ9T› 4 -a.ۏK#|qL w<?˧O8pg-iC%\x=ki Ǐ/ öq,s /Qy#y:_b '{ʓS9ۚJx/b*T:>0#uِ -=8yGӦ^vLQ];Gr`y=EϘ6S;1{|yQ)g([8} ̦%{C? B sx[ݼָu3& x3&.2{U^A<˾.^ mg`s=ϯ>3~_ȿ2qb_>_|ǿx3#ճqοfʞO7>`vS}58gm;kYK![q֮E5g_OG{uiq01J͑g\|}FS;g@%dU>gCu)w9.lЮЋ 6ay쵑6,vʞ&=qZ||u.;gs8o80KչKV} /iCx}-Lm_@>~sv u:gwWٵLϲ kṀ_F:Ri{8xU5/>h1>bXyԷ~n*7FGyâͲgmJ_o}Nkz7a{,T\YO6 :~/{$3.ě,!>׸&\9z'Y^ų>'$%_s?]bT~]/ u>'8R&_BZS`nC՚v[6T OS} cwKc0r$'&|D'f)sm-zU"pw_% 16dv4S;N5^ª_`dIzvlFxsicJ^/O.Qˁ))>&&?gX^fa`. <39׋Zob:Ҝ#ֱYv޿qۖ=S_տ9 1lw|poq %N'`^P)P([>9F~x( Z1佯7{ov4d?RȵM9[B-U)O1isp_ہdrvCmbuWʙ9o"s+ա`pBW &zz}+;?r^1}72QY{6k?w˺{g?0{Kp}d3jo? xlY?9:9 sٮ6iE| d{YNod^SJ >Kgƃzѿ^{6cWE<2U# ٜ?KV45S]Nޤ{%ٽݣ:zS'< i6GHPjg6^?몝3z9RQ]q]5u@_y^~j0ϩtҲ=́ OSm`>%L9wj20 .yK2!¢9G8>Fпπ7^իGZ<88r(g͙цÛD S?6mNRIF>ͧ=>=sw\l~^s:|<Dz3kLǬvqt.F?Ug=z>'w5qYy=9MOt5piWL%0ony<Β~ϧ휩=lc3:-~Ǟ;-);y6OٖlGlL?v喝!߿:# m_FY_uĿf.mgkLo*+Y:;_~`_ T;Wrߪ:=Ltsy\`se轌5Pn潠zML=ҧ̝k!]2>u湦C}v.~6).M F6oww[N p7r 4 XSNGՏ'OGi>Oxo 1pݧlHy{#,$0a{!i'ee:3|ٶ.yO_:=4F<&] 椷y#rQ&?\f]=XL`61NC߃1'C7M;(@{lj3b ʷ}>z] Pcݪ.bZ6'YExɏmUF<6oN>9> 7,/#|Ox ?(|Oa!|sOcTަ|^ |*5G>C'<DRO\')/ f#0eAoz#Ld%5E{)H/ A=Po|+z% .=eG(0Rmsp:9].ace0L+\ 6|6mmې!&Ǽm޳=Y82fr}('q)xDط~e sx.8\~WCQP9-'L<UǿiUq9~?&{r[iLO/lm0mȘ`isJZy'os?0~c}r^nߗ 0S~ *Ҿs}tonH(Eq)AF|!=G a_j'LVƨжϲ96Z_FWPm_%)\[ 7z/嫨µlyQ39ao&\#`pa %+03 _!,bM_~e;'z/':θw;n3/ع{4Hn^bT%3KP`Z)N"B:z(8W}/{+OΑvn֏V!Ý1vf=!cy 9"ȍr>1(??RG-YqvP5s|7 [lyq9ϱ1D\%=l |Skp0o[%<6ՄxSMtU9W?xb;7ye-x:O2yO9N)yS{d'w">tΝÛH%;0w%f>|l1ܹY>0|0Z$$ר;s6p s#^08VmLxAO/:ɿNx= vdx'Qy'B#/&b)x3kKgj̯J(ӖmXAzC8.>!ꨐpW}e%Lq99΃n.ym1({/3e^f7e@_Ӊ M億},ce ?7%xѻ=ә|SHW"Um3' A>.zV̘t&.fNMg . Ɉ9з@ > 6:ާ%MyA{Ǩ{s3gb*m)qLhL:8Dz_'s-_8u-`rorB|ww%/wOx_EϾqUzAqj\ܷ{_6Mm<2)/z"闼̣~zn'mi9/E|!|8}Wg3ڹʯ?6M2%~LG+aM(ִ3E5LL =*KBxHv&w>9 [o,oEFh/x*^=+w<˧ҷ[*d Ɍ9ȔB${+SBgk{=L|ɮ/o[&ӏg<+2Es Q[S5T%w:DRQy![Hן$ x)WeyM|Dlʍcnz"pije'g;WEz> xl ^U'TZ;D3'e^sy!Z( ppDaQVV/15Qߕg?Ҡ$JS* (2l3p[G5Ft_I^F~)=6s{&<e ~/)=Th6/Zt;}_g5ܻ9b_e1u /o'a!-Kz7O[>) ƸH|_XT~y+OMVVo6memfiEK#Do֫5AN96.J>qS"?ǡh8ެ{:NҬ~{o'5Y:fO(g;y7{^Q\>|/5ڻaa0v]]Y g3vUfOlfK'ߟ1_ڃwv\%'ƎߑUd; >Iq=1ǿiTo #~x"6f]SsI#8}TW""9ɖ /'ن9l8Z=G'-+{{txCw2cp6ݖy3SGs+\|kȘ) STN]/kP {k!;ޱEܓឦʗ0_Ovܗ,BmWHx+g9cMaƴ27!5;o wNUz?ob΄Ywsv }_{iwJޟ ;1 le.Vs e4etelsŹ rbf׃of" 6f;m'6dZr6VVV_3=Y'<6 mhc>PN?K>{?N ׳ƾI>Ax~.FU|~sy:spJW(rGmI99em׎2yR]h.p8]s7}Q0 cxZӑ9^ΰ =ԣ˝6s>'|‹Oܦ-`!:`޿v&|'េ+>\/<] -˙lWy^ '([urw_<6~FI+njYN죄c0W`[xqisn~8UEa< v9N25ϟ}ܕ7!Gb^y',y^%\gU"\Mi'Z•uHvP} 9>?:$}}rʤvױ?[jm}q- \WKYzXB`tix^._n#,VycTkÃm .HxMwo a~b?caχ>|߱g5e3/g3]yMyl~y-C۾N[ޛ1y˩[O,uB)'nڠs Q§F)Qƈ.sAѨcOV~cY~{=>>ٺEN1|O _ɏq D L8/p*bi0ssbnۺs {0i?I&-`=#,c: N-`wcy]HVQK}^yɗ|X+}6 TFsxJ o23v:CK׊60afsĆdC;:S$0&6vcLgQ&v|nFt6-h9l)JO/WhlF5Tߎcn٘ox3Uq޿ʓOW?rφy ^wһxe-):;~ 6/H>+)<嵚&vx[/}?K2х 3$Fd%gS) 86Rc両Q=kAn*5UZhl~sghskFk#x#`Ϥv3{wK9(LzCh~7g},+d{!N67I;}pgJ ;l/Oq38 Tֲ'+vjH? [Zr."}ޗz'a P峛ZwgiͼMENY1V U}.q7Ax{@ی'{>OW@v\ ?kI&no!v%Ų֥1ɞީ="/:N {6󙢼p_Jwepz+F^ ܕ;ڿxvwߩ>cō7M?^خHr/]%wt[J[ Pض:~|Rܮ* ;m $w=ӖO#fƷ)ej܍{0ݥ0dܖ YrKb{na̍kO:[ 0򕊗 *q̍s^Czzn78/o1w;yYy\Tns{re)w~"m}؝gcy_yb?arЅwC7ѫ E 7ݖ~e$8Z~۬v%/p88d vw+jߥ0Ovv0&3o :ݾ w}\4!3wrm^2*;In8[˄ry1+cy5}̿ܿ)^QsEH<4/['AxtV{`c%0߱XAp^SapX { G+C_:V\Vlh^=DsYuQ692ܳÌ_&ϕekmaotsHiՁ'+} $@ﲓ>C}ً<${柗Xߓ V6.}F_{a7x 05,nu1FqV3sMq1:1y~ K6?יK_7xc7=J;yGb,oxZ=*p/A;' 7k'?pg.=YY9~#?V$L Js!`cJ[K!,17t}&oB W-q|OK2^(>4vOWLKfϏ?ϗ k{,B O؏Ͻ_ODk@iÛw ֜Wmnk_'G#D_E{&B\szgX:49'.ox#i*/D}iܗDH'ۭ΀-f?b_,?ފ3BqUֲ}_2sKCUJDCW>yX> VGAW3$܇eL<6_꥕1k;D?;,gĥ+kGJEk ޗIvo#+SȇKm%2(eUwfSpԭ?)w~/?+;{mGr3ыK3C O~kOLTW>!OJ>6[ xs54!|{ O-oy}0y ySFT!>X`>;6K.L]hФz߭'<ٳSD=E`&-`&Vx^Fm '6>R 6/+"]8܄kڳ8TkB- CDĸ5qhz h|w7yyo,qC:N۬u+흚uAWShDXaC~i~tr?Kn?_ ďʦ?m}aFzOIRw8S%/9d)R҆8U+so 9ϵN!ܫ['<0Oũ)O{96D c r~> kf?#|~Upɩwu ÉCm"T¯BwݬbUUM]J|ἄ r,yw'n[ؙJ8ٿFx/y!菉a5FIs򼗫LɄ"%"l=U΀6,0?snc,nap/C8[z>?Ս,Ǻ\4z-03]9s1늙FUDk"KxBd nqpu* ; C8aրYy갌ߣf?}"@mBk555L>TäczQ5x&0g0et=FWac5PFmj8j,c$MƚVZw pH.oZ3!մg8'cj 3k=O4g?5qeM3~)?4~i=ִO2ֲ<왢"rlVVB[HMMk_˞}_˔?>O;wj-o/8Z<‚Z&/V6}NHWcF/<x vˌ330'pZG|ݿϝ+ pЀ5}Yk; f͝l^`fcL{=_ x' !ƒ Y#,>7v9΋=c•wE݄w:յy"R)o8[Rp}- R IuMrLxxMߧqNǗ}Ug]i[>յ&|'9[g| g )yOp*VгSbOzvN;Yzwf={z,zY[!o{Ns)rզzr_ΏgGc8$G)lZ%FZTLcڊ}78,8!5WYČGG6KFSZl%\nI6q}#ioedI^q~yX}oyV>?k7djݽ7~7ֺX8)iYH﹍qˈc@?N/UoL#*T>> |7!r>m;|>D /I7qD8= \pqfDֽ?6u)O'oSr`e<= ַy_ a0Zsb0d\Gx8p# "<1"`ccv췂Sx׀?!FF`7p/_x#E8p׻r=O2$g8N!<8 ГF {£S~|n4'4_siD(W+7 F'SLA~OFx V;wxM76E =\'0cu? ǐ]}5@*&/$kjp\qc;v;oj0m>LϦ3ikؠ)\_Ysbn[\/i;mMvDE8Sm9/G 9C_雡#WpaZYff~4if;o w"< \XS}!³a!/ 뱥%ȯ8S߆4Ÿ53}N8p*孿^|*7glf.pO܇أK0ڇ? 795/i͑dh/mo^epCs3'epLl8aR??>As_eI%s Aja5PUZ n BK [%i H J&t sw~* LnO'|8"\8pCX[$C ?vy"Kx]Ko oNy.(|VXC&' -S"}i)#\N~ 6ƃk ^Kc F2?K<qdY?ƙk֦y1;O&ᶘ :y^fߣ>ǭa'oZn?j6y/Nypz!cC=2N%\ 󼆄 d݀Si58=q['^F')o#|oE(%N#| }3sBUMW5} δ^ZU'ei[ z~?Z7j߷V1韰dǗEޫA%.D4#S4-Һjk{SmNEkp~-ZR|Uד_CS <_֌gӋm:b[SG%y¥1?vx v|pvhyL90V9mÄ;3mTL{9].ҟyGJxi;ӎM`vvUZJ3<̱1d.W{Yz)^0Kq;p 8]2+s̝Qۛo{l7p\0B}ez߅KOjju˯- u?ޮ^ATLG)~|{ѓg}!x FN 7NO9e/'xNh4]Jۂ~8|Ĕ–)N%] _Յdަgh?o_*~9ߘq[Ѵ3/7xv;bn~:9>m}߻:=u+~ݴNv4u&ݼL"iSi;{:zτպ [m>Y] Ozx[OS;{8/Seڦv}%>eXW=zzGkt{~s3'zٺ{0כK{uw:%me;S/OG{6euo]ڐ`okC޶ͨ״/'o@_*F{dUWsl}Xoc{tۤý~o}m_#«7+\Fum }zߡ~o;^=dį1g'!3~ ۔b"1~/?s> rIxr_"Ō_KA^^~Fv![q8Yr?ކa+p+Fҳx#Y?ד!yJ I[ ~`>xoaw5ɲrӻ)v{s\>jn> =@3 WE:߆~ՐK c.:}6?&|-HI}?ĭמ5Ƥ n̷}m7 83}TEߎ +.y_nUpu)|MśTUnj7@s$^rq- xc{'[5 ;x/x~Q<3=Y;q$r2?Wٔ^= 9}~ߵ/1Jr"~$>|"m&^\>F/GڣnLSP7ӕwpt=B㭞s'pތ/aT#W$B8#I<|0:;K{r_Z5~gyMo=7o/C섾4?!8>?ߎo3~+pfC>*0s_{ސ=/ -_7c֙u6`׸#˿`y؀~Ä+s*G3b Ux)ww=c=/ 0•0g؎7Bo¬$ {9b"@S'+3%\5p_ ៬+Ik8p-19_ީd|rhtKߝ<\_)9m~g7"Y90`+z <#yǟoQ&/UASny)ˋ3嚫kBxz+'Nyw$ی{67?p1SOd>  wdǮMٱkAv:xd30O%7fkYArm0z$wA*u'/!ܵ?gloh~?t{#Pg.~GAOYRe* 9|P{߉Y 3?"w2pJ&_~Ulw1vVvNL{%Uwfs%KNJD `X% 9dPDT (IpQ DDDA[uj2;<;sѷ:Uv̘;=^M`ٕwѣݎʺ=)2߆ec Ǡohg/}I@cpy%i ަte!طu3cxWw*֤ܖ;;,kݰ# pfK\;wX֟[|wvfݴ#9Q q8yvws~WHŰg~^wsoͽ_v7o΍>G%A=ʹ =I9#ۮd`X?yo5~ƢU٘G`y;ӼWT??]VԕL7=w;9ۄ=|׃$p51m%4piO{l99_2tEo2iw,L?Άӿ/cN#s?bD13¯pO+sM>ы7?89܇Ʋޞbpە[s9tY; z _;d e9|([2u+U GbX/ g/cc8!WH{*2Em43e>,z>c%;Lk{kH]U7d-m=k9kBYMjЬH<֋9X]!нx5+\[0]ϋr5?>x=*N?9E)~X39(U;ڟ׏tQ鯫ϪoqL񬷬W[m@)Lۣ~ojTEcoE?3h7SW~glR9+<L螂S'n9a?OZ?'Dž;*O7WW长aծL\M#xEf]\dxdv2 A?K[,A ]ܭm_y|k9s; qc/(}~~x-6]㛄gf țT;o[&qoҽmzo͛7ZoAqM'ět!]cwhO 0<n8{/j(rMNPŵg7gjgg1s4e<u>ੀMe5 6+7 r<'Fxq>:Ȑq٘WY%ڐHᄃ{f_3*c47} ||tsHmd`zrLmep5|;ڂ27]nE1},g 60F!fO!|plyŠ3 ;!<%MfCt!fN!tW 6 p7o [ pA^n_W*Vj([X9MU^M=>?ޭᛛ8m+|A5k(gFv(ٟ PR]FL{Cve6@7aKZxeO{vuu4-|())k]}ePPSj^=2̜4̬u-:  Nl.o 2I:.q]>te|Ϫ ߽-+z? C1)D22ȓj>pӦN}pO Oޚp޽50oZ\,ڂ ՛9C ĚFCi2+:2񶽋pGp{_Q¯+]nGٳZDS4F^I7<a|˚}#>t{rM+2eCx^Xupe0np%ӽ pNz[Wa 0kYW;rjk~e:dp.u樼t=yM#d=-t$}=3_/ JJ8& Uck̸J}%S_<Ҍ]46>?=#e$gGRhKy8ժl`xnFty7Cr%Ȓ=wy}V~5mod_=t kH<߿Ox'ұ(,wܓ=_ě~߬ ]Q/T[6C]5`dIdõ^AȦyldb>Ϫ.|>`HXs eo> @)6ʬG Â|dK;,cGeQtU o_'lOdxGq0 Y:h/VCn r<ʜ F6גTb? u뻲5@;BQ`=f/#3ggJGŋsOV*迎d|6F=F>L>`Tq Llu94d SN' .r"OMƯI0I|d>Cc̚] W"Jc=Xz{_ c,0NRmlgڏI>TDyDX%gӣLRrZn#g_=cnq7)8nSY06{BYc.Qqߐrd2LC-CDZ~)H>M==҆wM4DÈDS~&?ьD6<Ƕ}6ѼUC9 , 1OB!b UK6Cu?9?p/nc>e_Yr@K+q%cMw):]bGǦnȞ8˷ݸ;ڍA-<ד Wzܓ$+MElזqg/i  !ko OGvlܓ5}(O<7o1Ǜ}i0ͻݘ&IɆ}GO>&FBټ6`t!|E޻\&^ƛwb8㟬lKI*t5?ӈeCʳ u.w9e+9!w/|ޜ`Κ`N"OeeOx!|$_N:ʉ};"dޟ(IJ./LƩxDC+?9&>Y;{.~gFu'zƥg}6hيiP/O46ng:M`e3( I˺}RyM|>ES>dy%y1]- X~]*]yĻ[u{u_Żv,~OMzvgS:Xcғ廖_]&9#wNz2Iek`1)eV31ټ焿Sdrd|ZO~rVj9kwdz+M66nL-r7ZxN)to~;.wC-s#ƋONyp2sx_\<նvr}H/{=,:8arv=bu%{FG۫@SRߝ3uEog[TifEsLkj޽;S\%_]Y6+Oo%@%;SS.r9ai\95ũ|q#>}+8[NUnIӖg=+|Eo}^~æ\V Vx§U ')|]Bӕ f/*]*Oަ5cf\_ /T{P8NިYӿope+^* QxG23UP _U8,(2e#=G; 3/ S?w @_9jW;W_ OQ[xw ?*@97 o|e9y??2 j#q=K݃* Qmb`n? 喴HZ<0zݓ_2}70LIɿqh2Qu/t$1}y;/3/8[ߡҽ3|vџV b3>? G8@e_: D0;H{ߋ3}/L_S=z!CV ϯ 甊 ɮ?<33 ʉ/;z'\6yL%HX5|;*W߫1M%׭d3 ,_7P<0]ϽdWn>ɜ_?RXC['d'tHt~SJy#d!*r..xRxRk4()x>Ms?_i9l#I͜7rytc쌓kײNIOYg"ǗKŗ;4p?'X08{'/oq@1G6V-LȝB)a3/)4ӗPrdLtOx3xї}|큔))˼r<Ϩx*>A<)ǧELoU5T:jѮdWgы}弄7czJSHG]5iiD;'v){9_ & LB'H)9f'z= G2Ab \N?<\{ufJ[$v]㽫gdo9r\s5f\{<ż6: D>,HϲUS3N^-`)6AUahWc^C74*pD#c|[Jr9G|A}֨~gkr{r>ޓn}>a_No[n!-ZǗX/s!+Wp3oGI .ni.E|ymg;IXAwN7KV[,WZ;W9cWY?uU/M-[nap[nQp+-&p$'-nґVip/}N~ωDʁy% )np4U}+.nep{wɍXIߥ\nup߭ww. H> n1p,u1/譏⶧╔c!mmIp2MMʼA"ex*oC}i!MOEh6g)<և"{W ?ד&;XeխqtyUEUt ugCC1Wligؐ'\t&潼Ս͛ 6CWuelZl:w]ݭXXRίpΫps+K P8Ϊp3+I gP8NpS+JhT8BpU8Da.?CG*O*@=*|G R(|C _SW%/*|AK SgS )O*|BM_>/ Sg*G>!*|@ Sx{Wm#0*l0wXM`އ*zW;6{6Z8c?3&C_$GDw3wb㻁-l_VyIswc?9Hw#||MbgcYsw_;‹)_v!Hn; 3^+pUpBjcL/sB3@z|3~>r/՗Gy_XR=NEu/.L7Uu)O.? oVX2s\`쭰{9^v^.(]e#}k{.pe4ÌѝJ~=˻B'o"R?NGh'>0}dz8?&e^ηv^~E9>4#*r\ƒ|$>`|:AhXCxr?9!M\g>RE܃k W$Q)*@:t}Ms'/s)l9\t v׈V2}YQ;d.GFx]%~Og~Q0krW9#>(#we>US:Wpry+I8\N.: WV8]Rop)5r7D.zg7_a(qH)\cW|XaeSGk>]lkD "}N_3} kmcr} />BsIAT,ܓvoB=ISõOA~ 'srkH dyOG}J"wz@G8)CN;F1v[57} ]e5<3r>^`ʏ)q^%\z,ٽgPeƳ[pӦmuL?$Jqޝ~ /܃}Ï7\Z),row3 žp~4pH.L?9},P>Bnb eG1nr ukh:1gsMrEg34ۮɗ[I{g@|'ae/UbR(/;cK{gOE#~vGL77(olfy)俴__nA 񸋄u3$TpK#eB·gO~qBDw.4@9 O$omp!y#p k)\-wq)d5)䵼%.p_C鞼Vy;$_*\;8&EcI)Ћ +]`m6ly3VBngyu~w>8yq/Z{{ρiхiQVcg+15JmI׭(~+1zw+1}%m=FS[iͭ(j1tٲF=el#+H~ny{@Fon=74KFṙ]ag7*a݇nE2~3ak";wSA ?o3oydM{y6^CqötuiNKiF2ѿ A96vdž{Bߟ{se1iqи_ 4x'}W7vY--,kaڀ?љ_rw4ЌN4wN_MtKՁjwagIy)&7͝5޲ZN?L=FdӞ$2op7X9v)oa9uywu'iVAo;_{ష$vȈ}]?-HGQ;r‹wP0+wDz}ۜM;~];Li1 _Q4g"w}v&NȺ1; oek4i1 ҿӤ'E/춓9|/ag|gySdnjn;}bW;~dV;J6Ÿ̋awRV MÝf_Ya6U4]enp:S+V4ýYe/-C#՜Y ӂL.)$LEy.pCr L#?t1L'ζ9w&+3ir0 +]K`@CE_Q]IV֔*0]iDᎌqA_]环mMwX7zFVdnw7TVe•ϩSgϰՁh'f)Yyb,E\ڑVfa O9nݺ렰gs |Gy1]clwnjg3}Ka!X7G ,@p1Ljx:^{oƧ}֓/v܏zX#B9/Dw^J{ ~f.G^> k݉Jv|t1h- Zsz =w @z#gI(g}~cIOe ѣGh1}O[$ }$*􋁾:W[vqvXt}pQ+c^ g`3cx Wd<#d&-_??<3cA?f2 ?$)~=H=1O3L_+߽ٕ"1|?Ǽ: DwaJ+j ʙ8DAsRvI&''9{*Oϻ)sxx1w2ROs̷] DA8/D:_;C !̣/@#=K(DI"#[~V oR7*A Sx_+Gޭ.Px;ޮ6*;x}#|eLro;ߧ|AaêT&fsLW1_M_^h'< ,Q؟~ [w{FɪwtmPF캳8gKUae ".Wp _ql!R\bpuT~Ue,?4*;XbH;^8|}G;$rr6go3&C'C=b?(يR2]3E2gc7ӵ?9 ݆tՉ[/:(c'=uO(wLR:a3ӵ @1ӽ t(w#w knhZ*UaZn$xY)+~9_މ0a#?edLV{e>>1?e9s)oFf{ ¡ pQ)s=\c_cNJe^Ff~CsuJO֓g8p+Ɓ J >Il'@؏ީyN\L9L?;}\+5@Qh6ϓsO/ϙ2Dϋ9TTwNSֱS){Ⱦ?jqXZnc/ 4,YT02q~& п= 0UxeʏPt)}vezɌzB{#8?1\I{nY_4]܏s)[yXq{ q%z>tOvAy|J82'4':i'9#H_7t-ඬ=Qxt_0zz_,N_'VczgxK,*!%?2C_z0J8Z2og=M#bƞ=M?8;q2nrTsYܞ񳀻1/㺀3xOb\phuPf\ٿ!Yqjwƀ2nsqSߝ 겤~1ƿu{ӎ1?i;si_ it[Uz?4o vtP<zzc*;Ze ?e 0LO^7rq _$.?eKເ3CJt`,;Vπe$hk?^LL[ -E;[G}^՝ŀ_=Ck @>gh 3̯g̚Ƃ3t?#fȊ3f3f#1N|{T|<3ʝp zv>Ze:,t|'l[?K7Ci{Қ3,ikp'<ߑI0vHۓ(yiuM0 =k֪2u&g fnNƨ*lI[3?VS)aGɸp0^6*k jES_YkV;]'>Z[6a~npzg}]ᾆp_1=}ϚuΚYE>ҋs|qgnݑt>pNֹdYFm>YcD-9jJ#{9S : ߣf-ʍz9\]6HbY õ,IN{ܟx2x1 'z|''Y4xEv y<d?Gʉe[N#F2we=%sO#]{8 Jm[^y"l\w.^l&R ?yzp'1Ɛޡ!4{KWl|7ỳY-E)IJm;e(a #*>9h> MZEmޤFG7lHެlaڧS>pw`tR~:鿄c[E?O7MOi 241Ne04|nr=ͼ}qyLpUuc]Fd==)ubo8|VI_KпW? uw%>b+>=Y!sl!^;ru+FXJ`?ӹ _A8koD>wS©Pe"4:YFm.`2,C9iӈ;OzNmtaxfKg_gNRƏbd$޻"k̅6Ol NoH;&tFU-Hʣƌv^q(_j !ύs_]]ox? ׅH-1ڢyc9)yBV@acbO+ƨ/0{Lؼ f"j_HƼ_,[?Mn$GlGm@ dOt쩱<tHd]@o\\ut:E*;.|\u͋tz"n{!޸H{]sEڗ6"ݰ"Ezᇋ4oy"vՋGKvCKtOYKvCKvEq9݀oQ荂B6Do7Do7tDo7 Do7 väKvֽI3Oh2ɭhwjS)Vt /P}rHoD>gX?DcοDz^wymW]|y5Nu>d֤%n=.KUF?t+n2]Q|JK0Qx.0t%etJ~b9ðH׋l{|Hu_֑Gg/\.2}yowZFZGǞ8Nt{@8y?Bo.z.@%)b7d*@鲯m&vrԞ9᪏g>] ۞[#u_1~鮘"WL_! k\ǫ>uj]6vQ1Wmّi+>MKuG8,8>ILdaC׏â]a3Mbpd-^sQyɷW{'{7{/n8lWֲ1&4 8?D(cn8 p' VAڮB(Ci[(ҽ 0U~8;4dBg77Wa|pMC@w9qP&mX7<hfn x bsɗO~g6Oo>Y=s|2o<f@ÜOIŪm?DPkHv?1w x'[B5ylq{ɸw2<73gK';z}jf+q/}|>~hק7z`~<"'q r+3re5޽m{{1F#x\qXDz8 xǿ< 㓀\.u_^|v?BP٭ E|bvq3;ݫ3g :*5epU,c5|[CVL.n/޹1>)Q3w?zgpL}P%Ȏ@dxfkH9-~_>¼7?2whS`/\fqy`k<45~985nT~3O7/`;b:iDR>nO%`\W?OmOPǩ($v;)-Y~M}тAPLc^Zު/?ٱ.I\K]`uq5`fǹe:Bfٟv8  s=N1':(|d_Y QTGb,_ky49p+.N {Wpq\\^53 RCm%,Όbދq+uuq>ځ$ޯ+v[RIX?0$GgrRlw(bKc;X0$_e%̹1OISI}cҺ%w{pIy.WpNMc9hCcm9UO6>Ruvs}n6~ m_ 'x|3 m'>|ݿ`zp> 7^}!lps ȧ9$B6s֋,\׍TNDc[76#7 ,҇Bl9!T6^ >!$ʛo|@0m#r8oy1{7ysoZE/C9n jl[#𷸯HJ4׍ ?<ǻ]*Mh<ZOKԎئdh> ~p[C8XΎ푩,o?},3~'>1;d"3E<\_e;n6V6";s :kjW7Q2ކ1zWsp)IsN'8^px߹O_—,6pu׉귐_1cb㩼,Ss`H^<,vId}<^e hJN^\e,&YYɑ33FJP>4ac$ SPaj 8 U2}1',cc̷F1^nw>~7Q+p'MxGXg]-SϩxPOqz-қ;QwJ(jsqgQ&/iܨݦݧdwyvxuk/Lv }N,<bѶ>`hc3UzZ:cYS;$>m`)]i+PHk%XNm5Nܩ|0O܊>ܟL\DKejӪgޒI}cfJ/6Cʷ6wZvG#k`t*:ʻ/_g,:5چc{8{kAoyC-֬[(9J7 ҟQ eRg%كTlYu5㔮ei;REy(^<]v؟SŴT~0_| ె1k1A(oܔEj/zz6wS3[P{uu͛g/{vbg-f;Ur@xfH9[2Njiy?L꟨`ڽ}`(: ~SvZ零n𳩹 1s8"vz-v=7MMmhE۳ٖϼAt?1F{<ۉ2B}%M~&3ZJK֦yPn\~7QMym/k_bği;9k)^d3i KCiʝ* C= K>VߒBewe||IK*?fa{ߥM1 ;6ۃ UQ st2cߚFzk $ҳM9('d69?7()/CUg(Jol)E3n1F~u1KƘwcpg =Bq 3{d"^y?<2Z2ȱMxˆؖX;Gm+G6T՝P%em$z WfKQ׊Nfec<?YiܗLbzIc3ƳsxQ'ߓu_G3123F=ahE\95uu+m,u`['uE2q-ϊ`6>ؾUv&*o<o$'L=8 6"ƱWs6D{Q3퉍L_YA8Bg` @\X.+1NLs惰du"f-2Ztq޳x8c<793 IfU/LveR^D倿lj rO$ !k2H3<9;&=ٌ/g6ۜFxigee@2ƲT+ G,?M%ԭ faEODx:`lp}#-9[}ac؟}Fgw9B(3Wx}pD(sY9^wIrU4+.$gw2|VO~p*cv *8 Āf5;ge]e/TY.<~jpGSY%K㩽ݰ] 坋HΩ^FplԯaYwl򚯾A %V y yܲ7Eox 6bZ6jߐϗHy΀lQ>v3p2bca)f8<=:ż8=WJv{HKfKy1b9HfI.SF5x~efબOs^W7s:-:zw雡"&X;& t~%Ɏv0sCzsX/)n,q 8 '9_1-$s}krV^iW '36$'8.|Tƨrm2PPwOXi79f$ST<>Ӭ 8CQ|!(J\fuw|ۥ+~]۹7t۔qy)GY*&5yq9ܯue']z+?,}c8:cy^ۃ,=Bmx7| 4i XMʫ[wR6r2۷u=.$ȤʐY_s!I=rQubżmFƿ2J]BiͶEm\46MT- |0,P.Aa`7ɾh[oL{HS}` DcPO_"ߓq.C\ y ]f}厲)SJr0| p#@8ͩ8 5|!j698CgJ6=gyȞE?>n1J=y(F,_ac_ `(o\4f`/J(o 4҃p \ͧm$|a!9^* |/ @m!5 ?&Է\\H(Iq>$;z 4Rq> 4RpCYj;p>u4#ڃ땇1;pS䟊ƉQ~APvnn@; + T~-R%d9.S5wdMFr&Nt%Fn'/7j:ғxJ[@R8JO j)[NS~tm} ʢ3tY.L|AɔA-WY?ti|T W16氊r}U?r~Ze5_)Stu_OɄ}.:LlR24SۂUw'jL}VB.럭k/5{_k9 z_fǶw^^|˻GvP^^p~NuD;mL^:_v50E%KIt USߥVm˪@vU^U2 S}fw !]/܋?օ9'vp;VҵwL*އc!3 ׬k-;=-,ãdS2䋲`[<?9+>϶x-}y|f= lay2ƶ8;tuRof7?ǿǿW;|4wiVeEpAJKILjp |C{{ցGV>ƬՊ1hXnp>;n7=՘+(|cx=t!+cƘ}eA^OCwp,r/iYcUz9AL/甕f2Z_F=b$ЙMS:)ОJ]Psxr>1Mx|ڐiKx> $riصph`{} v0qaw Pf#oDy;ڻyT1x.$KI2Zޘ{Iw3^qY?3Xg|<2/e}eg&wtFb .WϺ8nR e#> ]+խ@Ao?"|bTν;ό#ǀfm 671v/s& Dym̏}:;|<%㕽&'ŻXa2 Tl` ~hf' 8J~}~ Ҟ?w9Ρqs #}`ЙفeYů [ 9 LfZ#<; {FrYõcg1x GmgCO C Uz\JT"~YӺ 6^XP?1l!\YGU6YB!s&Bƾ x `p!SȬkL!e'?gzU ѹ6q$ə0)d⻙?2aOߜNz!sJgtas6GaspaslasF 7*`3 za.& ꑀL QҵOyȪ ?m ?cUV*P\ fz3GY.?qV}zz߅͹f"]^:?^;G>O>"\s((oߔ1 zu院b FC6-{Լ~䤭qRH1U%ni;?÷EپE'h(q|90ce(eʷ_Ȯ~Su6~M<^QmBֳ{MzZ]eom]닒-uqwQ> EM)g௾a_w7ZjoEoi`dQ_SQYr_mΫr39|K߀2k?o?e3Ǹ>f苊%2NLP#aW~0 A(o=|YaYNAW,;.Z@;?\9_8ؾNJqt-Fwq% {~7D?t*ЭS^)Bw?qz]%Vv]{Kg:Yo2@Cs WYlAM3zQp='̯ܞsg4Gu-؎2p1CIWYސ҄KT_.w/yd\K_g<rwՀ_^pβ mEE[7r<o_Vge@H=ߛ /SX!{+c{E/3g._Ҙ]uWʚ3ĸe ʑHrtxPs7c:?py+g:hVμu ZQD +/c1F}U҂p4lY'E}ӅrfrGd4QQ;_~sӡyX㲕hccwN3qaUk]fM޻R{ ,}`BysFY]ܴ};v7 T DWܛ/cy;s0 #NߠvW>i:m$Ư^ -?Ϙ.yMyͱk[e=0=`||.ز\/c]8TtO7罤N/7k[=.72v wx \Ba6BmEw0 rx*l;P8N~I6SUvξ2Hy2~Y~گ\ͩb~:L/oto2]hO~W73ne|;!¹WRZC}@ONb*nwԧ$Ss$.mR&x=Qr#?dr4OtZ3?kYn rnC^K\'AC}T:= -_<)IaIHmybz]܏~f@m (Agju&*.< fYggklYω _ ^[=ݚqOg=ml{Kn=c#i+g7>_*·_+"/W3'gq|U㜡e)hYY[a1zg 3n?/cȘ@¦Dt@•Lޗf8 }x*wL5b$])sb8ΡO>o3Z}WFc l"wLCF7`pv;dP._{\>O݌ʻ=h/L\C-_i#p#J/:KL_F'7l8g+u,+w)0RdS*+NݽʦbF*& W[0q J,;?'Pt~*f.wƕUy5EU̚*.Y曬}'qli˾@%鱼c]Uz*f1/ UCG'hN|֢7p+CQ$C/Uc p٪<_ޱ\* 98vk8p_8Wp|gusnmUa6 c;_IJ&:[Ռ)~jv%W=4cd5̼Dl>T(yjG2sR5{>eQ}'Nj;r3%s>cN^o+?MSEs7MGEs? E R%Q*5?Ebhߩyѭ _6q~YG݅Ա׹Zw|-qE9eTJhbD+Mc{@׳} gsPŶ0"G~e ~[t6{q7frO'vŸL讫0! <(F!1}ZۈqOKq޼{u37釦(<_U oU’grWb+xu:]5h7S Z-PaQz]!F$Ykޏ<彧{GOᚖܫw*dچ1GxObG7jМ X`ReX$qҰ~ AYY_ՠ>P Z?QwAk7j&i\lF.K1tYpQFk(eG|Z~^ׯ$eh,5.#iAM3> _դ3j(֚tSMK0ރ{&OBޮv][pQ׹7Ey]2=MkZϽ\tE^qWd`ԝz\z^]9]8\QіT2so2oVz|Nwi([Qqz*90ϩ08g?q)2¥^˴gWEwX^kyxЅ @7,kbE3_m̿)_;/rQwrjL} j(ӽ(+B799|to{1]G[qiNR;p_i̯b.ZUXjCNV H{YusF=wP2Kn1}Gm{?<Rx«OYӸk7 Q[Q=]/r醡|ȞYgtӈ{w}C.'Vl1LGT^鼖{B+~Y/䞾~G?ߧmMCs-留]wOSmGj;v(6Фێq\ q;x<pJ7qUohNZs>uO'nﱯA9x-\;va}[rܹ%j=Z9ロqD~s1{>h]Tަxu찶w[P_{כU.]Uaƅa[*b]3Ǹ A{3sǜ0*( >p!Qf=wB/w>np_9¡^~~7[?^74Yh_%mK*NztG5p|38Jmǻ?wѿ(Ɇ_8s-2yeEs}\r#oSD軎([*,.o^!GgG=!ӻFȢ[6_&zCC{ I4ߐaEC:㰡!Ż!q8ԐTC:p!~ш̍h]**n)pk6l.ވzSf^ nFpׁ ]<7 5r/M8nNps[ܜ> n.p뀛Up|7wp Bnap[܈&QVQpse-%%nIp[ DpKe0w# nyp}ܰ磬gnEp[ JV9p"U n5p[)w5]n-LͻT?mqܗu>YH,m)[YVu8^r1OQ[00ąJOis55e?(V4њPR sJNW|yP[K O\oԜ] vZ?IǏ?V9?9{=")M龍`嗸T\'L@Wx_~j4@u$wS˦ۓ`xm>?X}.jA;l3>f߷RJ&=o -SߪܖL4oTp|ףּLQ&q<. <cS?(O'SޖPNq{.3.[L.e/ﲊJ2:ڗ?҇'{)G\Yl%[Ϋps+K P8Ϊp3+I gP8NpS+JhT8Bp)\U* WV~Fpy)\V2 V%.pq)E.pa )\P 7؞oږA]btTUˌp`H|SS}?=X^ܽ7 b{+2A/tۘrVqU(|o*L_3|B2]h:9Y7]ŵJ}NQGKL_Ό6!Gח'kħ!𩠰rfI7q]]y#i&y3S_N~/f]MzY}6DXǘ=^l=3;0d/0lF|Ew6gddf;mnzKx?:('t'ߌ &NH~)Oysg= IݹŰCYWOu2w%j]NPWYF'detdN0E'_dxk .c-D<߉ܮwb;;vbo}?-{;Qy'YG \щoD{wt=ڇ8_;ӝ NtN;3OՙrgL?ՙ/יΞWL{v&_L{L{v=ޣ:;=s;=u{7t{wv=;:)pߝ3.i.QV~pіnAFp [  .n \b8CkƁ[wОYwQ.*pˡV'm.WP^ByմM!L+VJ yB:eOW{6>/?~`vB4vzZq}oJlWWs$g8?Ս¦FaswŻQ_jQۍxЍx1^c8֚TV7]O*zL{RY=Ft@mLyYԎ]dm6gwde'mC4ֱ.?#߷MzȃN!E&r#[yVJ;3)USd)S4A wz2_ˣǯȌiǷcU= òeNg'>0sqlZZ~ a7C6o{(y~ۣ٭ 4?_qNM~l^n:}r 'YT.h.2o\fo]RO+g>9+m.:{rXi$I*c/Mn}i/Ϊh"4+!*l*\V+|A{ Q!*..-U4 vVob yL?6aAq܊s8_d{q&,:2.<. L `v]^no9ZܛUN7,[G{|4Ooe\}h)}oۇJ*}h>~GjՇCw Cw Cw ׇXԇs3//T_3R_3~_3u_3c_3`P_3`r_3`N_3`e_3 //p//Pۏ hՏ ܏ ܏ ԏ ԏ 8܏ 8ӏ ۏ ؟ ߟ x?P?к?й?0?0?0?E3`K3P3tgCpkMp [99p[6w n#pς'DYM )Mmnp;NpMEpܰQVp7}p*} oC=:}{vr5(np;[ np\ nwp? Qp{{ިQV3}Qσ}+S|z0jN>;lNk>hTG_cdLso oƼ08˼S!ņC-nC!!f^.go ӞeJ;JPJˀICiqPGr(͗Jӕ4_FY|yanF֋hީ0jQ5k_-h0/8CS}8oe|yν6ڹi4_h8͗oN|y4_^l͗?7;#h|/_; #h<|$͗IF|y4_>p$͗3^۰ր}?ٯgŻZ(>trxM<ݛ4\|[s5?}36/hW,|^wj% ^ d>ML0v\ոߟJH  QSϋ+++*Ŋ?Yci+\1NʢwޯSݟ@hqMnb6?'\k(K3ߛ.'Onuۼ|.6ȶG.g3G{xNed@^d u}ߕ xhw<jYYg8&z6{_w <XCrЏ剜(ȆiiV1?|}]eNz̹doz򵻨mkVM3|?oh;,;ㆬ|p8">%:}_ݩu\f-,SYӵ.S])c+!nH:e5Jrm$8dzM2X"h-vAl/2P) 8&ޖiCe,j۞D@=Oߎi`\r ]ӆۄG*2*R_Ef7,e>d&o62ymQF17 e<ߕbΏ"k ૌ|1GT~$C'!CѴdtBg+W?hc踚}ih464ɣiNɩ\/qowR_͕t_̝[]l+ۚT'O]kԣ$G]wF ['_|>ƿ^+acuXu)BOUB> y r.ŴH;pZ2H\ D1T \VѶ_Zy#Ƙ;9ƍhΙs}{?FDJNMU+cHW*VCl}/%7NwC)ƻi 1ԎGomI瞐e2Py`,M&.ѳK}-1ek ]DuD(y,vD=<6đ,/ZH$#^ xV !m~"{)o:q]wb,B88-ei޵B߱k?Zfmֹ?_G"COr<۱GMƈf(3`|3[oEuP. qkƘhgߗvyǏ/}|:ìۄ<}f'lȦȞA=L#ecXK("e+pݗ<]ʛǹ=ؾi,ut[]ώۜDg9$,71Ƒ1lRiWf WdV]ۉ?15ǩ;j_?1lK{ y=:!>d{Lo5hU=8*oӦ2rEy]w7h|q4.?F鷮lVx?+S>tOc|4x{xeDef$*3g&Qy8LTfM2td5t:ڂ/X%qK;LGl}i/_>ыiFiw+=-~>ba2E ͓y.ś㼐= a&|w/l*S@~ yyRLR<yuҸjjbM\_^ᆧ'IWR٦4Fl4L!3RiE:M89jO1uW)Ws]7gUqWPOh+L'F#J?WxpƒT;ߩ?Uj/[e.h;_w*2;)]% Qx#+ Vx_= pGW­~O ?p+0v;)vSx2%u d|˺X;tr,NvmKn᳆ßvG~L&"ПBmewۜ &|Tˑ.lC;)/8<ֻ !WƮ#]>Gq= I -8G87/2k8opk*/d(s8 ^tK_'3t `y_<;meK9jQвצ7Y.0Bk:H芾#ޢpT8tE8g7)I.K yOҏH{жv]d>!c}N{Åջ|י.BG7<;E9>/Y.X|yϡnkĻ0=[Q1Dcv"&aڵLi0_׊Fv~idWFvshl4F+ӈgis49~F<%戮p=U͕K U¯(ªpW{*O7`*<\ + +{(ZId)ö}f+ \5zMֻis FX 5wk1hٓ^OVe/ E{cQx((¯*N p~^ _! Sx½pM:GS3O+\A Q.p +\Q6d-]}%.p>(W gW8E.pQ+\L.pV)K|C:ius ҿOd?R^;Qsu8*:t.&Ӎw.釳Ct+t0d{qq9 oLW'2ӁM?7(P(t;t#{)6v L}φHS瑔M:>b0m)86@cU| %LYBD)2[VK盌?(_vy{}czv⿐P>2.=k~ߥ PXW%D)@[G]#vm3LzCxU[3ޮskӒpYW{;_| /ߥe]YeYrꐄ{ SwUXƸ$/dYp䭳-/tǁ,tCS r7b[YV3S aܯIdr o=AmB=N`K@wGa)wzLK3_u8tEuLSG֑͌o:c&uf<|. Vv-G׀'*hxL|r'jt 9 O˪ʲZS=` 4>"|y3ah`/ $AR{9f. }4Wz5$3 kr>y&vJC9p^gCsǕf.Up.ߕ8<ךk5,/@AEI*07M3p aKxEsebWkbΛYgHḁ2w}u貞G02s:Rvg5g0^у7Ew|[ bF}mSQR7!wd[;q=IGո|\c4ZHs{8i$ րUU=,pqsreAJ4q<loSk+9++\G W.3um\z ڑƀ+,rPsj0Z FS- np3G&}Jc|~I;ʲے۪8J:`@?#WDo^J xGtیO {ㆋ|px?}xkU(W<2ݒȯT5\5D8 AV1{(β<:(x_1t3mmIm}O<Ȼ1}7l?Wgl 4q=?o^t_GR~ -X8/B۞@rbzxC |ݼe_1R2J$%I%)7e[м~9}"/ֻǨE۫'-?={n۽:-"sg_ q)uY/2@C+2)mB%t~!83v=FѼļOSM OYhN+en7*'ʲTɹԤkC|"SFK62ͳ\!g?#{sGeiY 3?DiCwr?@?5;? ?(@ ޺goD玪y yren]aS_A-d*IOghbO0IOcD}{.b߿ԜO R5Rʯs{碊R2{6( OGl gcmÙ>'c`-PL&Iw[wpg^/wI_$}1~=H=rEpSAҗﵘӗcLʷg|{|{)g%$}0HjR!*HZLUzDpsͼn9 D_g<8 o2}Sf[rG|[Aҷ{JOd#voEykkL}_宭ij7@BL|=8܎5Y_ C} _U{$c'4\z1ﻙ_2, R>MP+@ S#*Ud Sx/{%< G;E¿@ PҶmp?+^mpQ* c,ko+T~2qm/g?pәn\FK w($\9GH 9 pGS7n&;Bp8pBێp!k(܉/8_ rqS7)r.ӟLl(Ɍbڃܑ[oW1갓3n7??^wwDdl73EtWM85D =Gydϊm ' ;seAw3P8_! ߸N};5{@= K' 酯G<ӇlW+|TTk ",9y?7e*Dݣ/Aiкr>?`p4c{HSnmYHwƬআ0?E+q҂_#u^}Yu̙@N; Op vtO&WgXdnAg& |Aij aSF8p[՘,V'>KEt5'>Dsh-6s-ݡۈ/2u1t.:Э1ޕ 07u ΁ix?g*˪$qccC-љXm,1vfwE]Goȑ!MZP㛾o1O3={/^Gilg ?']@g_T)ʿ謪 7 _GcX?ylWzI ]ٮiotz:~y m_ gy9I|#Q@s$7כ;Y>XsX/:izQmB!YCqKu)\e uHC?JX]ءC*L`i/OL M}9 7 V;7Pd(tQ.Fѭ^[*-gƜyML8peW[oߘȕ׍eaJZ[k;ƻ|m_Flcl"eZ<{8٬|sO^,P!zl  m eCWl+Ke>>:8꜔߿y~){W:hC//2B~|FrXѷZ,:όѥ2 Fuv48T`ߔ}\6ޮS}¸1_ N?꿑>Ce&@\or]a"@{e9Kyٷ+&>o2}D׾Bx&~?q$ൌ xf(SQYe}r+hda~Ԕv 7S?iLZggͿ\1612~ pGEGx(cͬC3(_ y|vhv##̝=k G]F}-ۺl}vL)޻ӋN 6aBvXph,ƷT~@c 'Ra~ܗ2 !CUK^,wWei?/:A㔑cyUQL~_[9ل#'4L҆b[ 5tme}TIoJb *pjO9 NsU՗# K38 `]t?wGN\w^^-FtT?tyQw3-ԗ 8ᑮ]#ߏF9kWvx|u振Ӈ8? 蛏8|K4X~Fn%096?zo"rQTֶpL1k ̝V4lFe>bqw\x!l}ۙ66S<^yNHr&m3{͜i9.w]sr\44{*juqU)5פ_J.+D;G?;kPx2ou0u_}Ew=⏽O4> 4)]N[Mi3r QyjYŴH^`Z$/p,('36ޘj>*5l_d/ivv\\Tً۩|u;=}1yNbӶӺ4x+2T9?ކdKmәb-yiZYOF? d.uեH;ML1نJ_VXhF#.mn%p9 nGpG ܙ;lV|X#k V npkCw< mw[NVf~NEc8RNc-7Ӄev];i/#*z k2 sb` df'M&^s8^,8bs2u.Ɍ}.v(_" C|g [aNہaa<pQc?xb^ϝԙ/B.]P†]~-Ҙlʍ쏉1{A̟y#` 2=^;Y Xd1|M޻gGO =/|Qy"WrDZ fЃ23Ʊa|'e弓1Ͽy@+Fw/c8?;̫KT1$Kvbֽd'Kvb4۲{W} ÷W<5n6Db.u ky_Xf- -FyE ;r}k<>갗{Ujy> ǫ({#^ E{2{g+9v<>g7vyvW|nyq!ӣmpKa_wn"iIE}smPhl~A Q4݃}TߢS]oY Qe)7P0²4e$!m='r[ ukE=4'Sɉkz>{\mq^Kbz4gp!m1}&K@s&쯰%]Eg]WtCHI3ٙ?%6 i5I Gj=/:q\7)cMk[^j~o˻wY0kD*=b1f R9v/YGzEx2۲΁rc8tE,k xmDac4c?x~ʲU~ۉdrFx5bVIr/Tm'n3H&L_W.[GE!b_-CW;D}V*QYU*;փCdE9TiN;a w|Jw|w|p.=g[ܡ{?zN UMO}y@OfjGϱ 2pO.. a}J3|pE?A8s*\Jy[pi!\۟MZ{*LU<;a-ٴ0nLme^ف߃MlDa2ؓ1|\s[c{̷ "gK9fL?1ECh|TĢt`r:/3`pZ_H#> ߹(O--x qF|;_lc_p,rdL̯dDyy>/nDZcm6Q3h,i6g~'<sgA'SdE1U. +c|m0U<(MR~J*_t%ar!rLWp_̻\βQ&_ eKaC|A5k?jY/f̏{*ryۺk2MQ/\ `Q}y c  X)۫ʗ;)ͩ{ٴc6ԉߏp[!{V9nǚ1u pžcL Z]sf&Dt0<ɮG+|ɌR]1cL3|~ٌ1{W %KJݛ!䗝& \4\ UUf++9喝c_aCyqI̋?c^fz8exLSO#9N՗ oq;nk{ڴ?ssA6U]c} NpPm }pg?H}c߾u)>pgRǤoFcN_?_1ckW̘\ CAWM?i _5}RU'ji\yɒNVJe FƉxνbמ1}R׍ÓE>\+ꪙk`:1i_5}|ռҳhd07UoqWZfU'|ɮnnj0_f񨫪fWM;隙aCygWjya8(}ބJ+ex+_ :1C2ƾ53|ޏ _q=Mf[e2|H̗-Ϗ_3{/4%QW<-U3Oô/k.z(a==cӯQeuDSV~˪_\VjEu.eoӞ۶m(>v Vq *\ѡCqQ]QrU5*7ُG>3gٿJv‹E{~Mh$!yJw,+cO6(MVj* nd?CG.7&sQI:0xv`c8zrY/73u )n)蔹nfm)P7|8asGCݾk`7/q:9&8♏^(  l1ҵ@p5.@iZLNluvDlYQ'`?qI "?¤`1^<(V">gJg2p>d?bspӹzyˉ<`Н9W3'cTP0;*<Ȍ;l=q{P7tyrwQ*YkY|׼G/nBGYHSWU[~Jxxn3W]e3!ڄd]}!O RK3e\W,أ,M_ReLݏe~xzRߣ~8Puӟ?Gr' )OvrYLg"Hi#63y֏[Ny=UXa[_a?|˚3y;9{a_P0>v:zJ pcYk_Z&oZ{l3G</(x6O39_}>Ųa/UXs` vw’wJ|y!{@"]Q _U[#?=Xٌa^$,nӱ D~Y'_٬' ͹TVKe5C$b=orI'.{Luyɘ\xpV֛\ȷk_3/D|K~ڇ䞊CT9%)iL~&3Z:,z7ȾwҔyYfP≦#Nb*(%܇:y1uR]мk9ի'J t{@_p?۱'<ͧ 0!!&整Q"g=N1# V :&QGχ*rp]D34)h O4$>f8eS|O-epO\JvJi綪1Ac`k.ŜC0|>z?w7`\ <1,O 'l_p_U4i?<,ȸ#rx(11 x5G1ޡ<Uc׀'0N4š8#) Ƹ<mb0xϞ<| Y/`1g/+ߕ 2V8 3^0.Z~juck^'qtMZx- |fmc|w0 x uqM[^ᜀHQ6ཌ+I=㱀38ޥ|G|+'{EX'g|qag$1no“̜ŀeNNI^S<7i~ ̜ҀÉa'\Ui_gS]dc<sӦm|WH;I.I8hMT *p4v&~x-?ŽuNVb_ /8/5}oX28%c Mx؏u Ӳ},? s0a_Y{oڀpd\iؾ<xw)?ov2c75/Zr䗍C: .ϼ`?Wy(WQ}%w#GtC&W5cSux dQ{TG!o2W0U8j}}·̓{}?zd<[:l;SW6㏼?Ux>_^W‘p#!?O;LOUvĻWNCo!ߐr~F-(oz)CהN 6o'?:廩_[@[~/J[9F>~'H,w??徚1O~ >8݇gi'|O*b#>:FI@Eh?z|t~ Cso;8q'xYn7Ƒ5y?}g+̓vFqS(7ߏ?!a8Cex$ ?/q.Ǹy>wO;*fv@ׯH']I'9'߈Ci-?'ηǸ^7' o"0or; W|pkgqOHwξß>|YO8IKֶTy<W~x'FIoSx]oq轣x8x}FweOoyVϬ~ye/ sqOۏ 'k+@P:#0@cıB.:#`]tF@Uv錀w36? :#`茀36?`:#`錀36?:#`C茀36?P:#`C錀36?0:#`茀36?p:#`錀36?:#`#茀͏36Cgl|:#`#錀͏36?H:#`xtFǣ36?>錀͏Ogl~|:#`tF'36?! 錀OHgl~B:#`DtF'36?1錀OLgl~b:#P+'36? I茀OJgl~R:#`dtF'36?茀w茀w茀ONgl~r:#`w錀w錀tF槠36?)茀OIgl~J:#`STtF槢36?茀OMgl~j:#`S4tF槡36? i茀OKgl~Z:#`ttF槣36?茀OOgl~z:#`(:#`茀͏36? tFg36?茀Hgl~F:#`3LtFg36?茀Lgl~f:#`3,tFg36? Y茀Jgl~V:#`ltFg36?茀Ngl~v:#`茀茀tF36?9茀Igl~N:#`s錀錀\tF36?茀Mgl~n:#`s^am Z1ocIxb]r ;ob }~b_}?K|]:ypi ?Y#Nx\.V };房 GŽ7Ɍ[/?c'?|X#)1qQ1c7e ck`318R:1*<71N 㤀1~ph$Ԁ0.UȲ*0. Cӓqg8+؟`K^c8'xgǴn8??h9v`{3/JNUǨ*cf }~Wep;ƍqϸ)ٌ?G2nҏ?iS[8~ſSrO18 Ÿb;Ÿ#ඊ3qSnaaߓ̯M3y}O| r3z"G`{3xd6 {cŦ\alG*7EXL rd ]+/cUɴHZ{iɴ  l2 b2ۍj2f=X球vin3sp{_O 8~a2bvoLJN}wL}􀿙LvF>4^%Lv??>a, 1owFҾ]wܷ|8OO$-|l2ۭ|b2m| gGa2Nܷ;)|Lve2ۅ.L}/#NVW'Ӿ]7&Ӿ]&Ӿ]-}ƀL}րM}^Rhcqn6?'ӾL}|]'Ӿ/'Ӿ;PvO}q.G #ܷNA]/۵2BvG}ŀL} N}X}ͧ]H즺s?c(?WPVorT>1C{~YT^3^84%a,Iw]12p0cD]bz]IM8ߓԲȎw,Y3Q?OH&x>SDޑmkjd_$ ^=f(M4gz3^6H^? ҪcIziL|ƤO4&}13>(PVnR*)O)/)HCeYi{x;_Z{``&=~ΦJk=+e:fI0q~L| cV:;-n>t'ғIzze۲yH]."ıdemn [2afH47H2ߓrK*e";8ǒn8p;92zMR|[wx<[;;Zkw LsqCLv)GgrfN&{|_I3{,~>J8F 3Bo2d&e=#3w [qvaEީ /)DҸjYc:ktlwc7k9n8r_z'iS:K"=wKf5e`ߜzY/铕yY6+W+49Q_%%ddP6KfgzL"f?wvݳGP냉q蕝b>XNa&yXU=*_g1`[Xs`w?-ͅkx30ӃyLL?쳒%L'L߶̬`f3|M⅙8cda׾: (^,|Ȱ~0nb>4)W"vyL&MxvWZQĤ"FQ9XWB;z9_Z=814,X(r{pݦ<@MQ3vWԄsLQYEM>+j{!DTV=b̗uJ(1 Hiyی06cQfػ*s9R5SܭX2MgidZ%J&I2K>Hݗ᷾2(&(ZSf-7U^~&82[н]+-]EqG|Gye7yw[CV\wqiһ/=Le7}qH~UqN*CF4I<8\}֔)s,?g_ T:vVbiex#G9^ߌÏJ(C}qW G\K[%-d+qiyx;7Y(_o¯5F+?lДa SMU2qx%L|%3MSr$bJvMg+`=Cb4TChѬg+F3zbqg.F4K]3 :i=SqZS3>/Nzƍⴞa4%h=h ZϨW3:I%hg+AWJzƫ4JY3 qlѳ$gL-IkKzơ[IZ+EJzFt)Zg+~+sb)Jť(M4=VKQ(Ei4iҔEJ{zͶ[/aIҤ~iMDJѤhʳєg)><Myv+,a Y ٲ 5 \TskJSeh0c ->%ҕzo7+KVQU֤Jl/kGQr0/g-(2ws74%0[ɏ˙IG10h4A*4!7q/ рZr^ɀS~qHwe2v>Y2:p#z: ;^(Rќ}!IlUbj /蛌XM^rp熿kU ԏg乍B\V)&_rq-+Ra\T?vNbEV%jrTa%jU붽vK3&R֊122(},/039_:o\p }Cw1'M :nL/ ng?qxwp .ǕCW6{[iCByQO&/qW2{eZ C>Z+\˻gRp8/bY%Qe/7$Vso5\4T0rFUB1xgzߪ^nҾlQ0Q})o_0|[ٜ8WoK]B _+KKE/LG* qYeUa_]{ѪRŷnKVUxU*2oʼEiĔϙg˚cNU۴=}_x U]^逋YUhEߋn9]m%r/w{od߂ux ǵeA֏a]ASз#΋LnОfό.C,.[*fu [,o۴Z*/*\Xj U.p9!U0[ʺٽ)˅c>@5G &!kM C5) jRzפ0Ia_uM ޚkR$CjQP>zgVcR^零9Wij`ZgE%X1g;lr(㖀3p-ƭ7c qqcpO8ga%'1H*g9Иrpڞqx=a86x/dp~3>6Àb#5#PN|3זqY314Ԛi 8K{v$z.åOy]uZsֿAJsӰctY_r^\-sevCx8}$X$Ppl z-T֓\cNW%) e\Myq)S:|mzN_1{ | x) (y/< N_ȢkxNJvT>|o֊+1R6د =O:V6v c 3pdz1qtw=k î떽¢Qy0j[=')goJ\{- P묌1=|uEzn7ViekX0꛵e8|!p6s;1K aM}z6Y+:ŷ,o1iAv+o։=ۘ#} 2{ ö_v=e>0tue*w;ߨO}#mQm7}`H1~#8 qX }4cȎઌ|5ay׺֥=ֳuڗ25кS]m@TP}ـ4qdtv"Cj6u ܞԐ*O׆۶[Sc-ei鏎4 UJuCsw&n#s3{#s3#s4ShwFN3C#sgO#PFc;Oɮjd¼y\?1B̋F{7T"s s1n;3 /7y}@]pUsKS[B]v[1ΝE׍5v9֘Sy>ݘMi=nw>k&T~oBT>фTjBqׄ&T7 M(7#r̽Xz0ҙ`^{tSs*WfkҔoD"0M[&D`WS:q M& 'I-LS0铱Il-#޷+҂tj[3ȇX8_\>.YN)E<܃1~bcp{"(s[8RƵ͸X/~I@#'L1W-x ݒʀ1w421^W2w`>S=j*NIL7KUg%=+{Yg~;c$ K0Ì8ܒua7`l7l.~~"~Æ+h\esNs:<[.䌗d{_\}"Dlඌ}%0f0AѶC>7Ovc 1~nc~[q\g\VU zrhEq)h֊qIANj><(hG2^V[٩lr 3-~ZQܣL9ѥ-p=]/kzK}ho繯潈ZUmj6g[{k~x1ϷEO,w#p^65Co,#i@ay<n09}Ax;Ov\p\dqkܛڞv[nkj 9)zԚml{sTmCsvmh1 9浡8lmcmC~nC~ކjK~jK~jK~jK~tjk$ym9mIH[+'2AH&nG2{BHfx;:d XvfY;3_`.Qyĵ+ ?|vǘrXY9HpLC\>/ ~;󛯧P/E47o~μÃ^oƵ#/kr:Fxg*9_6>J cSi=2OL<q^2n*t.?5 p甒>8(SiLQ=,H6u PzS{ꇄ,מW5>iܞ@jx/n܏3x*2>x9ダ71^Ƈ`|IG, ,'I{jlkOLhՎc5`5k;r[Yߪ 8G=SIg' @T*oc'J~l*O/|*1 [ⲱub88T*cP6ؔ񔱼?Տ|pdя0BX߽K0՗d ϏlbųN2Kp5?lTp|{}8n=~5HЧo@"~k6)ާHHV{?6"_S*-hq{nIOsZ )Y7c#}AOſ3ƹPe~>[mMUyc>5~JxէԾs  x̯AG7ˌ*3~֧t&2< ?Wd,$cwd\}%Wc$e\ebQ4c?9TLOM2%Qf>eSW&ksІ1P2d>6odM>dƋ+;N=#hZ쉰o:8s˔ iର]7ڶ` ?] '4 g,~,_j(3͛6ƷQ.xQgmNEԩ$q#cdw{|nwsne)\͸+0. yM5~gkdw[/:1Œ+ws );vRcS VWRGO*CϳY_ qgZo=vwL uLűlc 4+an=eMx|*cS^\5\W}uz+<^_ܐVcSia!Gȇeu8qc}Ābf.{}ס;y[59_Rq)Ż}}』;`6}Ւqn2~j&0ALy,n1?ǞAccqΥOkNL z3R:Rx^v1j% W+y/ko(7y gW?8#Ƹ,c |^?Fŕ3dlEnj-=f C𮼦k{~ xQNb ';er9`<1flkmOJkRqZb]dJ9-vIf縟6'|T_]C~U[WaSUrqޮo^qOơƈ ;xa `?_<]C?#c cp)`x$G1.x4RXGv—p1W{5)|K?>*|pntv,2k!k͜5SiYɄPybXzd=AT<Ǐ *fr6Yͬ.hl+*췒1lO k1<Ό^y2v> 4 gg nv3gh- NlZ8W՝xPwSw}K[^ݮhcx>nޮ50I.M n0X_t9 5]վwO٢N Yo^[c͜膊[,1afzKO%1-z>,G`k$.sMjKf%dL=>vܓx{U[q6^I7 c|1vG C\h Y٣B&eTAx ].Ka}|ΥuS}]G3NsщӶ.y*/t:/S鼜qދaK_ֶ{ss'US\}2 b6ՠ8ލރ4_vwW=}‡:&|oC%|7Г8cs1|}sYsZ 9CVǻ/+mzaO>COwcgk u>K' 5\9 ayIa >^{3O8w'cp)Ci8\lp^f-넘XҜqGw40l 5Pڗً>̵k_f)D72Y/nާy8{ad/s/bR/^ (_C82{Q?q̞~Ld] ̧ް}g3L Խy{у{xoZcʅJyظu `~fu}(1̱}LggY;̇|q7,g(> ߜ>})M4'~ ϫ$0,yO+__z-ƄqDk˳o/$|>Mكڭ :7(ުNw)~9Z8b)M<*Ⱦcn({|K۰Rse{ᵦ61rw{J{(>٬8`ū OTrhu}4q|}sCR8s7W w ?H3H3OH3H3H3H3㯂;r)N/RQ8Dq9Txť KAQ:8Dq5>m} A歃(08ޒ4uo}AMMXӷ oaLo!Lx0}۪`tn`fgU7bx`6յmǃT7"7R oPeBߠ*4=Uf}{T!ͩVCS]7oM#0gUC--`;0ۂyL+~˭փ6"(M1|[e 3V^ ̘ D07@ ,d%c;%.h_~c,ުI_|궊_WJo|r?y`7EaIkK,dr6O'̠ Sb~a7̫{wS_š> 75^0Zb`&N, Zf8xN}Wp=Crƭpwne4hWnb}x"7=q=ȑNk8׬0\& -ljn)́[af;|7Iohkwrxc3#;Z q=ء ](7"$d:b0´Atcٻ ~uV|tx*Kgf[]ÿ Bf֙rr8{:Ue~oG/J vF>S~}l1?g)g,}+q}.[E>/novP?R\Go}(4Yc뱄Q 33tygY#<0^62Z†% _BC>װ pNod| }= >Ϝ 3sg|cds͘VǴz1´+< Imin6i#^]vH'(t`?ey˻^${Kph{W#i^i-Mx&:SM}c1I8Uchbbc}K\90WIra90jErMdv\qj.`|*׏O|Zv~cb?~bq-/Qz1~—zy_]Q`By=0`J>qdՀ2Jeo3ƾd0(7193GQ]SM}_h-8F_;ڇ̷{5k.}wfGQL6P(3>ru^G~Qդ=oYCum>cs[l{42pxeゥ6| MZyz4OUZt;h5Etg mT_dc_X"!)xޒq8.8: );3s.mYwWV|s{a}ӼR>}< fW`π=Wph3~G=-ƣo#.7ɚ6hS9l#IМ(|p(QΘ{L$J'c2oIod(}\p;cATzSv9\->u,?ǦYz1eco1oCґLyQ#k{1i8?ɲ4:iĐٟ>3G |nݏ g ?eƶD|؞@cy Q=SNcIn%|GOuӮwsit "c/(4eҼ54K܂qtb6uw!0Jb1xXm؝}!{9?*[ / U+xo؎q3"cťIltpD14Rsc̉lw[lqoJ&ߏ&w/O^Ywy¿?|fulGi?#G10 |#&ccLk&waSO}0zqƕJp]E'hx>g¼u_V[~xgM7yOQ~6_;~x8}7=.o:*2qfqwo@NY;:Aq0x.}Bt}N:ެG|N=ޱq\mB)| C&}ڨoQf2xԛ1MӼDi<~9.cZWE&G*zl~es,8M%*M[__?R H7|oN'~RGlGӨ/~Kfkw $>$~_xut(p)NPeHnG.edx[²0G^R/2?# g?W0DN ^gvm"sMW+5U}Mu/n&mXKTURSds8m) 5^ 7 -`̗1m"6efDڗe@gơU|u8e̦oQ,Q$ރrG$*k9x&3(%g;cx!ǻX# En$`:wd~#&Q;96<6gb$2u >3Qgb^eB]?a:\ d#>[2#D?1O=1HȯLa23_]ZE=,?8<~g;;9fr _1XBDShYI9F|S(}Dˍ 'erGjܘrrWZp;8Un,%'a̛x 7e%kY~2Uo*g"?e5q=kqSISInVɝbwC7J9ȗd5gy$"ߟG;zu^XnȭSEGhd)tx(_;X~ʗM}z [>TS>'ֳQ߼imb8 N-f#4d<37Y~(/opKOf:6]fPgsy4Mc-w`0w9mU8\ze^oBxdx grߪ<''y{}< =>gp^vzC/1twюIM6y#|&'S$_]t8ż  L-r cÿv{0zv8y7#j1G?yY{V{W}|0ve#=Q8,a1?9ռK-g׀1?,4y;pu4C<O-` K o^ƃDgܼgYoM'׭zYfܓr60ye,Ha~MkCY. 3hmM"xoe7༼up甴}jw,:'qoyZa}/6gs{0@E7<&yf5:QzhH͍=~ Y4|u Г!SLGaaz玜[W/D3 N#,ya=I &^6ΡEށg>e-?);|JG3)ߘg޴SoTa(w8ĶAE G,{rBՓ{D^Lb^C /yߩB,X rMU|_)"'`W9*҇? [;,cH\ϼǏ:3ь!I{'p:尿E{=MX,.7]gК~^S`Xf$J`P2Qug 0:'){?ܤ a&J)oEfg@6!{Bj-BvZHesX]Le ^w3[_.xobYhJo\{ k]ÿC fyyr3"[Do4[D.j&,j/2ol\Dw-"\b]ny8ʏf)̛>jǀg8;4rNI Oms7ҿKJ:,X8vzfmzH7X,!f{HXBL7~We?fCnh?9;f,q|LC>mqGqiϼJpvςϩ܉%៓y{w7wH߁_I7Sz=Un, '|Ag`AœJduD:p@,Z4ӛ2)Q)4ٔ{TG41FOûg}?hEMeudWrYGpY/h>pR2xGu?`lSq,Dmwt/ 'u fP~qE+3edͧPa [1.mH\8 MR<%^jU]jAu9'c;UNgAQwQ?)|rhWMϻ`q+FYƹRk(?֍&-tX~Sa-%; ^s=jC=kM0X(]Kick7{(X~wvT,ǁǯ>[VKGxpbv : p>a6 \3M49ol Za#  xpBgR;vX6kʂ;5}+GrXv $=>!پ?yLo/8c8<ݻ"yp#z ~(XʧQ΋$u*'cw,X^Ͻ|%>WVIm52}kho?mUYy8.WқGg{3rÃr엳Τ_ڌ[8k飱|~tqs*iy =Q~{&G9qg1o۬3sVʟ o2adzxn x{Ir5 ۂ\aVnƓVZTN}y1}Sb1C;RyB{n6(W:+~Q]0[B.L qzy[u>Z߷\GR_)Ѫ/ovnS^~.*dXE㎂> !3<w)"b]tMS>(y@}XFm_UW<:0⡉c3͘b$J;9yKWQg"o'syq?}R}WR|ݫ_EOjc"3{lSk8cl+{--q~c~]EgSFn{Om u~jY ~5?3< _ 1YxM|Mκ=-@g5snNJ$!x~/n*kǾe\60N[n0G[bIt&]fRxpL O[I -wv^v[;BstsW_:WvckhjlfK:g\ݻƞcO~*>T| R3\,p%8enS?՘/8Vq^~wO~b󈅻?^-70P?z0?R5Oy} 1z-TL-B2טXjMEc2p?䵨)N֢bZCW8`݊54~Ij\=.z*SxKc?U_\C(˜u,9H{'g0V6+|}J(zP“pK(SW=v?Ǹ6K-3kϸNJ2oq~uޙݗcNqf21جd~`o`/SNu3{upl3̰x/gc&ó՞*6g\W*郯XWw5@]ݡ6.U93%gW]?GKC-Carao]uy}OdSg4o~Sgt?GU"\ԓoQO\ky9ߕO(ߧQ?y?.p}c]g p}v,w!'rg:CdV0k/i#0N6:w~T-.z]tuJkcyQj΂c,__#.5epU]w%6\;kMkS8o>++_>ͣ\wՎR1d塚qvg63Wlos{Ta{j О?Wa[gg{/hOя)_^%حawkoz~vw;q.1?{u^u׽l׽h.N8,&c,WIgwfYwgQݓzc]&}fu[[zrKR'=ד)\=RiϽzi zso;=i}zsֺ筧=/i}zc?O=Kgw?ZO so.mo@_n@~@=YߙP8HamlwlʋȍY6R^Hyd#y36RoHizx#靍(Mm4-Ҵ&Jӆ(MooDbŶ`}GMM67no#͖t3}-fs6fJқ) jlȶ!9敲2NvZ~aF_3د|; b3Tvopbw$lޓ7@)N~ܢ筭Go>3o&ټ-bıYG! =K*cA[hewmpyץ[>Y'b|XQt >"9c#Ro;21KƸvGp1>[TeRxKaG-D⳨[>ݗ)8EU;=~dFS=yl.ץ8ᛯ&qA~³\/ޢ)lya7 8̹VNRũ*em1JC}?bΆGˬ|z2. 걚sEmu`ae rNBW1ݒqcI5^Yƪ5R)J[/m4R`^`>6{Sm8V!í`ߚ`mx1RcSRk'~lrۀh3{0YK'zjl'TZ*OX55γԭT(^?gXvgٽCd0O{m;naȽJ8ƻr.5 Pg\r?8/Mfu<x,7c|`39:0GjGvl />SM=Af6ӎ3Xe<#볾Z{Ʃ(ĩ㳀]¶q|O: 2 uMLOM%߁찝x߾KfF~,NJTւ]!8X!4sUvZr㷫oB.缪u3Rovs'{Jq$0,+TV^ݜ#7aY{`ԫB޳dnl>st!qc|Qg;b]]& Aaou#[{.{+-q(`="к9 IvP} rYw˸ "ʼnk;MF6e>,oPbȲAwoﶎwsȑf7aqQYo|_1{~Tue‡Mg+1aYAA 0O͢8 f.V8tgWW8`cuG?p<79)v߆`yBmTa8oNô;'Y>M *9¢I3CT杺*ot6,gS?,_}Uf懔`ysQ)X' 2F;I+ s x qΌMLh3wǃx|Vv3\+p@cw8Z˔C͠ʃ(lra+vqmeޕc5I<)H 'P8I48_`e.YqItwI+Ki ' CZEsvњLƤ1JOu 7l2RsGO6, ge,{Luzj8AߘouExSx|W`}omO}}C.#oLۉN/Dc cyuݴ.7ܹUQ8¹~ߑKC8ͳw(MoM(i5oxA=Z=F=Snz-O95J" Uwj( ΐX[bʬwAz3:Ƴ{%D/a*ྙq=o|ĉǙ̯-@9&~mm7u fSx6o8㯾ʇ OZ;{O$+We7sшuMCƸ/'nci !j?ݦY'ދm9388sZFOSG9^|>ܖH1׻mV&tc2c6{_v|_f vr7gx2XW>.}E1D $X= ))ҷ<`P1 &㩀{z8M>#n ؾ m~mfo=j+70X9=O^቟oU,#Q7pC)==P޵Ǽdq;=ǬI+p h0^IvISG{$b\/ çCl},e`)/;}$Nkr~5"lpX1^XW{%w7{XUڳ`{OIʷ3M8z002={w<,k1=o!??T>H{GM p U@17ca0><wEp/פuU0r_ҧ6Ms@'x^#X>/';>p#Y{~Azu{`>Y<-g3t!=\jTT\Ksh9=֍h?߄VN0.hDfnBu)ۄ,;1_?g8wt ׋3r?%wqAR8>jc )La^5*l3W|]1T:ƨtİẑo1MV~Sx4gތ=b- G$C}*M=)kWJctk$#hq_9ǎ_5 /P1e'ݕw=۠CjمfB1G?dha$5{a?Tg8\Sm Od[AFF)ۡlxXG~@u%wK8o|~G^}AOW֭d,kY,cwa~ 7v []QQxܕ}>G9~:p?%ΒO7}oڽΖ9Iyh {,y75ҏP;yA.9@ Wq:9@ /s9Ȱt2A:Y }A69H ;s}9aD0s9̜`\`~&>x } 70q|`ǂ!`*fA0+@cz`B7hu(3=?P&g'&瞁Y̤Tz~0+caծRG}v6mL#T_;3a>b)>W'{#P}}P}p#T#R}NwsTQϣzԅessڑFrn{9sG)m9#9u8à=cva'_d`i[ccT19cY~W{?F7 ,c{U;F^{{m12^x.spMaB;f=f9.aoaKt( Q= ㇀1~%čx q_Hqn׃%9{i}!=y˜49ͱ>4/~ ym8mTq~8'0t1 "G>k"ڀg3f>ÏɻIU)s8O8ap*Yb;~q.+ow£jw*7~p o8یNDSy(ȼ'SWv1f phM6j.[[~6F]ڨ6*IjrvIv'wu'=~Άv6)7;Eht6P{^(W5} @rm 1o)K,SDZ\w㭧;Ny_d s^>n무j Lag2S&P '~(s+c GIӼ2+FnЛ4ݳ[ Vպfa 0H81 e>l}~I~U9mڬIi"m>Ec3W#Ҟc#<Qq1إES~>pӦ^Px>_*'s+\F wSx_)_K ?ɷ]c.;F@2 xmf3$#,eG=ғD\2̡uw-F֐3T&hj63>Cmэ3T;hg-pڢg-|ڢ&g-,ER[4,EKR[,9ܕM?=ueɬʕ1#4+.t~x'{t<Z9f~#Ʋ1ߑǴ`M8܆>;s 7AabΡ" G0Ts2.dљg pmvNc؞ #c _F{G96[Za^^ϔRծRi]T[_䦞3w sXsX6sXbћ8#@y'ߤx@n"H IJX%h'k£yÿ?`TCRsK,4&ҙk(“yeLXrH=#rY.Ke=w0Na?KCy~b^/UthKY>>,yyl?/" x M_hyCqq$.?,@\VGYy#Wg ~@w35Glg?ȕv ILx1}a_(~`5iY K_hyg֞JJr~>1W3WoBwݗ7,{eϲ_}}'Riv'7 x È7SgN~+?tݬ k!\7/}xt `E>?,ُ̅[2TZ|H);δq'g?W>_0oһF)fu̦[(\ϕ{o$E Ob˩m).ļ θUd&}pGc~of6r1y;i-ᐴǼ][C8|3.eBu|=Ĝv=.R{ژgwZ=Sղբ-_!@K~泲8%:7-dįbk.spK<~hxEa_-ds._2Ns7KU4 G_"y$qykn?\2"wUfPKt&&o]6ol %a%eڿqq7ls]iOـE_Ε[\xfkes~Qmȯ1^NX{e<={w\{U‘W =wM?BW%._.V^(0'ҚaDzz{g/7:2dw%Ͻ%.2`~kğG̯o?sk??/3J/1]s-O+_Nl` ͬg6^`}&ECt&}{s-"8tz1U‡> Tާ Wg>O V'Q \:Acܐ 2G+Zg1y:ug^6_88>Nd{_Cf(q/Mlǵ 3JC'8]޷epGS=d}G5az*,ǺA=㯫8`|8Ff46tŹ+$ As0 M\===RE| 9zc;W|ҏk *M\J v/ U8J^7d^Y3ӹ3qv/try~z>m2?gneGJ"o{5.zn,k _C^KTƋ{pr31L{%3@ /TkwϲLy\3:Ƽi**ۗ#,` mUqϥb@.g ٙ7xn{۬ [Z)xLcҜ1]_?׬ ɚ,6DcT9ygsy'vsҾLriqSL_jܦۼ}ִNߦu)slRo;.X|g0C~Lwh+ee*]^1Ll/x['oG+aaxWK`f|v㘷 ֦dgYx?ޡ|s<^i`g=] |:n_Ӽb]?+?A_UCY-k8]n~*/:k ~_~?L}hؓ7$|m: Rx0off-<|8m`N,w;GOxd?u1Ȼ =gׇ{(; + ~K߆.35ya=!ՙK9J3ﱣo7}ʏ&yq\r= rCoc弖 }svo!Z8g} `x@u@up?<\r8b)^sȿDHΚgbM؛yyndKM#mwl7ig8{0?|?Fz'P1|O1ߘx2x,^yݓ+uH;yHERw]I>9/-~ѓCSgTYOy\Xñ̏,X;#{Y؎ff ٢]qܲN=4:Bq߳_¬4V%ő#) zdڧď}ڧ}ȿQzZ=2sV,wNcѹ io=u5],{(Q8z[3t+4Ϧ2i|^ƼvBf=r?%G ^ OGO|m0ӎ4reU\Yv/ˍ]yO߰;h˪ko`*}@ UZ?{1Ļ^LOOxWC+b~-a\1ޖz!"ꪰtW}e.;^2>R=&1*G>7rC]Џʏɬ&wue~3GzzI0'zyoc2r=1i 0_&n1Tb p|out<|wVz1] xr<i}U3)za9zj[툿2R)ϑ&P &_*,}C=k̋S==Xn$(Ujeҧj(|/tv2L88,*0o&M8?[.~{SoG1rF4ދ ۼ}a< I.s7]:!z#}3O#)@xJ2NdMm+:#s^{o*喃\3߾*<>}H2*EtR$okQ`z':>q<}|u1SF^d<}xfǢ;sʫωS}Cv8iySVx |o/๹OEQ˺ Z=G>l'>߷tYw% Я o7rhbufGsܡׇC#^0N1 / 9xx ylfd G}$'~ۙyf GN,} ]V?Src<.CĻg /#x?ڋp+ЖyU]@38 ~:eܼ].3)#0KWK3jE8)ۊk׆z'gyAph=8Iv,ę3bS_ 8|č/;8Γ5pY߉.~t/b.͘0O^;~{ s%g_w.uG/i$ݱTJoXOÀ"I9 -W&_yů۷0rx Ɋ1JNsN^%'S#dYߔ cC~㐷{:l?p_nX>g GL7QO`< βX޶~jTx+KavքZ[䥬e-Om5xrA##^7U6o'Mc%LrPcal wi_5kw.W. 8_ =Ȗ|m?+̺ks؝n|{mgb>ַ{!>=){5)I$yzw]U<.?bg]]$CQ6o~M=ƸL`z'̯ r6* ~6n^j}?'j;tcYS\yu}'e*;~K^h[2pX}^[i*TI}z? ط!o>2mbjLl 3`J?+($p>ETts\c#wX#La^GYi5 q{Gi[yG'od~X/99n?'f!7#c2mr]=c`Jns8Cp<ࢾ։2'G7Y?uÙ? S~wk|m?3BCT}`cT~\ѷޣ7hGtLμ{$^Ji[ &,r"WOcqSpdC;8A̱Kb8Y72. -wGz< P9 :(~;. #&tV0YS0}0ǃC/k0ow`s5 00ӁJ22߇~`.?RTg3es7{:Q683x8S73ui]2 pdpUGXu3ƺڍ1 F X01oT?A H+p#!OZ(o. CͪB_kh? eLKKOΧL}a'ܳ]d/CLbO f{ 1zן!`\$O2$ O04qxrC)vpq_CC;{H/quR^W3W*J /Qijü qON#W2܆Z0̜/]=#=0=GF};71&gց*9eR9 -OLXNgSoL/(?,P~ުz?q3(h~v0eoiaa /.pla vޑ.k1b:8={wx?*|Y?tc,s`ad7SGzp`o,60k?:r:+Ȟ5}iE2>)vs~?Gb7amcU,n'ppꩫ sA>]]l0vlNf-tr/ a |>}'1,o#pj+,) 1p}N <4iaV9LoGOyEN "h^y_˛n155hE}r$wS<ޱL.iOѯHΏ80`;W|j;lop\^G߱\vf1l}O.k Hp#'S]>j>/?'yp)tj,3'uF:{tvnA؎zKl_.~1ɻn< 8;io͏j 7 &\ =Ms"Q )ot NxBy7w  WGRd ~g8˟$k'㠷'F&mm|O㿃#zUv~ͳ|2ғf/'M76=Llo:%9PK%0"38֢3kT<Ż >\h˴ a絋 b[#m}ęڣho!lf ]U`ʶn#9 *b(=IܳH0\x-fi[qs,64(asG u]C{V#a[7oϞ߾gH=_p~ɊQ# ^2^vܓoNPu2³y>BppU>gwRŴ !T[_,Ҹ?B?lNfS#rfV +O"/y nb(of:8!nTz7:eb#aw&e A7•3H9wM~:N{.34aw}!>Okdm^'Q]"N.bI0NaY d&5x\cs7M9xnCnnqe~~ye5\@W8<`gu" 1w6f$n sxSCyMKŌbS[q,0y(Q]S$Ln·l:γ~O)} [#L?Cݛ+~?f)6 (i-c#6sgݎ2t=jpcNJC_ǣrQS/axb/z~XW՞zIF*,6IDsT2&}%U&<}}3UOwVC`A#S翥/r3oIl} oTS ?T8Qs'1ᨑTx!RL4uM̛*yqR޵μ B$Ijs2:.9[RysTⱩwaN=1wNŝrw(|\J6v_] pw%6KQqQW?6?y'?L`ȿx_o!zǾzVų!x%C*U\P_c"{ASU;B1hSy՟LM+m^g2`.MvR2OK:?X=sYߠ]bkH7cƱbc:w0Oq̃sKWmY/o4l;u'{QGC}F)v?7/]`?nhG? =~OḼ=YBҗ70UHa][<޽oyΛѷ/(cTG#0[G ƿ}>\0yҖ O%s Sdrrm7u˼[ˢ' )xh-?_wK8x!jI܃ ,HerGm~k6Qc q5޵,TK3LN'Xe,!yE)w^8 \e%. rZ'7Rb3y%Ew26 B2*zmy}o0ܪrӅya75]r6_,3x@,ooZ|0|r rY.]QC_:0Mz[_|y{S\rKkK>T~oRA K r0ׅpxzU^7鿰 ΖJח 66=0gVmޔּQǧٯ}) []5zg=?fO kſs1?HaμL gT8Q*?(wnVg9} *{d\MIᅤ|}?)Y>nrhrc|매5ƏXG> dBʽrET9r7e 932X>+maRw,G;wyPƂ\YGH8e+2~ OEHyyXߵx:;p.UU|ԋ*)_嫫r~7y ^*ΰ^3SDp8VOo&ʍET_ѿ? ct[Lt\c}A=fr&ו;ʇޣ\b7 P$^irr2#Fky^[{T{ޑKᾦc{'j zڰ_~ש~IcxضP"tue]uh7{: Gf{og``ǧg}I<Τ6k,qm?7=E72a*cX`9!eMj5+;xQm!ʥg7Q[N}T[wjDZC[P]N FOT3A==Umv.7I íXC %%X~cئ|iϦTg4G9p[ #p]p%s^,7?;fX]ƭ"uB#/.,@ɕP>m4MLcGn%[)e[p,.9gPXR~MI߰o0e9e]q1%_W89| 86r Xn[ *2-X~ ~仳W*M4_;U?ܑyd=~=6:ʜ̷/Kr]0okן}۾'\KWD6w)z$|nWN?_U,O;;TF0O!pEa'c^MspO)˟Pe qpZ'Nx7ɘwx.0d].{P[>7>*xle=TZ=h?3C cxymv /;W .~c//+uWQ1l{PqǤ~L& `~-UXf׿,C>h.fzP65Pi.N1$dEvo>p=931jHZή/ò/*Yy ,^6/z:ђÁc>"󶠈Y -ȺK08Zu[H5ؕ8We$r$v]hhȵB}/wr'i'1{foz^AOO˗SgNs׏W(Wֵ2X˔o77>wyC`܀UV/вjGyu!r]c\!|`zo2׽ox >%|,gk-|,W-X1[bos{&}d^900{U>Fpxv^V-!陖yUxҀY$;Oe*`z?⸻#fk^{{+,8nޖCI<蘭/dsdzUҹeQ 4Nk߰y lNk/،e;x>{oϋgN2g?|!+2WE (ct3g\Rˎ>3,MUGxfz7BPqx"e3][&5*= {xwXjQY ?9 -mU$.wY'(<&,N79:b:|+160/VX!2N09VXwƼ{F#}j[9x.YT5DH[VJ)ԃ8ެ|Q:N)"I! OVة+ҵy FaL*%yS&9̼dsq-itݓ *`t$eNl W’or9ۃ3  +# rƹ%a 0<()ݯ:k9џ9}k68h&zܹe@Q=JK>3o?׏~5d|G=m3o %m1+1NEH/Q|?>/9 @So0>g lw׸7:nQc}2:;\ށl}?{u4/_|}׼s9f|Qxc|?[n[dWvect7?2~,Ae;ϋԽŲ>ldwDHX D$ (I.9 IXrDIE(`zjA}sOWWo+VSb_.iӃ/]Ą@8'L[I kOvBJxN1OTWL%m'D/7fb9zc׳; K#ϫ}/ݶ͎ ƶ7;Αߨ>[Yx^pr"4/xTߏpXQk.Jا(Jg/2wF-J,Jo+JP})&fD{zNퟪE]`XMܡna[0񭓎`\݊o;.ݢ&$3L0'u)`B(Ji{sQw?Z.zc;NmEͼF/SNYq;S97o |x/vܵ1 U]9^sw4𷀿C^eVX82N8pJC_Q&̡Txn*bȜg<_Pe[1T>ֲG26VAbYَ̼{5};d9ŨNR#嬻̫ˑSQu ߩKfhET$`HuP]dgDh‰Ur~Vm=wrUi0 k2~K(FiN%s ܻA,F?y>G ̅< eOYU i?c\+zf\߸WS1Q&,o%sPʽܿyép?}lG]^Yn2[Na>?Nn]jϟFeԣxlx'dΣ4,orcg5Ikt?2rKzϲ c|3WK &{T%'/AFJ,Y{y0/ tfRGgT2+YʡOrg(-%nO-AㇹVg rg9Mysx^vr O_QK`l= s} ֋z6a6d9X7:RV|NoYVș49&|"_yz$_z9y$Շ%+I%)JtI\gKz).EKtj)+Eåh|zOCJ7JRi4>VƧK@i/U2[mѾsJ)1o%bJS[YtWƗ( DU%JY=H;ҽ&pf~ffm ]H39cH<8V2*L;.T2)ko}巁)ۮ>uaPu调4Rr[:VO/9>ߦo3ؾvHuKt8,5%%i QNWUo(ʾMenpUZy*ݕ}eSWck6e6s͹2TR4"]le ~k(Qa7@\k䊼{WrÙD0ۂ%={ ߖ,~"Oo3eg[ e8JeaC۷%w 3e[srs4 l?Q񀳖sBZ{E.8p\9JD=|hdts!q}<ưwe88=ܣ q0nx`܇<%!l^ǂދcƉuH|8IpJIrѾʙ=Zīt4==!NTyg,Ozbzf)oLOPA/'=bU~?om!mx&Nݾ=Cm%N`=p9<8b>~7wF3<4*<ƸFxK=|T`k4wI# `\SdE15h Eev9s׿\n8JYfWZ-+Ch2)+ h|)wGYG>2\Wlr dI)w?_Y>edLGaZ.R~[㑴:)&1=J2ϯ2ɻL+,YQ1sSy (u ^,ⶇꂱVoo{_5.?d7}*c/ߣfl^C+Q'La:g{+1~qKKSOp ɦ8gL]u疕*eJfMcߛwlvN)g82OxfyO!1v.e8.3,_ x pͥ>K%Թ7fwWrVYQ ORq^OVx1+9O-~]~H[* |ݦS )*|R OPx ^ݹ-ȗ`'\KgAW_Xn[5)p Jk. K0j63#u{임\Ǒ2.3Z&Cr|g^ROsݓ[r.W_oVSSE{[^)_Z𗵊/].$mlY% Ӳ ϤJ3+9s{2xe~jxֵ+$Lߊ/X|x/,}$Jua3 7o{\fދ潗)֫UMy?Y2 <̓s^++ͪ׿$`DU6L\L6ng1˯zI}݆͠-Jyd3^3WV´s-~__V#i#?U#ث9VT-e~2Ru4h\m3W=+=DވFkPE}/m4nm5 k]~W5f^|n9>^  , ϝo>̃;D2^D*_U:ʯk'xۙ%OT2km=żOWCu^Dus?#yu޷Nc\i,[orJ2sj15qުN4ϛPySN{a{e~Ou3O8\ݬ^l4O893!^ߓ/H ?U 7[ ړ+\ìוܕeƨז nPdgԠF2Y֪6;kPAis͍OɺD{K(i0>ޗC||5)5)jR<4tD>od?"̽į7靠\LjXCInʔ;Uk֬iлR$1ۆ8o%Й.<)SÚtȵ=j;NZ+ih8o;9;~`n?T8(V M0XΛe t_a)ޛ F&e UMw-CqPH-*CikQ](PBZTjԢZ- ]kQ]ՅI~.Ex!ʡ dܫ>lc_QEUoE F8QEՕ8AZ.yyWL&G N{^,1uq$PX_" X{ZBޚm:Q&m$m$m╺kH>s20 X&D>[O@ ㋀k3NH8JeHB|=3SI:Cd  OF~$bbo?4kwkmS{kS:]TMu*թgP*PT:T*FlC$P1)0yڼ_o2 |_=#Ќ zo;/8#YbºZ{@g͝׷x睞 ;Z Rx#X3:%$k}4c&2o⡻.d ʼ̓Cu(Op~.u`Ս/Oc~* ߗ3KYgl$pP;ѭ/S]s0%P,䕫۰WY "Gs1GJ0פ4[f̠ĿoJ'G 5EQ`ܵ TKoS;߇w{%o\] SN%YoD+K Ct ĿüȫGmA;rW+1w@ӗw3者JR½ KuNFo#x3ys4?<s3]ڐy-Epc|puEgr=G,@=GԣG=jORBIW}mKvѯP۳hyHEMU=Nomy6*"1XaY~ϰ_[Ft;C?Jg):?`JO#zF'zw:g6]V/zeWU:<__+*JN?ݳ`?"|}4XFx҇McEh?|J7Lԧk }Ja˻yli% \ on<%J_||o}y0OΈHCs/s )K݀xy>{ݙ߀۹gA0^ ԰\ 9݃0!8ٌ\Dagb hYhn:bhӜ5F0/7o+NY_^vSO3 ׊ɂRp5oRr^ eҨ8mZ Y~[qJv8/(g3Q<-ui[2Y}N8TSƒNأ0f B]2b\M ~ciH8’ ('`{ +M0& 610/? /E1gYҐu ѐR!8R_ff0 "s^ xIq*Endֲs)Ոوl8s&42`AFѲë33{YNmIrӱ{#T3g' :>1c[P Qe%Bìz/ > ·m 鿛ycU _GRq>'<_Զ~&~ӭ,o0ʼ念YMfs>?Ӄe31ٙ?{ U GB)kf}iS{Exߚ F1 X>`_6Pw=)I<_ ސ4mʼSV Ϩ-O胑׏*w,mM7Jְ/&/1;h/Jak]E=0 {5wWG{W{Pj5kL_Sc[{3EhMu*[OxN ݶ@QwU?g?_XrL^!+CQd` olօe}ӯӖT ~1{XXEl'_Tz{4vf]C6_xBZs>gS8S#bϋ[*&}Q| sXZ~ 7wڌZ ׵TꔄyӮ"_7!/~-+>k7)/&yV5z?4V;ߩ}f'Չ-Qx{%A k#0g=M;mDaʻ x1ޔ ?"IVԿv0=5xAZՔ9AVWRI\%-Z:ŗf=l*C'{C&07gƟSR{?h[S _SX.GGhS?w&|NC,.e^ۚfp+SEfjso>yq4_bdO~ka;v tfx}o=cl׉ޕxuN劉4|z߇8_A?j (p>:Oetqha#'43*%T O{7[^%7 _]nM󳛜?0?kGy0iCx!M -9yy62oa!d\PyKz)Ǥ7o|ӭ-[n!|gaJ+_EP毶4歵juA9N/ '2o#N1Wn77o|u+YnOiVު-*(>Ȕd=dv?-C',&wͧ'p,mC8>/>M{/ںovN_ uR[snV[RN%q$7c d'>๖Qm}Z3;\CQƱ>Ԗi&2nKo_fY#u*}|) 0 8g47v򛂻Uyأ:ih%e{nR$Mu4tO9N=oGL^Q%cyGX^(Gw[nZ{~gY}>/%y xq1|W X+ro˱'lXhE tv%;/3Gv*S:R}H_vtw ܝuDԳG;sGG/9va k# Uw;BOܟPTrDw;?p~Uc&hβ~o^hU^X/>D_~\xRޟoy7UpdқR _ާ=ާ}vUw{ʸ6=~ƃa>{)s{ZٞƤӋStƩJ{R=Mә\t߸s{oܣ====}|jC*Y{NhO^#T]\u)z: Գ~o'{rJ`**=o楶yvOkKzc^zt0{x@KTϰ4Yݽ+E|S"Kzt(= EˆxHՈ,I*3Z/n:Pb|wӉu=eW"8H1}ɔr*Q=>+`Me~n@sMos˲S_ &E'B5 1/L/s*lJ]eE& ދ£dy:?9㲐F;RƱG7v&3ȡ\c3 .s)aES{9\ٯJfgũ ù0ҟE'{eK/q_/:ű';WxGGyw[@qlkuMwfe%l vމWiPR{C' 9XixEٽr/ܫ{sqX8`a$Zdz{+}r{Eշ5Wߍt[v*]~ro)ް'{GK/qo]+aJXՔ M0Niͬ;?nEX/ u 0/_HWks›Y\~8]fYuCo)³.d~71;?HQP_A'7j6ԩs?̪G{۴xOo]^*qOL R.^ȢL̛$37a |˹1Ta;^kNN4΋LfͭоSL΋&7/cg\pՕٙP3s)s\v69 h̹8 (/Ɓ3TZȞ;1Y< _h,?5aU+ߞ󇪼!84AwGIfb%:e}#ۅv״WZ~qGZ= G`6eZ+m57ޖ[4@$nǓ!;dXL;'}Ue"y۬yIJxvl)ȼT /*E ޥq>U-2{Ut_ewNf\-o^?fVڠܑe*]wؚ[H2|yG}Afvꧤr2󏫺} 9^nr~$x| 'Krxb?c+If9?ޢ]Jz^`aERX*PX')At2Jpdד] Puڍ` +/lLRzxg[TVw;0P9N~ܛЇq]7@:e ӷ摝m?d}Frt 7+FG8kW눼q? Wt Qmc ~}qf1߻ФA :o:  0#Xy)R7*Mv`}76[A_9(Ƹ8Vwn3?&NS~?b~ Z<~SNҍ)Ya]/AsK'1J2/m]d޲m(S/F7|Wy,gZHo*p ͎зt6{E@W[,>?& uMdR?yu,=p@!3n< y·̯os<7f~,09vz3 _i8*]JH3knȻ̼o<߷-TSwڗy~xk'附yTd7.ؽ.韅 POR3Kt U"{ x*ʾ:3Շ{GEp}73]JUZH`Ǽ*2O 0?xW2T<֮W7ʳ`6P﹊4Fs3eSo7x_ێ)s/xLprI#z2 =IO:wדΝғwt{O:w2's'{ҹ=tFO:w{O:w;ڋ ^mhC/7!(vo0'}Y gPܷ]%i>P%B{\C q/5Z_UZN3j.|Nq "l;"=OS 7+ ( h۠s7`·'3j`eY[{Q7|OgB5rϐ=nv8KbS0?so? O-8 8WC]w+OdHطz@a7SY+:SmzJXo<ÅP}gf[Tc}ڻ<оԿߦٟ;~uTO7e.w|Ǽ9!/j7{Cg{p? ^Sw8hٽw{TT7}Up=C3UaXRv?MYkXn8wy&+r\1?˻PAT(9Ǹ%;.),$ky=ߛ%77~olo9rW9 l7"w+!rα(GDZV#_M A8k1H> *,e/̻ QX6=PyݥI^/g-KywG5pܿg(}=ü߁Z6<-?? ByX}hغ{!Õr'н]dto4B2aʷۊߨQ2/OK^$r~)FcvP.%^VOOo,?-Y'yIB{KT[27 }=*"eaٜ_8K8Nƒ[~g~e+,i eLt~vIc4נ=XNV˽_*w=>'{={.Zݗ܏5wXmg_d!o8=]" ;|r8BSJ~o8#e=*[G}d7;+>Up]YYշ_W3"lw{ڭB)|zu8{Twn^5ge}d5iKV=.c. Έ`8] cޣEqߚ@e'=Hso׀LA W$ =EF?L}`7 }DH y>>e^V}stG7ɋƆoӜ.{>.|;t|eb~?oO8?dQuUR+ qӧxWJo?>@?OcG>1{'OIߤ$ c1Ǔ0> M'em%g&`y :)qMGgb;޻iFʠI^f 1/+EU%eՖ+ƻ?ep [V0_5?,˿u?,':g}r$_I 1\zTaq^{O0L#%5>wƣ+۸M_(ɚI=ةU7B.kL1ucb q22w._nv>٦Y7n}?p k?j<1/*rv}^!2&-Nҙ1C6L4{Ě`!8߫w xGaKZ;4WwJc[g)ҞI_E^N}ekpiSggMl?:^z 4߼dIcՋ^Da b&O:8ڭyDqhVhhǏG+S)0ˆ]n!T_s !?ŇCB~{:~eCB~Ch6PگAG2 #Cc<nj>x8Gx$N_C=AMz 3ݼT4нwVe^jTL 8r 9(ݱ71-p\;N9 I ؃msPİ y}Oyy;1t?.\"jwy 13ohWDݲ։ܲfAwQSxSx^*~Q6[F\,c _vx67cH1'$'{*]nw~ j !b߰ ,:ɘ82 @ {/&C;JOt~"Fu_ܧU_$ű}e';9w:cd$w} /#i=ώmHZHWƏ?K Q. sdY(oS~U@':u?NKKb>Rcԡ(c,|l_Ӳ{:ַ(vC+M ">ʗaf|F( f¶i}geQXҕz}lN(yBɬyGdz.1td ʲ'hty21(z0z!1Jg/3@)ܮ7My ی6tOƃ@!CzOG7 GmhwԯBqu ƅIlcbӺ5|-':T10 gYL3&ɺ2c1W_ct9|ɯcL8L+G~g#x䇏c4Ns|ky|isEwKPZr/&{܋M:wf_k<:nozE8`qp*ezSnoa- b}\o>1L9֤yf<b=e=AG#U/.;o=W X+o0W+} ~y.͊/({z8q+3.>wXJ?\qLֱ4ׯNVD+ JؿhrT:ہfX\K(?|%z]ey08~[V%5|h,4ΜQxE7|| W\^ cW^K"sybc>ι'A5+Ψ]{ k ~0+^c_yo\KDOgg~=ot>xMxş=ޤ٭[3㿿a^s)b^ k|#eG k J嬄-[ШbAF%txٽ`stE)hf::Lߧ fG%:-E>%D]D:]O֦R3m+%i/9uWi7cEvii) K;>wd^%y>Y߃&5[b n75iDuvn1?~q`UV'~13M/cIp+|Hpo?~ d:+΅If?(Kg3'0> X2o0_D6OcCzz$RM>+d Ov|Sa2o' sߚLpNaiydZKLk|&ftzr,:N/(2:鎟wMg?>{LzFT},Lk72o/צBg 3<יa^l8 8J0ʆD x{^=-XN:#8[;It8 0oR䈽3)rJ8xc3L13eNT̟ejUXjb){|hϝBk8OY)crWO']^ElYL7rߟnţJe_a]f%+h-kJaȠp. +\mL g<_njY}g$ҟq9G?&^~I%&_{%ӖsyJ2CvO^Iɗ2D^wf7x*LMLw"gӃ5 *7(2- S g"WE28H>+ݛJ3xV =}y=KOd^~ywh; O6Ec/~a^a6aW'Le~%aߙ%?X5^Y V8M/b lSW?7^Lbٽ+C5ep#wH- U8Pod^-+%\\3._ǪwS̫ď39f<~o9FV9OMeɝ ożf Ӕed ;C˼g皴2הEip'ߟ{.I|s戤oBJ>o ?\A^t̛?6q.!9x&c=ݹ1drZ%#f{\[~1߭]}fS=L<3d_OYĤJW[J7w?T/wb9D*~_V6*A",,EmX`?A݋Xm:ve]9߽,3z4Xdg,^S}[{"g7xo<=`6)!wL[0@Ynpcư k'opVU㗛74oeC[T~*.5jt˫bgo1BYmp~+ooncQ}8q ackͶ4A-aiI `JUotsV4Z?h`pocNhևpL[3}kݚʼg9+d_w`#d<pSg8'ctK1^[5c!*%3|)Xcl8w5q3l )(< kLjagʝ] a|k0Z_PmY:}~ ӕʿҹxu ;hi'0g}TE\XkTdI;]գwL<ZS}nF\|vR^pi8.zvj3m^* ϵꕼbe WXsɷQrn8F Lld~hBB"5헣 ډfl4ü~8?Nv)$녏[5ÝSS>6܏|X1B ?6tؼYZZǦͯq[cmn KW'ǧJ??{wDZ?b|;:Hp424iRik4}wѾW;1^ 4w pmV˶-W|qᝥ蟃ݥ(獏H`RGOjTJ}kWeI{U}ug/BۗW]XN^,7O8QX2T}pS|W eT*Ð5|GyMOtYz6фQJ᪉Zë{ԕyE!JDS' 0 Zuty!kon DOpU!{O>_ap3^12"ĭ## w/w4g぀2x970x'㡀3s8!ˌG47t8f<&> s4p!51}:K8Cn8p{Ɖ{xZXV.ڀ/\0?pe_B|cZ2R-\DG7́:> ^o5wNJ{Bh0ƹ(7{|>}Sz)OmK.)T`_msuz{1Ѽx}AN5Էb8z?o9F`U{5blWBmh! _n:miѾsAs}\"NWNns1Fdcl'qX 4RƘcbesƘ_c:c9@u31O3iV?jt1\Tw)Vܮcnm*1Ue[K3+G CA oZkL{ ~ ڗ?yL.?`vtv;c\CclnyT\wDED>͢?=?c?6ȧE>k$7"?%o_?HuutrlM܆1mGƘ] KζxZ!*;)Qߐ Ѹ!ۇJL~K֘߭nw7@u=o2o#|l7pmw #&i{m{&CaYLJq޿19\"︌q0Lm!^7ᐩ[Uw2uu!SWPq?soz30~7-}+ǀ:>c%q>.>}Wa.v*h_ z,_8zz[఩Ez[c)V.x d.SmG?`zm<$|˨=b~o2j۰|W3qa'LYF} ۪O[F r0ևԀ/3g- L9Jr &v;ֺNcE'g,{j?Sgzt~]͍M-gP݆ЭC pk\R |V1O3A:(<)eeuRx~$mzԧB9*3|_]~}6zGiDd|HƱ9fʡ>]yVcv`iG 7T§ԵYxs^?o«gWxK_{Ԍ w1q5glqL(mB0N]eOI{{+( p1{%1s%u9ˎѼxqEJ,~s8\Y/_}W3[|vpZ]uœVW2-O8~r66Q br9p_9¢'~kv?mCg-wɏ5YN-+DBx̻`7wyY\ss{suu:ĝ4>x܌!0/d+] )yī+_V TF_SoX표2oݗ\ZuE%n{q=yG :$5.=Bp X^r{ =O}_P{̏a~$kgid ^[o#ݷbGrrܰ{-rc)K::O`>`^徲Qƶ%99YWz9mRgқ v>Ee8p2]ٓf_ ם3#noK~>pbIW*VqpҬվ}Ҭ9iO5'M{9i/%Ҩtˮ#oZF9$s!rS2/|7ťM9%` ܎2yK܋+⃦-eª굖*}nߴ)e-pMJX ң0^xa-˼O+oۜ+XKֽQ_dV{ nܾ⚠yw1Vډ ҦSJ'lð 3,qqujM,ȟߊU)LhJ{)~Ra0UKg+ɼ,-ev㖍/)=Ǽ6V uZżvOYD9{yV&eh4?Hu%z:ݭ/ioc'{jۃ/M[.{m9e>-UCU[.:}2nOnS`)3-ǻ5cB6dg#^ ̄}<3)v57Dh)j񛺣NQv,‘>1N[`?̚OfY-PL{^N9-<zέ߿UEJƘ_Ϩ}ҼjTV_Mc~U9h'uW&p)%]0 拜wo9k2R޷ߙ}Ճ ?7>>Ɔ8S<+xr{߸gmX1.~:ݵ~ȝ0aE3΀2Nc&XKy <nj^Og{'88P9zVGuW293QsfL]S7?G1ݞOIq8N)cϙ1l1j8zcaAGOx%ũ|  KD߱3zmk7k*/}Mf}\e9X,rUڊ>jkDm*.ZC _SxoWx+pfSM2Z%z*>{@;8u0݇]? vYeWa]1' 05By=}[>Ommo>a[gE> ɨ)ԛi߷^ s.OzeO_-ߘRp$Q7W_cc,k 7ܘo8?ঌ;dq1~[0 9㺀_e|6yq doW:3EV fS1ӆ{!U*7/W/Ryˠ32zI?+{BEh_Ql >d2uM) ׳=g:c|3gxN>;‰Ap̺ ߙu:h:h UVd@= {trvi_ +273֛g!g"9| cX_DXTN%.ϜD5/b,ԁw~@>,',ǁrZW;"Kdx w>)ux6ؗ@>ീK#_=h^ *mooNkH,dK q#M X dm_"88pZuYw#߅9̩ 9W-{u,}oBM]=AearV}ߖc3>}B ͫsߛ37.߿Ma6I'h'6q z+>|/\6sᲗypU?znνH6ld{>ѓuF=J*O1;IZʑ1n%1}j1}&i^6ȑ~|?Kxv_3.s; Ƙw0/8n^6wWyq(/LW=A\6KWL;_Ycix28֨Wbpe.~'wLPou|0a-B㾼RWL~ Sfp^>8=pF;W$sIlS_ϋTs'{SJ0-^5c6GW;iIS]n\5iz`\E#I0e9ӝ ^ +x$Sl[=!.bjL[ҫ>/6 ͫ]52wtqD=Sc|rC'I! ;YF|An|A~Ҭrd"ldOeF0}6eIe,Obn6e9ǔ wY7:m-1F}&6o_8$Y0O>|W uXw3VqQDH^QI7瘷if3^ӛ>eeAUCrY~9L\])F9^cڋo7HK~f!1y5@e8ϲJ4}xITx6m},f,g %M ㅩMb} Ύuה,?I|{*le)V7o YxLS̛wM?bZg?Ȓ̟i'-­μٖ\'oΰ|vߜa9u`9}s)7^~?RO4.8C~hdWW]4&Bz ՅryH{Uouh8~O`=)lKu,0: Cr|h~ΐ{7!n\ߚ+M2^q5'9UTUU#z1oe1N_VuMyN=r_꠼oɺ'ˇw|Skdd<"rkb?Þ^|3‹7C{e DdKޮ Q;ΐ9~7pkڝyܙ$ɫ2xtt L9cKM+pqo*y~?L?QCsxxg ^7kod}–>73 a >:^``#s m8`'|c߉$0Qvq-Ƹsӕ~o^H8i.gbo>mb/v#7b.lyI]"ތ~=yn!;;;Z,7/ ) x77қ,rVce7t `/'Ʒ9("3P1K45>xc%c^ו{=KωJR%gW+SJ=Ki%6{MH#U3pZI:c{\rRY~Qr8YSrrZd! 1-ĔC_8~+RO=WO-Z<Η6uN1dQ!&.Pl^~{!(7>w)Ta;r ,' ɬH.yrd}E"d9yYN%Gcrd c!Mb(jҰtɗS;VP-CMZIDL,gWR3+M:guZ9F@gKúTGw1VI.`ߴo%CRQR_Xik/,aqJzJ>?s|80/W n 8%+={)=gzc0F35m!2p0x0^9%뚊1^2v8u.s:#x ݊aEo7:n҆D: A#MځJJl7HP\x >x/ -eG_d|w>?|qx*㴀1:/3 c_77[>o3cg<( +;a{vZPw /L:L휳ȟZ/֚|~W{$17pO `s ,Q1OJLJ+i,w㔋gsf'^n%\4*Ʒw'k~2Xd:֞:{;QeM't8~S=cxU|}6eiΘDE2D:c|f!Q- eZDzqa Ƹ|&coGG{jqe^/FtƼ|YW=*e=??=]ֆk~h?zmW,t^C(+p:/ߍGbeSz\IS#@nDv{C;ρ{aƸW!ƄTwPXSw<. [M4;WxbL]umdLqGp;Ƽ8OyO}پT6a^+\<] qTRw-ɚxWTze<-IO{b"F)+?rj61 yVG>Ie!eM8( Zy1^نfnf5xj?lw#0YdQ a[+wa\<\;@s!ڪuYcv混^6Cu|O@~Dɉ&r+UTg fVI޲V! x=g9OR^V_H~ _ƸZ߾ 44=& #:O7NJ9f09!,( CMi(qs98aiiޜ#S9[Fi)}H8 1-s^QmA85gp5c6g7r{(e111RZcٚ4aGiż4rrp`;-IRsʿ5 xEZSW5s1v)tI^ʺʩ(g*<;7lee Z0[cRgDW QaCґ\DN=r34<_;oyQB{_Dߝa߽y{r3&w;c[qwP>XPy nQ)V)W?9Ie8 Q8(FGUae!LIgԟsՔ._~DԳJ.rώJ=3'|}?oJKZߌwǾKMҢ Gg;-C[ɴDz6?}gl_&Z1OvP)x^c×X;x?*>u⿅8 ۓUZ/_q2d1(}J̫'e[ڼ̫ڗA.Os(m軀3]lWD1ża̓1^Jc@kޟ;o-uv`(PRe9a}jwJc94od)cci)13p5-3DMV^lr~ck]ebT v4w>1p-Yڊ61quX< ִ;z⌍z"o$XVScM4o?~tN-Y.Y]Bk̖ @6oPh/QݗYc/zvfފ <@F<%b| v)w֤ Ǽؚ->_˺Fw1Ng_jw;`Sw+ò6cv@75{6QyNgr/ lk*[L䎕^Nci4t#)7el[>}S0f wj䠰[䠰堰pzXvy")snko!$ PqWʷ#rR sBޖV`~ 89izo01cyX.|ɶ1N|172pMq>wAwU$i302X?w xf\tM\ \fy~.suM.shO.u.s'ƘNnSN͸YƘ1O&?:G&?C8./Zܼ_ݷ0>چ3j"߲I;OrS x~6wjV6;rS;)սc}s}r,wsuZiQ_rSioD;)sKy}]>'g*ī^I0kg9s _E} ASXǴ'P[zm i߱\Cw̱,j*˷92g.^\X5o`|q>/ ^]>MUmU/Z"ri+:Uw]AޘyWvK9̿f{ߒv<߰Dݲx/" ưu:2~\tO/-'#?b?[i&߱o/_v :}| c!=M88qpğ2߳G{47~T˟bvKf!џ>d?b8lH_ ƕҬ RHJOL^?<#JߴڱQϤ<96ZɼJ.ޱ)@m gm_Mlk(5OO]y9$5x?֓x˕]W?}}T2aG)7wL_ZmX^1UP ^J_'}+S3)^R#14fzZyd.3œwU8eAo ˬxWx% GV[cJkqqp]2q4Na26Aρ_\.2Va cbޫ)5Tw/;Nf[]_[gꏷ=X?%.%iQ4 noEe`-v=oC;5c_,aVeh^=GNpWQb1C1⍴3v8Ř7xn^RmVbiP1*7A%O2e# ElTqY5+̾|)qxp87 Y!UnZH;%YVYo /;>v.Zמy̼/cZ 魯{{hz2Yr\xPW-8sXIW֏{>a,apa0e0cK(uklHWe`=+aEKu{a67h1'ÂeFI]ZGۖy}jh$GkE^U{$[[yv?hwӄy-|WۍGr/fI%ڹ;nKxd9W5]+ֲ?roG8YK')7ik+M=?d#}n5.mPڞ~2uC7*`̻Mywom_G~i0;ə.Mc6_~[3G/pSr*Qit=Y_%|ZeAi^~$//3D~&mZi&kp;:~sdvP}"|v6>—,cb^MZY2Obѻ _Y馰N[DHU26*|Ȓ--I[H-Xq2?,%~+/ʗ4 &͘uד#{g`n3 _/#}e> 2PX19ytHʑ '>f-o׶hރ o7M7Vme~ e_geM:RDNhyL[k//{ [:xOww0z{q+Kpۣ)mwD7N.p5[)W /3s5}6tGs6hW93?oEs1οdx+MLڻIWnb+M wSGpi-vf 9ѾrC}k:cT=s#(˩r(|JNY i#eqPf+5XkEJk{~5umhlyMz ug~sGq~_HkE4RJt9{if4oUrߗuTr5vւ 9R=䧋ܭD:=D^{xW~iƪϢ9H`>k}e7#ahy݇FlbV%<ü.{p*7HZyOMˏU=>upcq[>/P?vg޹[ڪЗ,gz,+h3x5|A3Y̏ KY@-vӂoJGɘo5e:5ѽͼAxZ7UU⍶Llݽ v qj}c\H-5cŨ?S&߉<OjּD߱Vk^jf+<R-w>sCy?Rm~.5Fޤj4G|/lfg~pf@[D KxzH혷8ηQDZ[3oꮔO7>aޚdd ه䟻@a-)Vy ;W 3^Gp {sZw^La%󶫴۴ݴk׃2oSy0oտNC9[}{S S_"jp񚴗eU;yP%9^&GASW<;yRqtȊ ʘO05`=?\d_fW~/ŏg /Фes%}SӔkW,s:_3 2e&M-7)\U [+py;h`Es9; kT=3.|Jz׵=\E*ט#-Yҧ٘1N5fWgkӘ=_m +Y?Mz~{Ԧ]\Q2ŏ}g g!_TXȑoZ:H:YٌuCk d~ n,g#IWN:48?G K-]5X9*ꙓ8$|e?\6U7_|ߍI]ל:DCxXy {9kO+u͚ou=]CG9/yUGcFW{Y< 'cy<}sqDI58O¹VWEROvU:^=ZM`^ =*=էy`|}Z\f>qOOk7:ӚG摧ym/s |zK*ۀک|u2n@),_Gw!/m;+#˫v.*C`iD96*k~m`΋H01Nuw+ptɷN*)*ZIܤsR_V|4_!{)q̯$켒|O8ҽnj Yʺ'HUد} )K#'Jž/e%u\'gayM,=DĿ oDGeFGh)ڈxܓMF6κ6M{I(=lU+r_ H8Yr㾔ۀǍp19qiG#lL_LJ :3[Dc/b| }ݔNh/f^&R$K봷nY0_ײixG|Jy1Gvœg 7151wk2Mx n;6SηcVY {⭲d[ƼA@m9҄ʴwaV%ywI_p印:u&i} +lJFYý 37zW0Kqs[y aqֈy<ޛMx'[|.à̟͜jwI/̛2,Gy,=j]SM#k`ԏ̟7?یͳ (wgqoc}GΖguojF\64|߳yK__=ȟm}ڂׁ9iO27ha:)ˑ߂u 2~e-)i4^߷dd d~ſ7 h _ ћ O{cyj}oDS.Z>W} ZxMet#9y1}[N{O7,ﺠߍ\/)w7THZⴒs@ˬC@JY@dnj|ȗo*9(?]Kc煖#EgP2Qs-Korn=ƛ^ȘVN=щ-slxt 1oO5oIo̾/} )})}*}~t>@CzU~'|GF) :dR:H} ꀸc6^pA-o%鰭W09[O[C|eOzK|ZhȗoX_E t_޽|%̤U&Ҿc#PR{uG}4i|uTV 7`hTf3.ܔ9,gm¶pS~uat~0/zλL2^0ߥ>RQO=)cS;ԳRPg~?>{GqiW`ehK&#>[]ª4)ӊ$ 8Ʃ8b^heAtt 2 <-''㢹`-+߰ŸیG8wo]-3ywxg</~3x*?`<Cƫx G?;㳀b|!ÓqZas2.ø*pX#7H-'apc-u+nUSʜYdG쓵6,;xxKrִT53lM̯}[ִ<53/jM̛[>ִ|53hM̿}Tmh9:[_ o}' &ӡmi`s Xw}A0?&~mCqL6}8o[J|-e0Ҹ0q?lhۘ'zYX~FÃ؇{o4緬MC=򴶴Z0~.uK (L*0GɌ*dqzQAOf,=_{Y8 K>g$lG 2{VWՉ  a4Ff6?].,, >G#)ا b-Q_|be7*YďyU+̫8[(Wa zS==#`ƕ̯'}șBgvz*<4e53ڼk<+@M)4 t 쯱-,m5qo29vu<,/SoS'>}#[u+_'+au~;e_41cBJW|AovIWSz~mٟ>*ӢͿNWʸӑxXi&B֑N;pȼO䋼b{;Olmu^GgsN8JVOڷ- 3Ros'/g/+宪̱tzpoD>Gʙh[1o35gA-A glo%3.N&}ySS?WgIq{{v+TeH;2bY9_y#oRfLl;~h9{f[mp0$\g{s2yl4^X++ݙ>9ayzgs }*ˬgə.TB9n ?trgk`STd,Rf;S˭1W9^vWZe% ɷts/߅fs:N|nQ8uZoYpQ.fI.w1/|C{7&gDXo~4yJ MۤOtUgBuYm.t)+PT{w]Myz$rZovbݖ~.` J<0oU%]7%1ʿ#V:ѕ߹{܏{޷ەޢ?MޢՎoI?Xv"HZՓ;DiK7U_NwV<m}m[췀]1vF?q,m0a?V,޳~$qwǡ3':{Svz3]7tT}J07kV]o0/[V6]77F;VZ7f[ݞ,hby=ͥN$dMu5u(;5֒꿥MntrͿo_x铡;nk(%*~s.A?Cs߷>/~;'1ϳi<{=Bw;[t/*l=|O$ ɭ8IpGTr3v|n0̫;ӃX!SwU9e+܃Hivd}W=sw{p9r]w{}MN{.]ظy$|Qa7T\|;ǶWl~[ y3~{rD֪zYN!D}s_rWV^wb {PFkiz8cW9zQB/WJglk14mjՋ)|y# X;=o2-`‘T5>ˬ)8~,Wz`?s8}e^ш|ekvTz rڗ)ޡPZհ;JPe^-HJW;1Jk _`Wey c^C ynj"{#Pt lys,s_U %5 p%cǟMi>ͣ l^cmWުg1HtOo>t1m7ߏpdWoYO~l 븼{E=w'@: 0l0%kw+nvxo({Z;=5/QoMJ9ؽ#ӑ2}xp{}MU}.obs'Z:g}0_SXdq߯}iӏxðb:c1N2h;ݳ->`lvooGeOܥ\aɪ,{X']7j_Zn'cV[$,,w 7k)wדgVޜ?YX#4q^eNm?ϲ'rI?v_ GQZ5HWRĔ&&eX$Iia܏{7X[&I|ܷs;J{o7dv+ޚ/c]>[c﬍dOL'ElD{l7Plʛ{*ȷ۫÷l~O3?lj B]/(;m.{~h=yYy<=ݏ[)$ Sܛ~ C7X|Ou^m(,aeQ}’v1UVqPk'jgq?Yv*?0am}`YѴ{v7AY7_cpfɒi'Kod7 ?)+}2_'eo΁szȎ`>sٻǤ6}xm[Y{N&1nМJB8Dw#cFrpF3ȍMڿl$C>xLfp΢T0dJi1Np 03ԹH¸DB=80z/a.~;ICP~Ÿg#>F'SWm4e̖,D~G)x@*+*~'UNos㸯q-uwT~~;sIQbM˫8sHΛRy_cATk 2 :7;xh̓hM :7 :7t0}n0>;5NLf sswӹ٘!tn6:7[m7й١C!tnv:7{iJSҹ*Con3~0sJfyátn60:7[l`3;ij_kys'/\<`3?_Y#b,&ޑ;_s3, n0`, o`38GR`vO,| Q1NL_0˂YLhn`wpϟ0Lv9`V3ttSX0Y ̗fU0G`9Qi]m{0[1TޞC*oPy{{ c-Cm*oPyi cK;mRc)̙c)̍c)RR8 38 8 }4*=ĭ8WVk_닑Hv{7jr^Ļڦpkӵ۷`G}8_ߌ;6M8YM'+tsrp ˀ5vOe^)M.(*j2(6@ҰCcxzz<}Zv¸KZaǎ'{,/]"ΣC0B lLhhtEBq^A>i#K.at󘆀^ t46י{v~cDՄ ]n+|xf7J/?¹ l7@ߙMWY>2[Kq> n|LyT!jݡŻF]u ۝q!4z?'CpXK=}4`{Rt 7z\qkw,c{>q]Rk̿O3p+8:A~/DJpL-{^$z^t+?>>&8>'*31$/"$#s6'xAdnd($#߆+l?[M2];h la;K9e,ON9G{82Mǟ[Gʫd+/pM]zF/qF:[["ǩ}Ix3C?j)u J1u Y9Xgsb{c!1n_!)QV ÅlElin3~1A13L2i9~H]ږX#'&sLTLciՆ;&и01-d?s4ܡb> ~Q2O=yƉ0o|@9\i<x'Sm*[7p bAc9q'8?[1ICF8͐ 7GgBq|'*eƉ?BqcN^R8Bݒ6du w;i {;KPHLf d1k%KܡBœQOq%P(@`+|7֨u61e[G`"r*^I?ŝ{bKnLw87R~!ʥǤ;L16Cw#Czomt{ `jv)W4JOS۴*#X~$^Ce5w^1ŬbQ$*^5vYm P'ѱӅLb}ޜ <7yv;&pʣCģ=NOIq!&%RAwTs_g?k{Sw ̮Azi< 5c4snRL Yg?}cMr47^n16OwxiT~dra,6QSAv.ו!+!ɫ!k~x&s& \߄M-R~35Cq qqn]зwz1CX¦1JZ0k'j-p4:r}r c F^R}T|agSlI1#ә~40 hcZ_/(?߶5V6g[F]Qh+iep~_QXOv x$u١/e}qY1h,sOVtLC.ރ*C${+!VZl(-LIϭqgS;l6NlnvcSSLܣ$豐gD?巅8W?̼V> .kIڏbs8.x-9WT/k6G&m~dE?O3 1py7M*?J̡xܫKwƏ?bLwWctmJqNw̡qά9fnfȟsmcas;%\1ޟQ\?*s|~ڇ͐@q &5N}ط5Ywc888p{5<מ 1ϵnFT=*-;깉ב ȇ-:Do~:#WK%gqesLtxlϢ×zO>ff 4濝-oǧ<͜G9/I^`:'i?׉ts>=N0?OeՍ90aN`Oc~f\IL\/0?"6P{ȼcU~Ξn+89!wkb,<|جʛ!(0VҺ VR#JjC|i:I 70c:y_]pF5q!S?CgP:|$WfW ϗF<_7ࢷT-7f:t: ʃ3HOAaZf4\3IO0*sIhDq{J5)7FJ،g3K88;}󙴶Gꑽ>Ӽ.#bUJ"|-Tc@n*G$6\[K v/n}!2aȾ5p6v/J06e__=gQ?pV{2zU͇bÂJ|fAVk{r\c>Wr䃸Oa*"2v [fgZr󳨭ܟEm%d62JVޘM:{I4VNM9Φ;fy˭t~=edCKϡKPtCg\͡SMs9t~aspt~\^Kmt~x\oΥ#sչt~8h2GkΣq7"0y݋8>|?Fނù7iaڌg2w4<*,*Χ2k3ߜ]>|3~>Y~dz9߂(~O(Qcy1rpqpqHP}F$77XI(x˴DiJ%on9Ղ_8/i7Ϫ,$[/$?^HdO Y qP!71_ !G <[' 1xE҅gPܯxdzw[,a81A)x }9pwN+}n0zɜor{;Ʊgߕ{iM^Bߘ,fy Dy׭ܟ8ؽμa^|{1ΙNXVR?"ctƛC#.Iȭh~]UgAKq=LUFԬIXg/3c[rAįtv[Jd[)}/"gJO~Gᾀ+/5XR|΀r}1=+}1>w_XƺTj)OS 2y**{_-v)˼݀K3%\kk~7;պˮfc *௸Fpo$Y:\iV]m=RCBQ^nRb!՗d˨uXF'Hb.#}eo/\Feoo^Feo_XFeƧH_Nv|@{|eXnν-z͹N͹Sk3C y{xb7YL04L%0c9 ]ϗvq~%Vp Ӌ r 2aН1}迂)HC\?a׫Q\TG!  Sxcv+YYoW4,om!~p毡93oRևm?&aľ~v#O+}ouZ0}0o+9Joًy}3] A+ZÏ+Iw5mC_G:q|(\oc *̂F#gn=3+'4WEY .jrK}CNq/;e`+~Ly8[_rzqw#ߘ!~g]yvߣd~`jUreWH3o| ~O>{Yv? xޖݏ9/}ʮs 8̈>cO-J8Ldf6a2nC)SMb\YiSj_DZg pZIv1~3,\9ƋWӸ_0|Ez+]+-+N*|N3 U<&브˼w9/'yTm ox$;\c8 RYovsK2312VyTh >Tƭ07Ƿ\q|μ'g?(<[Uk|0G;א$@ڀ)Wrx(쯎H%/%][ib!QFض+5hC*E \a)Jd])Kr`?`푿yHY_f4p>w)x)יd_G=O9XWx7YL}w5։32pg\uYC~[ Y,* 3w]ue Ty^']T:u^`pN%%eG嵮 /&)^Y;Es[c3| '+,C漤WH=ּ._]͸hzEw[ 'zz|JEJ5O#i*,qh%&2(?ΟyuUmuǼj*?w;74f^mL+\[qZ$z~$eGe32emĈ7,7˩a*O$铄(:/2J0g#~fybQ9G,!=G:[aY,*n;׌.C\|8 ! >XE~s%e!-*ZcWC}  l`yyݯpCfa[XF6T[ ~{35[cU-ǽ.^O~]xݱ֎vmTM~+ ?cw; wMdi{ۡ$rtJB^nM~h?>^W[s7 { &] f-\/.Og{8y. .Ϩ==U@ OVnLvǾb\wzs>SNWql|~XLꜥ9QG߈N~'r{Ls_vH/7W1 Q0<65 us n eo#}8Ui<ܛzawR1oa9y٘=8H+JL 9rymA"sXqqgM6 ;GMFrdtO7hN#Rp.JJ  AE9} fMtapQds9qP<&}o}śx*ƻǏ$}<^1[8?q;+ p#< 7|xx  D}\.=}9*eL #e.Gt 5=Ro,9d`w7=P+R߰dWe'c^˚~m2{_Y-e wM9 S%с(]T $⚈uc%2H"N_b_¯h?M~EFW|ͻf.Oھfoٿ߷Lk ď#ڽ[ƇoN.YS/c/_$?|Ivru% ޞU9mq%;}Kw~һW0JP||v_d~ avJc/!k+*;?Js%y??5sfi!s1^\sK`7mY3ԺïkWTWS&) Rx34*@ SxsyVOQc ϷS|&buIN~V=\r[kܫ ϟ,m< 8qͼ3]ɱ̗DE3/89\z{yy)OsF!syW~$QvUy=}x lbs40/'<41/#_1/麀N0/J'{N=yyZtiѣ0??*'yr )w9V9{r/Ҧ%mYW\\Jb^eظ^Ӭ̼J.]ٽ,ag[?0{VSvvviەCSG4r+c_MkFNlGsv=KpbW:,{"vwyeby\P> _LX_EߔRɂ/RB!aKccS/B Ow(?:HI7~GcxhpF_=]grRϛd.PJxV^a4={M׷dqߞck^Z,Hf|^&/Ǫ=Y|!sK,q'qzǶ,J:R#3f3⩴ϜsW W}Γ!FLt'/Pw:UQ(Lo> og/}lqc[޷d$8Wc<*r Pn,G$^)muޅxz%G6oǗ5;` <Ƿq޲cW0%UIY(]T_^+z}̓Q7-z =4 ݫtO)?k{;= m;hAs A ,9)x)W-ns/U4}D恭,, sS=[N$} 9KӃQ޷P+IΐCtJ8V?(_x}A/*|VᛪRmcr`8WSy<[QlushCTv~ưg&Y~ְ/>}CtCt80/>L a_0/r;La_aIILLL3x@06<|:iqz[io-׃G;Y㕗<߫_#&Gw Srwoy|˲ʃwP|t>#ˎлD=r&y6ٯϙ~8jJ]]"ny2wռ?~M6}Ws|՝_}CH} 7hNo<=h|\\fGeyû 8Q^"-a7v_Vn ]5?߄ 2߆sWmt 2Q}xL5$gI#m(pZMx6b9W1>81ʼ1{]X~7ԙ6~2Q~ y2[Tf|8&+.xߪ\p010a_@FN.!,ȇuZC}oQVYzӨȇ9p,\'|h+A>Dxo,?S8(]S!p^e7A>Z!p+(ouuqC[!k 9ʛXC8L8o+pmo S8S2~ MNP?sCOзpmp< g|3>x ~vNe2F`[>oANvҼs}HXѓs$}IJܓ4m?i;ni;.i;qi;Fi;SoIx/ec6)ik()3Eis\)*A&.a~|ʤHp OdKdKCS(^w6jfrldn)&><7?}Zc鳢8K}љ{|Y[`q kάU?0)z7uˌӃՔmwc\iNGYYdgC~qiS{lPA>McOXCșfICҵ:D~U|v(N@ ݳ)RԀ=mο|d^1_b~tڴM84ŧ}&Ν6c8Cw3{W;C[go}ԣr{m n'7e3Nr9ߜ1w8>s5>~yKS~nq/,$]tq1\MS܅Ȕ p 'N Lڼƺk $d,~\%@֟\e!(T[v׼ I3ſ^uF)3< 곜T#=U7{p2[X:~6W[k+O= RJۭվ~k9n?Uio' Ӄ^53t7Z^,5T;`7YxXeUVOrY>M\[d]G"[к[*0u{l91/1vjWGfYwP|nND9*OL.dF{G{6WRy(u7>SGΑ&}%}}]OdC]vsy_'OoCa kU30o(=oA':hetx|\+z }-z r<6<;/qQZ@=5tM֔qx) CƸ1HF=bB:? TxuŎHk4ح8s~ .*"<)shOe w߹/D.Po}{9/< 9q f^Xѵ}nս@b3qݐo2\ {hn8}{}aaﯹ@FI(51DžD^$k9.Fy r rhY'k5T,Vh[Ծ}ûZCq؀a<8{"ǸL1z3Uݜ4YXϱ(+.UKxxv@/u9ql-0xQQLg.>]dy-.x:HHyTf*c|eU/闢k/B4]7C#mh}^=xɞB:v9ȼr~a,vdaf|#YpFpMӔ.UzHce Ӯz@2ĸ]4+*9D MX^wm%zmnzo?E0ߍ$qMFcɡ\NLv_( ex26|E>!߳|)=OE ME=|{ /]a{eZ2[D_uWh+P [4BmPp&.ŬsbQWL:Od7]1RXo\1u=W̺+f]8*q tq+8X~ƸGSُvI&O]5yU'un!f=o~Hc=ɶ`n 0kH= 3D]/阻orškҖSdq@|9pd L0 Ngw~?TN(w*7[O˟e8cd+G2;ƉQ 7~b8fl5j?ª*3D ^ìUށYE}2F9Op[:_i>:)ĿU0]+&Aa2& ywJ4WSQCވeFʝ>X !ɠ|o`J0}av_IgP?zXv/5߲C<7kй UU/?uZ z: p/5G7L ~ >+$.;9'FBs}y"R^i߁;lm31\VI4`GrpZNqxe"sc`cuMZ#7w.A2pߤp-SmmbA0.-rOA {z5ovȀem˼LqȥHiEݐ[Ùs;㑗/>͘6QȾƘnn¸up 07r9`V.}[sc2߿X8  gkګw)ObY*k)]'ki'xU}5ꚱ]M *\U卨un3'϶+*BJk)_Qwܱl߼Kؽ!h3$$voÔAvopqt=r*{ ݣpތOucV {q$iO{&G@[n[5똟^̋S\ v?3eo9Unk3eQnGw?'J7ǫŲRc :꧴u& -'u-gy?U }!g_'2IC{Ьe{P¹TW?S}{?G;_KgAv5!YPeI@2 o=PZ~xx>__po3`lڕ?~?Y͚xNaH%|1|wUAw=r_ WdSc|>1w#r 8#Z{/ؗg=AV!3AmU9Z{#^\'0oğ ls @~=2ߡnwK?c͡qGdV\J̵wb!_2;-iid&kid xc9u(Գ筥k_w/x$,W^úmG/]tV7C=Qt>u:̅pB'Ŏ*Y}k)IV?=;k8JV=mJɂqIx}S;q'1֙=V'0bG=I4T8uyޟFYQyuّ/0x<Rۨhѷ ǎKߜ@8'O[d)2NWVy<xq N2{529nUuu[GLНy9Vu.mpxY/Õuww]v?-k"Ƽ'sȝC(ә4@2_Yż8{NTh/G׌e'?hϓFgb\Cw wy瀗 : aܤ8f01r\^]UF3-:g - _n*3^VZn+ -ɼG'1o/d-Lzl)p5+ܒR}$swֻ,k>י t{(V㢭FzT~=c͉+ Ryϖԧd^%W Gؿīt`##PZk{uؽ#}ܱ c^}>DʇzF*} 7 ovYW#.e^ =T~v+oƿiN11^}- l% q:x@=-8dq?X!q~?GaV0Ap/:̳Y};{s<<8plew w ]$o;] xLc^ҶMSM^<|b=!Oh{|G]vw7;/j6s@|smaC_Ks.r8C!0y\&.#csIk{PWxc[*-eaQsA}If=ѧ/ి|VaR[>Ѧ]7~tn㧰-e2jeO1}Zik=fO[Gjvq}, sFJ˷"sP:jsӘ p9d|p wx1Tqw#{^?k 欫4<ŷ;*b_"yy0qƟ}7)v?h=v?_Η^SrD{,\a~[SQ>qdݹ Ѿ7*{*o SUPZ@n#N|/ϲRdrĽ)GR'Ӗ#b)~=8rb,}W; _U\) pywſMo;iPgR{ڄR.yʼgp>{Sv{!:wj`S=D*2?l xeCS9ّDGsD>k 3Vp&r\(yŲc s.`Wm 0 a3}E0W 3}E0Wϓd=#mYto)ݠ_=;"@MkfL9 =F%e?sO#WE{'d팱?ٯ0c._=:uHe,=_{ZGR8/bPf)N{0>}%c-x"c1,[xiG??Iiee׿_ rUs+At[)ű#z{rTN._܇9,[9P *s3iVY)9k9npỲKǼmvOMGZD6SS[WΫp *\@WΣpYT8+^#cU&l6s'hT~cad7b+{ÏcvCa;)zX'_>!Q^wx?)Ŀ ^_;Ӂu #q w|(qx =&߳xL^TP?Xyȋh6gGځ}%;{^\'°F}sp:œq%WBmNfgxWa{+))a6:>67z0d@{}*>T2O0Ay%7tWD]8Vey^5vu[G0G;s-: fmF4wf#ן/7!?G;3~w7w>!`BOxpecEX~ |@Qn Y?|Apz$]u`0Fؾ0_]\efwO"9} z: ry 9y8dawtkH'J({|%p>N|d"9Üa.Ykia2n3X7 Bxp|:o+Ƿ;Fu;ʞOY\ĸ¢ooYI &5-"y8FɸhScS]D87k03~1M᥎Y'xV:8K^Կ<}:D][Zcc\)_=mkGS;D怦IVnd~=#oDϼ-4J=P/yLb%'!›M}͢hZo]]*,*\Y#Idޞw*wy{}@_|yOUy4 Ňi(?%'b]uv9?NF}]_v?]8߾pacgkuhO0o7 8V0Nؗ a+p=iݯ;’7vMp%ZcASP*$= 6kYPץ=Ƽkű]S`P6G2G:%OClw4nbsG?ّ6{o55NA|WC}I}nSI& 43p8ANF´ϖ)^MA[Kx>g}ٻioGw^iȈ߬YfM=EGkA|C[H/=߅p2˳S$XK:اM  T) zF1AΜ@konL=/vG&r]G"l*??9~Ͱ~xi^ /BɞZ\~F ~$66ݙ_||P'H;T)fe6^%kR/tJR1/ؕTqUJ;I$^i#o-2e- 91e `@Ԃ6GY6w;1wO[tgÜxnh[#e?6u߅NTa۰OY<lq\yX;[66v1Jq!|(lEIuy-\ %ۅ0%P:@c-~8x'jup\xf?s^~ %%ޯn *>֏q3S< .Y+uGq/XƱzjs>AyI=C'=MV4 zsCQ&9~FtJG}pt~FO0ӂ[5֖S1'ZFD~ث8/]֘ FB8ad. Pīy ad"y99&r. _zf},c SPNh.63*+Vo2C7[lteutyWQ:0Q,6ݮ ny{8VYm_g<8'*'$ ViA gɿanN.9H*G78֥̽+\޷/7tߕu}~q(S / &. '*, :0q&`B3nY9`frf0wہY^`2Iz5J`I꿀gOOU\sE[հ䢹G 5|L\.f-0끉K99`><L`=`n!lu'`6J0K~0>~)0_5k&} &3X|-`bݿf[0Dwi2f>0ۃYo\g` w1>0Qg&Wk|̃`v]|f70獶coT'0{L5O۲mc} >?_2>R8T*oItNuϓ92vNƓyix&hx"c|.9ἲfN9)[t="|BJ>ek(q?Ƣ!c(ǧǫɢ◱9üX)򪨣WG^n)|UbEgTYϓʾ*3F}кm 90okmR?eYout.:$!emo ^g{7E&̪F W zK-/<MR'$cUs6|ҕ8 zʍ<8%~æ jBk-o3?FyDf+Gox*^tOqܐ.l{1;Wc^+Kup:SNXxrgxO +īpU8E~vHpdɝJa?]:&ͽda~q7JBo. _"$7iGY -`R8<_(ڛO2cB/dBO.dio6@Wzh-fDŽB6k<#5(q%&Fx.7mWkQ:c_M؀ PT*^%Pf\÷M8U= !p-Vb J,CMfuy+Js 'q[ky9!8MI,:*iMaClCDuG -7ԺȁIMRp';*npnp{(pW()Yw&/w Ҝiowd׃ߠ2TǼMθ@r(+!k|i‡`HŲXǹƼ_ozWarߥS)K1r)lyo˼/ʌQ;o9W[ggb/[V/W\kb1ڀDNt3?3%On.{h*mڿ~6ƵZ6? /9.W$s?@'32mQpLF&; eNQmE$(u溢[ob{wWyCx8ix={Ѣ;eY83EF}WK}EXjh>}],-%eEGZ(Cd1.:ja/F;q W9=,;{0|W !~ UfZ g܄q"׋~P̬v+f:y7Իԇ1- U8x#C/=i=5ђF`,c̟倱.* <:&e(r.! lgcSߌA+=QHtR _ lSs*ͅ =אTN^35E8 ?|+v^JW'yx\8]4>I^I tRUnQΰtOϪd`?ĽWa|.f :kIZZf tn MqIKQQd-E˫CRtz#J3PJ=Ós` Kgnl98_8 R4OJdNԿ)m>}m7xj: Tpͽ,w-?_@7K{ |1sHsp-q0b^{]ڌg0hYBD<,~F'VL>2KIf*J}÷}}2 }3>W1b߆oxﹽٝfAisҤ#'_ݧAV3O_cᯉx .ceՒ t&ηﺭ֪5?ؿlJ]^UoP?kq)՛TV=?t~2?y%@F}POdWՔӈuPFK1o?h@f+`y;g/ƅ}b 0֕7ef H>΍>oRcemi\>'w\ߌ?]VنD|˖p2Jo>[FI2[2޲(pM.<|>w*oΠ /bY3J{Cxc{|2 n1z xm:ӈzfK0N0Fpy2| sSGea+ cLw.8.R& w2 g(e0{o: Xgp !.7ιX/5F(ဇxۈϡ?,/oRY~[agElGsע 3.#ַK,h/<댯vcqt'Lmsw7x%/gLmཚ蜃&EE{ qn^\wnۼۦJoŪ>{Qv,C aj^K)U t蠒7AnIg_ԇXxh}+#;bU$ϼOԼD:cd~_zI2&a~2+Gu*;nʓ{]uaIVGt`16FɛթU0x9iiky˛Z|C4V}ҭ"ˀT.Ur]r++\n[EHs=!J2 mz=m0/SEGt{~b{\Y  ׄ(̜`_{-F%3䍖PM|1=?i>7Q?Mب}[aa\y݇gJfyw>N3v_ 2V6}U/+ܐݗ;&w`<Jק$|-~ |g:5cXb K~v9u<}7˃w?T6{][#mj}xWk o8*1F/QoUžPo|'ɼ1e'B}:a,~͞#L*79Sp*\ AU[ >[VYUU389ST3sAj<'S cg_̞7nڻx paCiߍ. sN\xMo:<@=]h^5[vy;?{%?O@V3ߘhh~-cZܻwl'b8^337b'ӕg6^ǩfmo2fOf2V3{K>'T3;:ōeg^}>jtNWSW5!o¤1',csbWnGw>c}1fC,sƸ-ij\צy\בxEu&0^nPƖT}473NT7pU7Rs96MC8]lrge̚Y)կt{_=ſsQ =5h\LU5Lr0:t?z鏊0f 5PWAߌw0_aUatnr:?HbI戾"j^~t@?c#o"ΫA^T 4A'7Rۜ1bha >瓁#{t>-ncA  naY8[~~J DG$_~ dcXQNVƗze>$y|{&e& |N^zjt]]tu]uq9bdoUCg:WtTcΒ/;cΏ>=ux걮K<^~gf0whe;YppW y){2qM[+ x~'mYKP]} %ؾ&?پlz+.q:#6mI;fr&7DsAĢSbe'kΐ`'s7R8vWKeZ]*.uՍHQ^_8^ ,#V`̩K:0/)~1LY/qY$`fp$k;| #uYo(\X>:qMpm)\ Wvzݴ]Gzw>*xLg9}p=ƕfN=ZWUeu]C&)P{x)<[ pKuyݕ>\ |-u^ag,@Se$y,-2fb+/Uv+7PsOy^}<÷qm2c7@&s}r ӿݿy e{/{7žn(ǁoyvRXQz+k/@l2z כL-{?5:%\ /  *(2|U$Q[s8Й2i@o*FLen5.,k?3|OʼI(:̛txE.gSk*5Y_h{.F ^r^>{Y5$iC3)ZBkb:=Gc1˯.ױc]Z;>w}$s[YQ+7Q_4tDa~ۄܖdٮYF6Y?iKw%c gBr:Ggr\?|u VGEP.U~5֞?Akƶ&>mbؿM7>,Q Qx/>R9R3[}z>mgӛ}[rg~g&7VRX$y%<9SAw?uS3ss*.y VM/ Q8ozOHz>qPٮ9JnNqވݏA{Iנ,Eqzb?p8yw xs)4+lۙw!wyXaIo ~șjFKSA?to_|m)9O uǸ6mϕr_yp_8Bog=V8ʼytk^Ivӡ;d.[VP/o{o'࿇"=ρNUI$?W2_%.;'2fD KWw*la^Y'}Q*^*? O4v{haڴ䕿rԂƥO4 u 8~S;.&=[uJr$z/[9֧:rmwfj >7;d~O%G/~irxzpXG9U<_"}@|a]x4NaRrpkPu+p;*܆99bi[!ϩ`Cٟ;7 x_y`H/UXCSpF?7!wycWk!Ӏ~˅uY_78S~єɑD΀k̿:,~Lvgܽ'9* D: u6Ӫlsygp|:O»¼J^s{}.7-ϟt<ܛ;y=@ʝ9W7ߠ9'/k8g{8mus+ы,7ǔspܷU~RJX݇i}@3PmP_<}Cv][[֎G-[mˌ9x%--?TÒ ,}:wSRv{y>o>lyi{ I"1ꛘvHe|hcf,)%y%԰3'؞yfWn;}M‹ai 5 @cc7mۇ{;3w{4=9Bc/h;8{XGʼnKX~f+!0b|3Џ#;0HGeߋm5p ڙe + miɯZZN?:72ΜxQ׈|NM~ Rov=imWzDO7T8‘eB=GNŅ&(ySy2I8ƴ`Va^&I+|ɏ_>x;Mf 9 ا=8ng޴ũb~$s:4L8s;ښK%˘בG89GMs;k{_~ 83#:fno{.ެ]>'ʷob&M {Zly۴#ڳ=)\!oZx4Xs.U1}u?Ur\Ut3ə*r~eP貁>Y{%⒲Ueq]no <ǩ0US}\q026P1k)x/VR! ceaG{3eUdJcobZ:D[e7;w!plo^]ϨXB,8Wj3| ' :ow ]b!/x3󪃟vOt0gSCc7s6S/Ov?KC_B o=v)^i/fޞn2oo"_⽤$m([xUw$iu z27}dQų_}bEG3%a˙M4qHqA?P{+O Wo(M/y񻛋7}>)NB bw]|ۋ=%{Ҏ&T[[^I8r%%YV=?ɺ"ϣ߂/p+nkG+AVX>*KJwso]iNR{t7rO? |Ngٽ"g``B,L*Д=eQhx=ry1w9cro:O^vNL{K0 돧;tgaBJw+)ܨoHYa8nM,%ûw_BxDG:M$pGR[O P7p̈+lynִ\/o R|`$’KW ̯N0:3&>W>XgSؽ.K3?0e_1-H zyM F=Lyl ߃Y؂kd-!γ#~ViݥxzvKӔw/Y{;@y !:q[pkǼw'^{^~8ګ?N`➁~`csHʷ{w)SwO%o 2wq?M=͞;+| vy׮XD[/s-Mhc~?G"~X%^zyvoc`w0#w>}uǸ&#}nJ|,7Q7us G) z~vGA>ڏ~ES.|HcUEHLDQ'su:aAše@1̟edտցfSF_1'CW!$f\݄?M T7x is?4.U}da|C3#+\T TXF[5cɷQ&Nԛ!_|\<=̻ОVo[>]dG o?OQa.yU8G[3.JG?$M}O5yr{Ä3o?9,'0ȚMC-:|;: XďK!~!8*X0JT,z-?(??OHfOsU") obخOwxۢUU:?Ew?]̿䮐9}Λ[,>}-TWA6 o,`[w׺"pCGymU/0_v 𿟘 IKzOUWi>P { 2wKa][iV}_5-^ =:DpYA[\9q.*V*9)9#a| ꛗx G>0O+1?Bt%?2?_ SXf"'7 v-YS|HdtIٵ9?&oY$JuBDˌ(p>Y'{2͹☓98FuIwݾeᥢF1U},MT O|(ʫ W8©e(?;Ho9%5 \I^VOμJf~^;JE9 cȳ!yUEOs}y%bWgU~ _T$/Yy? ɯ_c~iG{i _5+٣*(s-b^mQ_ŏ(Z_yHC.p;#hO W^hW˩0jOs&ވ um 9q-!#pp8!,w3=3sYg-'|Jg-ƀEd+gos]}Q׻3%wkK-TOil&rZ!"Z)o|gltƧ}Oߟjh/HYnq83m>t,ڗ8 Ʊ1P7йD}5%\Vo8dZb>\`#TĜ<h5>/Pǂ}"[>ׇcx*9o &nx._}H߷G![^ℭtnῑVJM^󸟋hރs G.Nu/)ZT7mnjc'%u.BOF5k@XWl^{+!IXVA\v2>-Nn?P/bÁ~@]Vq7{dH=~@z@Nq1=Hd:~n@mw+ [%oX;s0&g\20.Aw/`|Aw+/`Bs~AoO#O/hG6Rm )c܃ 'e1؄#} 8{ߙ]7OHd?r&`*ƒrh98ګTv 3jy^w>. ?}HC"-·`m׽h^v? 6ڞa=·}w'˃? ࡀCUcyΏwi QZ ރ|cޏ|EWD>tF>|C8 cȇNM:6G ȇ*1>:|_@>|m9.ak>jްt*1Fm/K7٬Ǘ}yxݷ}w&!V3<?B / us JCIȽy)ոy$pS$xy $W{ ,'O%;<9~9IUً1n(lFwUutF>#~ 7+oy0'p8u 5gu8T* ٩oӇ\7Pa~g q粿|/} +{'&{%KoE '?_~WđRG^/6K8 R/OTW%|Od^ŸsKURm2)8V3O{qHżV'HqV#֧Bkꇥʱrom}c}6wp?s:%e\ EΎx$?b}}q/]H>wWh/6{{QFjڍStyjϙ]v-\⢻e}t|་{s1jCcˀF+(#Px(SO*)1Fʾ(e1pY?P>p?Vx“^n()58npG);Ks|-3]^  yp+'}woB;{ʓ̻~~W~&$/L{*?}ƸOh;zxz4vuU.KSD/hJI;^=AaSG /*|G' {~08º$W=d;{ UW?\Z%{"a<Z}s|Matۍ<̓ ilj }*n"vaLwG~m>*o7asvi&>AΫaJ..iǼܓ3WWd2m,̫9ٔՌ>=UoBGYxpJ%ll±Li9'kc|']A=>m@Xx;W'j1o#SM閷upL}8+'OeqUrH=}krowRLyg1'2a^G>bc՟Lg*cΥL0ZYIJy37úaazP27 e.y)>XXV1^/z8]S})36.3.?@;6P>Y&S.~ PvGy{oyJY>k'}~vs/Vks/J㟽S˄([gcqMt ʖ{kZOUP-PvTټy6O"g&mO~*M}#sIəM?V3c'S G?rCHgjsFǴlfxv=`߾u[g18xC.;sSC@rc0p~|v>n73o؍#*1r_綠Nq<'_GR{|ꚗDNp"m{WDAN~1=N=oYY%ك@ɼI|Ff6%MY2kL3~bͮ`?V'̛ x,cy{LLmFfgq{ȍx1+omc݆9ւԇ8fm13 ׻*dct'Xyɯṙ_5DNEM'Jewڇ4N,R,/}up)qdrl?dY KU}tӏN'tL' zS8h٦WmxeFzIO|>8e:3̷1<2q{ 3p3 >`xg 3bY6iί0(8eCrNҘe8eF2#xi\/8Ig Q-|ȼd= o{D\~#&P8?empI=O#<:y.3HWКisܾ59N̛gScIYa劲~-Ɯxχz ĵ Ơnʛ`{;Xm;,u\n,^AtLS`gn7>z΁vAhJG3~w <([C@ts w;\˚8 | Wf.N6_xi˽I4wY2kƗTfxt/GS,3j1K[|&鞨ףwzrLis>-lGΆ[؎ V_ F`PjvD~٭e»st1}X|fR_7¢1(MDm/r,j3:(V jܖ$ GK=:ƢSMQa3CfoւYWh ;g7rߒxeVw^4eZ2oxJL;Am[<Xt`=nc8X^ٸ@p쿱݄%)qF19QU'cr[β#8}lIȟ?SO]')DƪyzqYf4Ɵ|c6?gSRaawU{6sl5,nøbi?U.?33!}v1Mz Ãgn;3}\V*AسzTXvPYȇW R5YiwK0+Kߋ`wl}'*R5m 9;;mlK9=qn:`l(kS?̺/w̼;Kﵬ;˶`~-ϰt- }iK^xϫXb >Ei,!(PZp6.&s7"j-X=D[~vxl{ /#4?OTV:'yJJ/J%%Cj)p9&k #]=av[yQN} TcOz`mK8昱pӧ,CEny(^;'n}YJL;LtVLXUo㍜&^cmk=uS,k{v/ugOk+'1堹WXNlYWĨʠp/zsAv*}u(z.9꧓yET;Ӽb5AիwBFݎJ3*?Cwؽ㘅mt K؟2c^yG›}aʎ*r ob5oI}y՘'_ {+z2 ̫' <^py;ܖ%.g [h^;èw^,UO.ugC]Q$Cx;EgcD7p}}79'$5v?V%o{/˿$ Oe 0VȘ#qM?W۩rjp#!\fyux#s߼;*ԟ{}:R:Zeү0} saWr&0c__,f~CxٽOu#规{{kޖ53'vIOFvȷ-1QX27agܣ`:^2/x\Q7d(|dZj7.~E^޽_Y']h R'qZB}FZE$UQFKeʳtp-"kti»?51LLȅYJaLJ. %w1oky~ (g y057\5;f*,v_ܗט=Aw9C(K5Nuɳ/u\K~RzRzRmҾfgKͷ|Roѹ?T",oFGƘ`:LURcmbuOKYAﱿ;Rړ *)2^821ƌ Vm1x~Jpn65df4F%C/||ƣYFo&^`@ y+\o!{}f|7c{.qo2cߛs\XoE>#eT7 (#,67pGEg p \|E n_|9?d|ߌo~w m\]Uxܖ=?ܟG0~x?gucg?|?aӲs0~+1<c78'8dơ2ܾ7W8D|1Ƒ/0|q4猓Nʾ|36}V~p?~7c|h )s=['%olq5e}0Ɩ6;񀧬ϢoVѲVRۉ{%(l;D?Wcl۫ͻ:~Uo̱ˮx V=qQ_s>Ǽ{?]E"}co$dx*>Gx*coc>vP*һc>xVqf"w!ĈOc;wG8p|p3_>-p՜ޔ*j)-Wswd\GkVJӷ<q]3pzך BA!2_g<<"=RA ZCxYMMp?.Q˪8 Gk]ZKX:_Ւ{ҟ6n~op w  9Gǽ_|;F7c7ߙ=r}tk}-WUYK\u"FZEïv`eS!־__H\Ձx.ꃎ>[Y^c1w~c:0FJmgAY/k eA}YvK;HWk< tjEl!|Z@A:jtm 8]Zso L5ɤ;n5Ϥ_Kaӯ"TJSu?yCaNØqUϸ:c\cӗ,QY>0~)~!ƀH<s|C<䊭e^[=\o'C]t$gwp}f=2Q`].q^^ P5 J3TG|P#i||q֓F1׭hCQ \N13|F)W(wP* ^ ?|)ԇ ZB<ՀQorroUs`9 ҏ;UxWڍ[q?INYVܧ嬟pDN}yu8˰ܗU8:q݃ӋHfPu?OzQΛ~ꁸ;tK[a]IvDxΒ`.ؿ`ޣ@}W 4nKq{Z h^@k0MIx;l@'x`qI y6r2XE|ʴ!{O\ꚋ7ʛ XRRi܏eau)WlP~w=c\yKIs[(b;Ƹ}1Α39rV^yt歴v-?I_3Ɇ]A"[Vϭ8_,ieɩKok#&HuR{qq9Ѷp0/d*c|%1rYvG'~6wvpY*tO3l:y]Y⑳Z݁_g[s{\}y=m9x$kg[ClW^(I2֑U ] k/ʍuqz(?+ 4)~i?gC~v>Isds{/UL7 xH50Q3 s0c^<54s m Y|m< }3f*{KQsz'NUGu[7|săkk}}Fg;<3cݐuX ^׊̚ʌ:6US,KiY´e? | H᧒z2JtV{'%ik2^g(c[VoLo&qȅ6 mӝɬqgG)z'>{طcpbO*<kDz>!cirsy3pyvF[߀[|y!x.Pd' p4ilE p=^fExa%>qG4N3[u;8TIMgtno,kV]z`3㚖=v7b.k(vp1ӌte:>r,<}Y֡fRV.Z:HmBux5VxүswӴnnn-/xnߨM\Gz{ecv'z0bj^WZwS=yV'og}$av_=Kڒ=Zg^5b|sSA1Ǥyf#G*0.OfoB*wļp\_\ӆSw.|eKƛ6p fS]pqHPq?}6㹂/f^PXP7dX7tK2c*iWc^yT|׈{Sva1'5{Mڦ}~:s33Ϝ F)?W.ox]R^}K6Ų~3@/~0o$]*} ,o.Cͺ̹nyˋ]=re~هpœ~88jXOt[qs*8'fΫ([}|N¢9 N:G89?'UG u}|6~Mce5edyP7r "''nƲ.sNOVa7~1lmYgb*=!޶axټ N.m2l?n1Y֛^"(j50ij>먝׸-~9±蹷wxƯ* WבW{9c~מT!JdKֿqN_º+2є7x˿u􀯞%{?<쯚e ])_K:x_y>>`0k~N2oƪ:Rfi |0L"Ǝ0+:8NNid !NV>GyqʓLhY&{A:H}=q!sY6,YQXkjO2 }:} _r3y?,`y WÖh!Qs]#4c%et'b~\e=Tc({%/2#)ΓhL膘7op}ƶMg~_O^]_Ϙ|,*' 9েLNvؤ;ac_TWURh'>qkusX ׉aaݡ>rPqlT(ny5ۮ+3Z/>QXߨXn`9p~Q!xp~W_8q+~ʻ#yq7`?8d=X57L&9i;hbe31i?b- 8b{G|;D/e-;dmwΡLJg;ڥ?GG] 7:H}?lha \{o+홿Pܴ7;t>;-Uu]Oc厱W-k0\k8 1֯4Oƃ?f"uhg0tx(_Nx(,x.x`zϸ1vn QsnQsn?co880oQ^߆3 ‡Dn>}|R 597s8(Zۜ5Wܘqu܎qM1ڀ1x8㺀`\aəniWѾ'g3~4(@&^[oKd7:[Ռz\Vۜssj5yHz]pvmy([Ԇ71|:{ToQx»ػϦ5upxGqs)w*~ġA v?)YyK}V ?SS1 Ǎ>q~?Y$9R=>A{vW"^DZJ VVO;0YKaVfFup$WtIomL>aeaq.o-o0/qayƪb1ȥߴa㴣 {NPD[*LSa9Va⺺>IgYpm(/?hoN[ !WYi|e'Zt<' ߳~k6vy_bUƭ_7+c܇ɸI*SMd|]p`^6o1#ۀ_BxY>y~;1NR+GZ &lCc% Pߝ1Xπ lxz ?,ל4k;OF{tNUwҋc]/avm_kҋw?P}/{Ҳ%Swb)=U~4wtO6>EzaStV )ڟ(yUOQto"=O߯On9 )sv2 Ï8m?d+i:PY6?cln(s&9?M2&9N,N{o ~ΐt`hkY(˞0,Ø|$KoOнL^ ݓ8*cT&2{^ xel .s/dw<e\eNY±-WJu0873nㆀ3n8qc7\qSM7܉kg2?dّ)ۿѫyBwSZ{u S8`c;pGCfI&%}%݊3/}n${kYR=yF9j9l/1H'6T>%We{noסp-Cf3*Lk25,`׊‹Qm1o,毜c~N4y o Pp `^~f^A--{5/璇W$/}c^1OwY1==O=)7̼kEl>o?ex"s`ui<3"{;W]4z&棽Bf_N׬='E:sw|~sn Ma^A:b%~G'̔0SF6 af*\޷diu̗~o~XK+],Zg}.».,p %~آ.hExQ8(]-Moq'X{wA8ࡌ:/H*?Xsׂf^2 x%Z+wҎu`%~WEomt8qyɱKfy}1W| 207o3֛KƖC竳_Zi6ۣ߰w~Ao?g~ +{cidl͘ kDDớunp%k(P TU5GYDr*) β:RL&ۻ_8~[6֯^2/ڊ޿LI_{}ezcbeOP]Q^1jUN>Bw]G6\7q^gw&m3Uz6Wi8e &~uf_&0٫C0v2 ݨULIinG{rU6۔nS56gp19 +|p_[͌Bw#NMonݤ{hl1JûQ p۾oq~\1|{K>>g;w7y1׹^:pC/Hƨ\aHcQh1~ȸC=Ծۙ⍇6F=*ǻnݾg;7U5}^ῑS1=?ݮڪg ur w!A~oV2R q8nRZ){׎5F))l|/~(`p1}׀EscB}I=7*ntL˥VOc5!sm.oݥ.wujV%b]ߥ]/hlHwRHhqnHy}H8q=+R'}+'}+>OzŸW̽OzŎW\HHh^1svp^%>x!UƇW>oEvYᚔ=ӵownZg7ucw5!.:?z ;) ySmes xOfMseތ30dXsN$$ApId$(dErVT%"%DDT_uta,祻:wO1ݦ|AGvx3g@ӄap/L1:Ib׿/nG26n"7KapO+ ԓn_J%*~ܥclfC@_is;}Vo:EKLw>R*F _Wغgp: (\:Oۄ~*G f*+>23xlE4!.)A-۝)!|/F2T^Q9=I2_.;0}ݹ2~s2߇RM '@Op·N[f Gt摓郁U5~`bG?Px_+\~m~@$V⼁X>PYOg.6}4L<>6&Kd7{ZM?C:}O2DX\p4v3*ޯ gMcڀcw[z,"noqe_$zgr姦fR X~W<12,ƕ/`\,Jcid\)5_c\=Ƶ{ 8crꞧɸedP=s75ƀ72nb _f*7'! euuc`sH9\Y0uüd'Ż ݋) ̫?/c#و^WqŃ]1L_͕6O:a瑤&s+b8Ϗ7m5\ :[{k+)k*3]|Ǭq8>eTyȃ4 WmOZI <PuAIޙU p\6C}wv&a!@WIaIxGcJ?}k}G|l&7z<7_5dx-dlG+Ch7OP6;9z\'ɪEB')9^ʁdU馹lq7d.B/L7O3W~CpA!_wWvCڑ]L?ޜwWO¥ߠ(=Wi]hvi6Uoc𼬍ÿT}%ӧu>%cL{Q_x3 #򚐔O!}[Qgq~VKU:+9E)]%2s(ŧn'9Qk~,ψ0COdb~cM S,}'_w/su}ɤ^/YO /c@⑺%J&tN:ۇ30p42ڒ>>;e <{T$Ty>dG-}ԥ/]H:|NV{y";39Wّ>xӘ}n[rp͑/kp#KC콇7+cPǖ]w{eFmDz]e3^_r)(--S c3ںTy%7%|Z-"Ⱥc@ayNmu8ox},2/[|3&kxPfK/7 8 He˺x i&Bm,/3 pwuOu!-31 Kͣ6.giYXNAn|,/|Q?2ރoovh> ~Am /0_`Z_`kcݏsX7xinyF<AyV}󸟉qk{%-פX@ a}[HӬ; Di[A7OvӼQHAn)}SCy (wY =xH6PLEY^KF<_= |#zC~?.;p9SRϹMm~>?dZHav1ܞI$ ex7|AIpX5T'Sy(=`g?{&ĺ:wmX:uleApF?Fi.dEcy 8"K~Iy)#] (776 O"%g{ 0ԗe)*?7[x^fRɏgabx,,3,EaD[2(*+'ᛁ= r!.;VhW<I>dF5|{e(|ј^/-_>]J>|v>Zo /mmMnv:Ƚuח-ʉa#q G,`g~A^|ώ0=;߁ѸǑ qNvY)!<)M>$e̜ȟi0>ld2"CŔ4f7dz̿-~v/d딒sDž)i{Y.Ŷl cƨ}QƷpP*1o#C*ctf#Gʔ7H*tYf/Ҏ_IS}RkYOTH? U+W~nWc\~3Fz1FSQY<2F7ɌQE7Ѥn?&={GwiԽ9mi*u͝%Ys}?.-qˆ v Ev}*#3Gh'PA?W.;Mc`,v}$j_ObYv=+/6NQc EDckiXhC&Qh'Qy zYR;5 Ξv7އkw7E#H_z6y,DyH;xTƽWaeG>gk2{va1فȒ=4xv` {cx<5 2Zݵ̞XSS[&ơ(DmUG (O S%wɨdojEQ٫=^:o*QTw#fB=ڶ]#0Z\#9{G&{ Ls\da8?)+ uԵ?tHx_I^+T.wb^c;vk4xl3/, i|N1ƨ+cOh럦7v̐̑,,@j*l} 6#c0=ilC7L7qSꕄ:9owHy*'\΢N࣌17DIϳ; I)'M78=cy9i|(^˜/i) hq|*:e$=;grMә|ħm:o.$T?=y&I~U2 ]Հ=NV r2ʏ|%l᜖iqlxBwث=\'Fgn)_e4A5o}߲#ֿYssh~ßCw`3#R=4ÝX%3Tgp] ,tҚ6hMNoFx'1 M4s qsip+~JQa 𛌷g-Ko)1ϯ~Wdø'uƹO)L܅ܧt^g}oK9w`s%O >g;/Qmly>=vdUd^JJO:lofpp lx0%ۄ ͚~~d b\Q 0>vǽ^q9Ƙqx12{p2d9mGxwZ&Ÿ ;)UO8vqm{FSGh#zCqxv/tR t25`[1-yhphۃTOqh0r}s3::,fF%Х{Qk1F]duuOY?A?co1WяJGWi+g0&<%]j1>-;ܳz?R; rcm1tqlx>'LkQG4 |Z Sb|y1vmo8i:~aø< Os] ߂iڝcW2}A{c|}U}UϻJSǃ29GW%y19[QSKi@ i`TelJrJUBɆrDSLTn <Еr]TL <73ymϯCqYeqL4I&_53 goWif=}[z*\OKLe3?ޗ})n*lG(ANeυ&ox-cGut5{]|?Ӎ @wZe|wTG޸o<L+sgZ*~ҧ~pIJ0߼'t.MI9&8Kbjyy5gчq~t] B?un-e9L8o7ce:Ch0Fux/!Y|Ntp%#3콖 cgB8f!zS< ɏ|J }F(bbsLYL%,aMY6 B?aϛ*ZD2tx5<,],e_ E 1𷳨Zqz0>u8F=]Q˲1eX,kmv|]_;Z׀Lk8y}n2BN;`@5@e8*t ?{}6ÁUVr 8dZtYi/1@6`Z`SDJu蚀17C$Zi똕;+-~}oi<8wZlGJIW(NK8X[9Ԗ͏%letZʖ{>'Jf+f[^-Vx2陽HIWng3K}xןl>+DރzDxnTiGI޿9itï{~ FSPfm ^^nӒrZBz^:~6 j̻eLd[lwknQl6>4OsMF2z|'ʠd٩DUI 'knW{^'H`=rЍ%{~鎀%nPt҈ҥ{]? /ig]p'&0 Wܟu_r% ѕ=^ p\ m4.ڡhBỖ$_Aq_ зrIZo韤c'J{'X P[Iw>ٓGvjw97f !8ϑoQ9h k/ v-+6$It iEɺ _Fes>Uuȿx-r>[N?]JcK&(ߎe nǻ 9xzc"ȳ}3>~MaNC[_7ܨQVU xoG*sӅqa_A{$E={О;όhూ=C7%:5Gؾ'6>_{;Y',(Mĸ1ѱ>'X{HX)gXރy%J~pXXFY`:T܎q o1lwe\0 x4Of\\wNM{,S`*w`*w`*Gў"i? ))՗ϕ@E~ dC1d7=?dpjH^z/x]Ec*yަXr+| Ob_8Ҭro{pExdW2)|CWn N*FiG椴`u 1~&{~s'K49) 1oT&9T~ggqIk65u⹣Qi-3BTN.ȶhN#yMW@:}l\D_(}At>eF/q[1yc{f=>M]_߿_}=u189 }ݑ,$k2!p>L)DhEWz#}Ga>5R. U_ӿr8OܼX-&%7aݸ<\>ӔM-,Wܦ߆roCpfJWeol]E6aD[vL_d~51V,C3p뜛:h}=  a| Ca;L}&([8q~Yg5𛒛nM_A9 #C|ɀا xQׁ[ 8>GW{e;m pcrB{(?ں7>kEl7JncpT$)ou9/Yi1iLt+O(f`-rY /0N 4ۙUpE)ʀ`o36,ia4ܒqO_5wd#k%oCu<^O\wPz1ӍO;0݇7C>W(gp۳'w8X\oK#{K =eQ;#ā{5(<5g> eo*s4W{$#c09Ϻ_VRt;[]DyOL، L!"KWg3Wx]rpKU8Z45/kм;3po |_TC~_wbrMmC9'?D>87. ِ qTdʇQz^XS_%u60#*c {ܱO\dYy~je87n=OxGYb ~=#ܯHr/ͲR F> 3 = ЙR>cQ?>90],+`&0{M  iW*(.0w-\a2q,4 >O&OvٵHNk~3l [i6σF-eV".A㯷 @Đٱ^("Cư8.Xi_=LZ=%yO=9n&~p AeAc^^+?(U0,'JԞʃ=N_qs}:`t9m_3nmaćxNA:z¸_P?ϗ3{l CAELu qOBwԓ{(17r,OAc8u`O!ڳr55^~_$ЌYBN4eOs^F>06Tڇ pZ%n{D w7 6{o//y2De/ldXYy§T /lK֊”7[ ;׶70h('hV4^'<ߛAxQ4hԞ4Mxe+""\4i>;&JdaK2y?c~/Nu0<;:zv\uLv"]_m~8W1le0b7ϗQc]-fgk+jǹ $۷<}B)Snj|W6u/KPR|P=P k"_2lmbi͹赜G[ebHa yJ:c|.d_5sO]-T; voI5=todҌrGU4emdq^WS{b.[(÷%ӽft]t§({juˤU&kA.(a uM9ݯRܿ}8՗wvR~Mr71wO v)wCp~pʒ+KXKbK, bbE JՑk8ڃ;X3Nlx__&E9{/.jF8~֎5+"/Mr+7o'2_|BUmD8)zu= k<5+A"*2uU$ S,c ?;DA t_8F 2 8߇~nD2tL7v2(#KDY-=WM>Q>3o>*ic_TNq Q|uoӅ/ l?>QIj~*Iㄒ~J:Z䳶R|7nnZ:;Pۘ%ɦS>g2,A46[VۙSPޗw|2|CjIsC $\ʿt6ˑ]o(\}[j׷OZ?WxK=:v2j@v>6iwJy4Ke襾 }q_JwG:sKCyG{1tfr~kT}\kTïF[%qMoo}{}'s>+1:Yl u)[)=l;J+S{4$+q{y u'|ڱxCVB)`h.L\iٶD8E`ݩwV:nV?(YS|oꏽG̳c]?Z_ɉQNR?dd9˘59m1OY3ߡ~G\Y긢a2m-\fyN};l&tw }7gU0 BzDc|:IWLwQ-}<~/]]$?k;2N,x(7bOprUe.sU)_+c΃cy.czZƾNoqg iB˓,uI, uRiqNŵ(R Ew͚_i_-3`}m6aZn=gppy=ۜ7VylW֏D7oʆ/ܻ>_;;rG)u͝4/){hZ(.:,)o$(>UuSGײtAח.>[h<֫\ di?TW$ؠ&' ~\P0&P)Rfe{~ܙӞ8Ǔ`SpDL]YnkH(̀1MzV[uyOSC@k_v,u׿/!.}c[}7h!L0W>n'Q4~='"jCO/q ާdu{Sd*Žgo}hf 4f.GYU/GkM6ׁ09lr8*g_;x9{>ncYR/X֔+O{kj'jy[Ӯ<9)OUF?< < }y~Wy:z< < o@4 Vz/ n/uy.Yy T/1F0FI?<1'I_ 9 רCs CDVU`7vH,-`|oqø׾@3cۊrdr[Ѿ 7T1w?fRp|q ૌc}IKgmmsGV`8 c,x2>ڳgh>9 xɻv=bT@\\p>/V:>FyM{#Eވ"HM{#}(8`C! |=p8I&?m}UH_ϡˬTп0h:2H}Է6>[}yuAaGʷŌH.Ჭҧjh"Jh{fVD=uW)i"^@)TtU2eNI{18ƴ@ʮ-p}L_UΠN ۉ1~浶 \LW*T|mۄ:LWWOwi} L_Uw7pZPktN.̦V+Ҟ->ǮU"yd_W9`Vr 3q Xq<5`{7^dX9WHf^ɘʜy*s $g~zA}$wPU>өG`sn'/ ᪤,؟ʘ֏M0޲RRڮBL(*yo: gCƢm@U}/ULtJebpU귎t\ cEO:Ϝf6 xvU aDŽRyP>CN}Lg9yxC _(v~?k|jϚ6R:&i.62wk}X!Me}&1֑OVxk,uz0\6|g׶>u|=sϏDk /4.I{pFt?)W4Y,ꟼ'o,cj;q8ٌS_C! $ʥ%Lf)WZһy/d͸rMfw [sr Y\~%_ޚDsy@ZMyPӴZܭΘ~ݏeT2c*f-3F1ٔݱ+:Wgק911?b})>>gIcxFYwR+:\k=-2els-iIW-3{ x3yN\Pcj3ڄ/Y&MHD8qem?l9j8x꫶ghT3m}c}O2״"(&OmgM|S]'m; dzX]x@L{Hؓw <{!ޭi #<Ȍ-ˌe1ټ}OYgGnOweTchk=݀O(Wk^7g97Ⱥo>|s"*Q~<~2"#d{O"]ƾ|L^}0x{Fd < :~C_4whٌ1>i1= INɴ0>nU&1K&ER&NBLKR(cv?ϲ]Y:`q*~~'3W s]V3g RawL+B >CwOHo- 61)9Gگb' ы.T޸>_M 5p\D:?W[|:f&ckP]z꺒§cʆ٣6/}:4)=cٳ:KBu|Oź/k U/o*ynMA[xW>NOM/C4ߐ^{úwysh`<9E_ܖ3'~c~o2 3E\Rv3"o^ާpONȆUdYdOd|Zd c3^ g p_Zzf]S{竲5OṀ6it+_h3]&[$.{)7.&__}&4o%%?+#g+M̻u9&f}[sǚф&4ߏ{d.JL.~z(p_x6 L<0i oO=zGXusZ& V<\rWa;W?:~zr hzw?lߠFɴ^>ՄHc&f؟Mޱ'Mޱfbf/Rf/Rf/Rf/R{`XS3qS:ǁee5K9waSesXS(*_ORem=ׂ>ph99\QW^ /۵07%scҷL礥, ƹ ׏?H~$/#Uqe]|CoӍ| oeޯ.~{ìsY;}NPN%?h8Q.y7yigX՞t">p4j*s[*q;2 5}tþŮt&VDb,5XfB,$=`u(xJkO􈙮ܵq;bFRwvD| 46/槜XC:Rl"zǖ xΎsl;[{_k2tdks'=2ߥ!߭i'}~6l:@a3N\9?Q )/|(>KӰyesE <}iоcLAyicl%GX6 {_}Ɲ>&zϿsz*ƞF1]+P7K\' &13%_|6|gwH;}@{q}MbDOgw(݇(޳>\ /Wm gI׵6d_wsxlgu?z7&u`) YW771,q~Zc[.kLɚo} q aДqܑO).I64G<%}MX7`NIfR{'۹Qwxmisiv˾$8޼݉sĒ>ᓠ7O/'yœV?KS+$=vlZxSfG0F9? `B\x:/7gƛsٹmO(m@sJQƨsO +|#?}?m}/hϢS҃d_D{UzG& s9tuQ}3Gs)]z,$?*.Ê ?lu.W8 pl[cJ:V-ձmVmM0gzAm4}73~z7-HE󸷘/lM2jM0m]\[}̾ymӫPY{v{Ysa..,'z?6mQ\ ugQӖ σ|h[r_`Yҝpq΃k2ap=Pɋ~hKiYdϛ >W^ n: քʰa;Oڀ!Ƞ~ ZFl:@ L?<7I{čy6hE6\ʭX8xm,{bϡX33mqk@1'}{s7 t/@b'.;Cr0xT1R~ʓ:!N_#.a_)z%AV}hp 9 vNB5<y{ʫTKp%T=2b4>(zgOR۴P ;L᠑_kkh^]'p1n\{S/{DCv0YvM4HwsGa˼GuoLn'춣|"`ԩJ{L[[_gڋ)L P4W:i;}E:Y-;ٻ駍hi:~ڦ掃sS&oqXPNvcڌq،<c]~ pag"XapЛGIz] 3ly Xgo}N{Fp=g_ͩ)܋T-*YU_]la0NE]AVqdp>k(܂՗b,4E㙮*__>wJGrt##|o0}@a۾M=(z,B/tU߱/%L_?^/+=r r-bW[~bƀ/t#!Ǽ\RxFIm6sr|l %wkԏ6}]] u6BwBwObmZمxBt!^]WīJWߕx Jvޓ}׻}Ѽvnt|$tv'wޟKkFS<܍Zqnl³m^Gg#`4hyh!޾o7U^PuPY)*mۺJGIӻ.u|\ڝNN;gS>^Nx;壧=`ǑQQQѪѻőЃ҃JcmcGL*+{PYIe%kOztO]'۷IoIy='͑IoIoԓ8ғғ{uz|4VEil؋غK/J㻽(zQ4.Ei\݋Ҹl/J(zQ})9{S4iMi6767Uo$ێ$ۉ$$ۣ$[;$[;$[wHzl!zC|dɶm;$ۙw[Տ(p| _f kyGsKu܃d~!jB wT~o\?͟eHKAaS_HCwa$0=UCQ 8}gFoiÁ?ɉ*}M?e;;MffEfgtytPclf>Ɩcwn wkM[(֭>TR/խ}n5Ku뭾T5/խoR׾WӎL_돾WP?+Uծ55ŵŵuuO@US=~?vOxl]ҟdG';z?ɎKv4ۻdGKKsߥ}sy5w.`ݻ.kڴLj@4^@z84`s =@z:12g?o'Z{G1@|62g}~c엉?Ȍڨy^OU=L(kb?8yp~fJE(k{|'W?|*Mx{ͲqNe-lql?0??ٻZ')8 kܛ_?YD|/숥hr(oD*e. *Tx3,_QYX}'՞{Qvy<Py%u۸ o?'}:2߄cM"KʆS.R~?p?ɞB);x̶6dZpbqg/m ɢR3jOoe=y܁m^\ܳ9=WgZ7+^<әDIp5&$%tzV=v!ұ;櫸>T}8KdmA[S|97߯٦IVy4aϱ"ا%' _ljw2y?}Ok!v1vqzmkqӚ1KBa pHrOBoy|!ܖ@=_7D]n˯Us5g; t$Cu{>~j.[*OQ(Jafnu ÷e)milAׇP?o/WR^5Dw9;ywvPx}cY/r{n`x.8p=8Ug1\ L5o+2/Z>_nH{77IDZŋߟ>uu%?!StGipˉ>grˢo硭LͽU. jwye<Ga&߆)nwp?画dK=_Qzs[Nq=d;qAo$Q/~;pEƸWEƨf#1wfyJҾF1Cĸ3){^0sx:/}k. ),c|3'8 ggDoU_鑔áL~ʸ r:Ҝ )u2ǷfEϋ6捴@z'mz3w! 7岉g@^=Db%vǏ0`]9.z!wZV+reX S/-|Eso'h|dyo94{t?`w=xy++(s]VƱK0422~S"<|QYF]epQ76FJ7vue+վi:1gBr _odzYW(]ƮH{ &OUT.3]gބ.h4/hm({6!,W=L?߅ 3?9ĕʊ|^D(;edXqYO'd;l?@\8[ U4S*/ mnhOFy٫px4ٿokc)J :Ӧ4o8˱X9Fsb 'EZQ>&1>0Z)s郋QNp5[1[1;ŗhQ{Ec!0=jm`+<1ьNgn+-c-cm_]A>)iT֌0 ;9f|p5X1Ź h^~`:Qhbo x<,ɀaLE?N"C CY2ϽyH[!L}^~TCc ¾mF#Li|m46" s--gı`qq< -*c}8<7ȱ3?u>c-g(  -8 2TG0ZVRm?@2M^5_~kig!Ў`}TOq^)>86E^#x'jƉf5hmƶ4_oưq)F4)Ȏ {m|z1Ҹ 7)x.6f4j".ܠyxN zqvOc^-77Z7"C"yL7X߱̀Z8qOOXo1t&}ԭ8_X? y;)K~BqՋƀF8=W-* u~%8𗍃ޑbN& s&><z,À_m x'DM>~Hyǟa>>n>?M6}QyLw۹_c}B8l㧟byc{=Vlypsx;DvO*)t?{sU)T'QWOQ52vyԘAͭ9D<ryu:l>B3~upՠ)lm86q "ƨޤτzk}Ambڅ]8=3(:Ǜh?j)v;SçL:Xx*\y*FSJTGN>{*oR}=q ڂ<ɶTfei\4l(qlv7Wu#}Gh3h{e$ޛi95{qˌ3p?z~I^Je*[]LK ]FEw]N2wyNټes~2ed_4s~d}GePk$'M+U ۾x?\6#}KGm2L=~{4"]rrL77pݱE4\WgKլgX W²xrSHyo ĸiGĴ[@=c;1 wNaOhF(咸簿,}tA@ s^˜U\!.Y$Χcݫþ~3(|+ (tъ\2te.%ͯGyRkӧfȒF>rKtf5b07#[82*"F fL_t"tY]yv ~gW}3>cgNNo-t ܆L9t·3M~l?`?FI TX<ӏScײ!+N./@GfRte\$|n9nu7|ѽtg埮0WxL7G?5,k*x@u=P=8|5?xֳ6b@'vuL8˿mإLWN0&J&LBgqS٤L пpc i{6[>MsgsϬͲ&kep-]>שy^n\y{if:/Q0v^G蕖Ǚk,4Ϲ46^QiOVJk9^J2Er?jce>OL7He}+W^)Ec- _ZzO'xvw=vm>-Ságb@S>S昲y"rcNcNeThzTk3}u?ͧdo,ا[flv%?uXð9CmzpiN֤r>a ukUK^ޅp>5J}S|fpVK|fL˺?~fsv8}F =?tyA{4x޹$]53׫>z}33kLͥ0|\ d6mgG E7{IE32i>Tc_qM%y̆)G7=<=M'&]^{*w\s6h1}$5{y>gqyf/ Gv(vyCc< pyJ'NT\?l/{p)*TW}a&ҧ76Pf츛f 39.v ]a?Sa{+WO/R,vA%MTr3-(WٖATi^yg^*^˞SV+:\? ͪȵu[~ oUf=J˯aQ=6,JSV62LU ɔa ;ke-VXx ^LtnV1ݢܷ*2+z ڮxx~ |d//%SVp0?<0 ^ôTô2iM }0o}q*YK:k@aZG첬I}τX:JGY{֟^w֟顽OHx.cWôAܺ6D`jFp8'LHx!>罅qxo<F ] <:GP8B3 ;#ó@Ek/Ʋk/>5{MT li X{̴XwXhG-kBs_N:o Qә){)ʑӲxW@vϑm`;xA[zXVnzmL |a1|wd;OֆWHxi['d2m; :^NI_^{.eq}Λ!//j|-~!ٓ15LbF}",gT-xۥހKrߏ2(ðTQ,?;wb> KkB(o]:ϤX}5<_yojbs̴Y#ir/fgʻ˪T?+|I Q}lf#{Ӹ*.)Pcs?\}xpn)I5xSX!=7YVw.?Rh ?vS]%J{;mMd:_UE r,VݲnP>Tֲ}AE2 Qu1ۭʌbT@7E3[ KU?=VrṡdLSE+gQ.6 Ui}t9]/?OR:yW+L|itgz<L7v#/ La.~Re.h -J _ WH1]'Y<xK>K?p܋s W/kz3]pR{:q?_IMX?wׄgT~(4j?=Pۚg %)yXދ,"Stȝ9x#Mk-tݘ!н̻NZb-/o[yu8bxܪe6qo\i_খ.1jRg`\Vvi şւ[D^2_ׁkM;)eRe Nm/7iܤmrv3r:Sӝ\r^f;W尉͙m)#.w=$OG,=Ocυ$lk3W麹?'zCG",pųQ(}1yZΤ%-ԡ}CJ)[0Z@[t R@f.- s[4Ǣ9,[wȆk>WX+(q2i#PYuC̖4(k^h'U(gwl_?fVVPVSxk>k}>l#}YWйVPgˎZIN}%Wҹ+k+,\tnJ6m%[{Js۹NYI1$z<{=΋[+y<``?tc#WisGdP*c[/s^0m.'ѫLۇKi2x 27[eکi<֟do&uWzy9pfu0n9ļͲ 'l*ZCf> 'B ^en mϤUt ϶U T\o7SN1wuX=ta4 xt] Yo7)TaQK2U}WnEwMbݦ(dyW9y"z$O0GvT͝D;ܫSfj38ęyq5&.o*)n\Mw߷輜׮**q w.XsC|)`X^V5cT쏫D4Gi-|j^G|h5%|zjs=qTn4Xc ӱ>d_soD5|~׹j40,cԲ=C\GxS(rĻ* kxט{oZ΀2ɼM^f#fPhҪ)yA kfV]oguB^EӘ>ofXu'y泃b>a.>Yvn{cO>X9XhemV{,Kca^kr5^ٻ?ٻER}٥wƘ{C0)IV֝xQa9ɤsi(՞uwue[ag^$ZנiF/(vP[2ƴԖ4iDS f/"a-S_10^] q?m8*{27 QO]>>y]J˨rRZ}yj=6޿= qe._qƞkzkq8W==CCW';_xpo9 /Y/LC`G^:Jvw=˥(qg"a-s0 ~?gFP`4bS,7YԾr nAD=u؈;Aݑ4H@0Oo2ߕ1u_9?[fq7y},ԟة8S?23w46QxcᘤkZ*mLBg&{n^{n^vGl;3Fc;quJ9RYp$8!ՀQ*7ciQ7f{3psz7J4k7X/z_3!Tk㪣M?+wTe@tʖzV|rk]EtO'N91|}~D0οş˞x m4"g8ao^[;cW}u?!\1ޒ q~<{GG<ZcG޾Syt}n=OIxDu g;JcC`W{2@+S~{e 2BafFm`|pB1ió<}N3|/}vpi G,XfdF}Mze*7r-rsr ~Q~7:<(c:W y:ډ]f]x$'Ks8m]VO6i7,Wam@p[<^U>UxKF^?f7{dWD9W!mvxf{`1߾˕yo6I,c s=ysNqnzFs6F5(Olâ6 C*|3 Q'Ԕ-?mW,QmɌG$;Uy @޻X@z\;xXɄX eOB;nnwdpf:[#,xfAZNi.-|3s\r?3D*Z_5F57Wp";6a߁~WQkh..?~~r=T8-Wyu+*}deLA}l!.z Jn ܈-f^s Sꁤw=? Wa[O̰-̙2]1xE_?_jt[+zYSA_n>: rD͢ɗ;|Xd τRB6APRHNLa#cٽؿb?Sͩ8fpIGv9Ys[Y5Џ+B,S#cOySǨ(SSp 2K\eѥU]G_AK(+%^WʍгgU7#-or}8q .|U= ^.B,ϛ;^O$SGFZ*]5؎ziӼ{~y{Ӝea xYߗQG[< Welʽrxi,ԣLql6$T|vkodR-L}a=a^wonL ~V27[y{7MuwSgun3n0y4pc|pc)s2mY ,o m+ 7z6 pwu1 A70x_gL}Sv۶ݤìs׫{i0*gI=HՓhMdPG G:cөANy1_`ƨL)#ٽ„$p\d}$yߕIfO|S7%yItPλI\86Ӆq$$Vm1##=g=vC)S)bC> ew;Fi K9I^ض3cl3}7e 6b/Ɍ^[ ,=i3aX7|q<_|[E,O oGg!wt;֗֕[Ջ5.*n•:ctouޒ1B:%Gw9{DQQK&fklہT#ɠhyl0m/هY{^Q y#FxGɄe䳽=PṊ-Z걆q[Aes1˙sF'pW _y7Z$5xslZHo?=&}Gue\nq_e>{ZgghK*:LTW{O]/rdY.?E/1W޸ _Lٿ{켌k .zp?3/-9j'Ռ?wd/g^gqtKqlfW W۾ҊbeJ6-_+vܿòvrQ(ʼBjpy։E#m_lݸ Wp]4;447>R'cxMыƩh6YCv,__՗tɭ5lϑc#Iɡv{Vʶ0aq c<`H$#+0 4a| e}Ѓ?A3Pg?lp?o'{JX׾\ @{ ǟM=:s@ w1tD/!/aΘ&Ӡk-OC>ޫʇ̽WkR4JS*N'cW. 8D=w9D:ʿc*lG}-k!_0%W &12W[妻tp~|/L;Е>쟮`W|L3i6~+Nգ-goۯRI݄&_ć$>is0#y61LN^+e ]NgӛFР+Lt_~ݙ. @! ;o-d.y,k5E*:]~EEWZ}O@B}]FՎ>o~u7a O(f^mU~5O)UO̿t> |q>x톴Wx;G5r^NY;8ˍ]./=xc5}} 9LxoLag,.1JGgײ>WL!``[sM>+*FZ177+n3N1t9Ͼk#i+P+ 'e){/ "{/ u`%S+/ߞ+.+c?zjM$^5X˖s)ˎH>4X9Aj2w1DuU1'Oµר}* YIȻ}ӺܧaYt?EkF*rYC8Ɋ]0<#A{__P q+ST&Y[2astayep(}UںvL"KT-8 o3&-zY?>f,Mg>o@;-ˆw6mNt}h@'l\j#?N9 &qGnu 9wD6F[yy.Ә8 7=J{5Dc 2e^-h\|aQl۱ed7Gv̥\SWrB_v;O~}VSihPm둍IJ3[;_&ch>SX+c|6]t0t2pej$}b 'e\ze z.2]W:s}_ 8i)~E!1븑ij%\s^_;Mkx ڛLU5=>pM..Ȫ[l llا: Qgsj񬟭ɴiÍ=ZHqo͌w3e{'\tNrvwԹ3ԏ\Gr1DLw ,`?<ΘgH#c^ߟDzRUV~3?:Km< >GQf==Gaciί#X󜙣iau_E>ARy@w8g#4ȧ9S>Ņ}$cqZs{U=ۧI+|T8WpοBPnal3n-[-w|Ok]8 7u@]7tySv1L981ꂩ;/^YK[}lʋ.[',4 />?p\ʠ6S Oa'ϧ`A bL,T~ԁ.¹6Wi]i3 oxk)O$?L|o4cs1?,s'͸{:NFK t.vK;y$c.^g60oa#wyi 4io׋'فËv{jSZ|5s3y꟩.3~6Q[!`^CqM~!"ҭ8a7=ev 7+?4_3yzD^ZL3cϤɜ>jw|1m _d~g  ~͜WK/^8_DqDy=K.K$K;/Q%/vK.Q)/eKETK/Bs(!}_ɿ]s>ϒ_h_Yt^0\"g _|N/Th;dY~1Y!,'tE}f\g}}VUw3`,g/3cܗY1*:cܗyc:b6r[~ey(e#^6`PxbFQ8੗zW Go@?py淆1obx'D.)*Q–|F3r*Mc|/t49̽'@!YR.sR7>>#9 uO}bb&J'(%Ezy= <8+}`pNZ ÷\aݧz]W'9YW}ms0Dz|7H T܇*zR2w/OGƔ8x0\<>Є&loD?5VD'K[\7GxWd-~:Ra+q}b CUecUeGyʔ)tOnCw[\'//fp2a2Rd7[W|eeEdY0rt+$ީL*K)ӭɕ&wq©7gm7gl16[hska-m~'[]58fw30y_z+<zhd|Q`#(A<qyqjs74N/AL}hP^\ϙKp3^3Iɶws <<;Q#M- UCƱSqZ/W[xw\-P[ߕ͛/s&aqh4%V{)G>c~ODjpZYY5g +\# 8>Q&ޘmxnԹěܑ'züUYI\{2"( 3(o^#OJPa:f1~@ڑ g:؟eErYSn<OvìIϙXƱ@/?̝0k$'0=6A#(P|Fl-*6-C̓ly㸠(cyec_2gON/4>!:0w.+#d||ek xRw^nA=n-?q'Jw[cl3_?gXϝ_r]^]Nh yNܳC׽%{n+Om Py5dytPa2x'oQ4[U?+ᾷ~gd.T GLd_𑷸d\%cE]% =@N\wFd:I:07=ԎCO:Yo' cۼyaqfb6/ uL^Zګu[ʿ.?>s-?wlWxtw(A O5Nx 0˼/+o?'lǟ?[.W;6?Lg~YO2KՅgQg1ϮK|:cېr+,=y4?=!Q_=7Pf§]i v} FJp]:J/\z?&|1\xП(#KGhzKBƌB]{M>Pm43S_<ԑ8*^|s1ߊ7Gë»Ny~=?Iv0z gIEGs\b0ƮzQ+/Zکsu Ky<p/8[)|Iͻ.+_$W޿H֔x^?7"妆}̧d0 m3&D/nf>Ϸ*~G:93r}r:3Nq=\g>]]vo(\ɱAv`6*Gkӕ5RI3ޮ>4_D{}X}Kg%Wmʢ^kA/3vM|6 S3B'}oJzx/ { ow\k#8+2(E^?^f:tc\%chtOdt׮sIL7^G:I't%ϤbW~+߱ tnvm3Y3wuץcvnjePs\;w\fC,6">2%}>sI{cFxYrc8IsE?,㒀1..Ҁdn8*e%rf< 8 :IwȦTI7|*V6uQ.c":,g1pﷇ1ardegz1S1U1D*qcqoྉk}cfW󒥌}j)s%` "ooXNs,O-a Ss#*,1=Jo+&E9zV=4'ޘ^r:~sp8c<'cyx.fn >Ey߉7c߸s-Q>wân'X˸\1fBki62/BqCOڀNY"F=ƸwV);h$u%.iX^~KGo.EAfŨ[(?+ {`/ ^88_/ hL A('ی[`LHPπ6ُ81[OyCH ڤ";S=oBᬮǦyq>E,oz >6eyc.)Xf2Ux7U ڃj2Ny;azd/QJ~;oaQ_r4NQY>:@җŽ7u6=kS4L8/c/֓Ʒ k6DwK7kMjDE=ys[.3-BSȝg=Sߓq7 Jy7rD5O8}S|xܙzvv>:x"x*?w=f)-gb,!mTq\./%ɟ <psƉb|wR0n,;?<?=YbvQ\΋|Vm!,Op(a 3GeƩ8psƱ;1.]/% <va~˚ cXwRZPf{Gq[uVqUp@*λN]">%uǶsDPyMIh~bU+f˻ l ={6{ӬǪ|MvUPĝxȮ8w{"eK<{T1[>Ʈ>~Q qZM`JOu˟H?7WƱ0tFm'`^843>0x>xzhp|Gq+M "D/=cM9s fP'-]UZt_2ـPu` Ӊ|:@z??-8Y6D/+cBBzgt;gX-lP_ v _~O|<}'^^]) qNw/MNx>E'=E|\D}}÷OhV3l7vg{Gi^8OiTsE,눔ι5,%13 Nc_gH!ޫoÒ/'A գ^+z7qed]Xiyo>Ws9)lr^?ʆe˳Jڇ ˗4* /Hf#蝁9fMs*04ˬEJjߘ; 8eq*>}Nu{A?E8KZ&>e:f`,6ffW7 qX\FtHיSEa 6&h V4̲͡>WJ4pMv]VEJu6lhmy||gۼswq#m}=K;(.$ZgO{\;}#L_#dTr[L*3b'ʟkE(;[.2o,:*j-bo)k%WK&k/c諛k븃A۽#-f#jzZ Wsu{^g.x9%<í30cq"W8xg(N*,|bSdE*p1xR~ߘ楲_t́0 adotʉN!ӷri;ht7<|NϽG;3BX,ڛ>U{{gqv{lҷ+{o:8[ 浲NN0]黙3理a(\Un)ؿMnYJ2TzdY6w2 Ҋ]uYs3}䟕TcNcr!Q%^3iBُ/[VNg >û+&/3=fZӷyֿN .݁P;tE{6:|!Lw,@Ȟ_ ޤWl-/_}J_O DY]DŽos#8/=Z=?fE{v@8q]2\lqt^e^ fONK|aĵ[?:T4ݮ`Zqq.1t['?La @]e,s%B؎ @8ߥ=ALC:4͏uj?-k)$TQi Q^!s m>MqIZ4)95YO5lIWY GPx('*O^A&BDSզ |M,O-Kep +ɾ\YG3e*CgHw^Ռ)<)OGٔ,ZA)I_Mq׀qX_tJ6#])XF4tFEk*b opIKᴪXTUL[PR/qe߉bݛF̠_ . (W w zZ^q25w=t]dgJN*\ky&%'}XL7趸¸˔1 }w> @j9\.L::_ǔ8\uJIU ܲBN t*e/VrXϑCUp_~y(י+E q%.ў闺;t=S<8qeugo t\t/|3 u^}H|_Ṓ׸߃a@ۀugrRp>kP_09'b,Au!_ܒ]p+ԏt?(+0ߑ.8/%5>k2raz~]uQh;{-@^ʸc'@Lw3@yߙ+=Fr3@9pKK)Ry)twMBkk+zkT<#9L1 z Ʊ_,f:ȞF10/``Gd,YbPkƴ9 ΦpY+uʂ-S椼z飕-&Ȗ)SO{#]q<ۦ8}5QqܡIOꑛ I3_g=mlԛL㢗'nCLI>B'k>x&^+pY]Fߘt]V.ԹL,H{52US>L+\i=%K46*2' j+z |{/[ u6HYb:1}QFM cW'gp%]`88\d¹g:WÅpղ /ҟ;ҧ M\ }&>;rqJ*>+DrL_ %.NɬÉ8| egtx\~^x\̧przc=}o[8|q<֋{0}=Wz+$5p7Bg>r0]C+cjǬsI<^<ؔΐuJX(2sZCwޚ<\ 'CB?oCݮ ;W G8}-][3_LFog6*?)ƽ[^\ۻqICH^ܭ,@|.o ~_P92iև|=L1}Fu@XɶUL"႕qt<~'JmqQeƸ.[1 [7?8{3᪼PT>?VM5 N~e;4]g4JW| f>lGh['~6solSXw*Vxi MYlZd^*@6dz 6+1U{?}׃uxB:LQоoPk2|q#|Ѧ /nUl > i_^Jiߡ - ߜ|sXG "+D:^tn!B 2vL!VE<}$l=N 6&aSbq;[I OTxٕGU$ AEN$ VTVQ0"F *t/pEEBИ@YŋzUf{鮮>^U8?@.:h w 𑭃~El__y^Ò{ ӝ}wY(=fE؇ʭk^k sVDž [W5KpL6a &_i{˙MCo׀CݧgiC]#j>1&mؠ%>Ƴm~淎N㻢/^˧py~ӿDߚ D.I(8G!1@`aO1{1ѮMmzV76|4 $W=D%8箴>1 W7$'cSrש71sVI&=b v]4kǰ?M lwڟ>K38vs&k(j!?懲q-?&@a"0Ax'_o"㰻=6lH1Mƾ937%}h[j߹x2eii+ҫ˚ilӲ)H4[1'\x|SΙ?%׷9[fbٴͬ1Ec0.lOtZ'1 cOSQ]h1D>SӚumew|"I3?Փ{oFp8{>i{[cO̥6Q_5Q]-/V-l~&v?$bopNZӈio'H1W&x[NL"=8x)_I$҇_&QwOWN{L~~$v>e3/fA2ɒd3 luT~m_GmMI_iKls_'Kv9^N-I3t-jQ3~4] nO]mc!,b\Bp[-/-+&%ϦW,O xQ8O8\„C"1;guj I-ƺ`+^)[|'`ɗHi/-tN|%)! |@-v-b-};-*EX^Afex"~6!o}Yۈbu9o6PzmڬTJ{>~ٯ]%YF~WmiCi4>L#}? ">'|iī.͠0osL3i#51 /Bbv8Qۋ}\ ݩWC:}:~F3^9:gv<{Lr &C B*,g>S\#i~ k̼Vt/\?+#/EI=8Џ:r}.3OplÆlg?@vN3q.!33*$.,F.(<آr2RaJjjQY</\Dz_jQ9ގ/zeQ^_- ߸LgQ|^z7"='YTq-Hx+mQw mUwqnўg =( =hH,گ<lls,?ϳx-GS?>x.:`k郟h+ezI24 œ~zju>5 ƩQ!Jqs 5Np||"l%6Z87DƵ8q}뱏9'5?G`:+Pm[$ӗ ϽYtG'w蛍E[{FTc;sxx fi,9^ic9-&^_{=8Fzfh6ofk[ 3fƙWtP3E'esZ&eqx1gk(.sGױ|VH?(ZTƘFy5[5, Ndiw+_p8Q "^LKl%LE9^AǕ|TvK=_Z"|`I̹Nky(AÃ^/a$}iģ$*|3< .۪ 3Ip5Aʤp:~ iqZ#LOO(+z_'?)/?+Bȫ QGu<2F^k06_=G(^ﵢ~wqt RLJ!p̱~6ׇ31,!M!F|fV; ;9>qA,n <5D\:L]r;OG0yNt*!M|[e{̂{f\?27 O(.]!C eC)!d|{7P9欎9⬎)Xإ4I&&{_n{)rqƜr rlۓ:|U e.˱Ϸ10|Cg\+xt>$m|3wY_cK>hx/el60]|W4v<|RvAô*#{^9]7iwiwYG9>w܅lgIa@Y/ey)fxK=}r$ҋ>S0x^v_ ̟|`};w606rރߧ?0K,W_i/g8#7'}fU.A:L.Az] {7Yam,G-Y tt( s59\u2=ܽ4Ѝq˥ LEs:|(5tߏ DŽss}.皱\s@(w_ 9ϓgiG?P/F-36sYkxƣ)wc?;nE.|3k A1c '-+ۛTtM 4l[,-xjhou>@\s̹Uzma?q<O|Zϧ5>:(ݪM|o |O+6fQf)'8|pߕ.ᛔzv/wJ ˧{R=Lrϧ9$k#e"eƺ~ HG3$gFpϢ5#D mGK#):{5-:/ʯֽP9^ ow^y^|x/w7yqשOcLYiLL|VWt4Gt !^63wq5.S=},׶ĶkvG}NfDћٍn鵽 ɞ]ێ`9 7+Boǜ8oOrv*\D֒)介h\ӻEdEf:FG)sIfM(`k]IzFusM,&[ѿrljyu bm6gAӟ 2SY}&lb=<~p<-Nmᰗ}{uӿ.˹Ue{I!#b?+|캵2uk g;IJC;1lz>]cA'xNǻ~=T/A3C {H,_?wdb~>言4c)Z<4@ 4nAsPO  WohPew[E!)3]}*&+]yM-Nyϣtgwu.w E\QUAW>&x,q?m{ (.DYnۮ̣ٿcW~ b ?Ýw'~uEyՑ09#Z N_# uO OZvOv*eL}:Оmu^ &2Hx?A_2󙏞oIB ev2qMphۦxL_g: |X&:k5#G4ֹ!OuEM_d O1%DzPy+:@p?;RIN5:o@w=5%?1 rXC(? k)/XL^y@JMWڶ~REmaWJPM_i!FA>ߵ@se>Cir#]])'=[noyT}VrÿR\f|KIffo)|1yfd1 ˷M]=s_rN 5GF65wྦIv!lkuOIx1}|eT=*H' <ڿ=TA s94p.Ϭ.r+,+,ԞLrI%7*RIg,Ut@N[ &V˜*: `]'T-_^50ڱ;i ?=6q GxB@E88t3"/aBYzo=x>V?7R*[|cEWުVo ۡN}{.tvU|>׺7жVoxroajf+G>vGV虜.]5iek)u: )Gg9)q?G'!nAwO@^ M|mdrgm~ \'"֑KM Js Au[W=@e d__=Rkh$_$p? Rp.nk921\8/GߨWtc}IWj^ý P"] xUE>Y$e]D6AD "ʾ&!7@B (x ۈ(&(%00((LխەC鿪TݧQL?ǓV.9Zx m٫0# 8FEu8 m5 @t:~ >簑? &j :<Gd7(1@wf3;='z.`T?#/:JڿvHe/^xevE!?jy^+&;+vdUυuHl3.X*قƸP@QP{WKVɾ0N>UgG8`/b:*܏2Wk4Bwă㠛'2>GSx^)fԽ/mou[1p b_;_Ϲ~5\1Wh5$E+z*C[}`B1Wۡᆱ~iEx[DqP*z^i/dܫN{p@]cdh]ۚq_ LӲ*2'vnY9md|c~"u&qGRk8ԓ pˊlV>_qw9c~`zUˏ͸8_L`e1=Eϗ.}0A]21{YZ5˳`oᶳ~mc-CWp~">nŖMwp[5_q7 Iʷٽʏk\O38dzt3\>6Ŋؿ' :jvbulaϵ:oOg n5twbá03sP2G3cǥ2"༊?ﵾFnoܡmkQ}_㣯=A0:l=x|_߷UFI|p:Ν>f/|ÑjoJi:Q~c63Uni ÐZԸ6D5:C9};=K9XCeoiWGǏQO5C/KݚwVw6#|^oxls2Tyx$0UԠpV J pkllל]W'! !$ur 8?Нxh %>jWJ躵(/$P|ћ _#$P|q$N&P|ѷ _HE_0Z$R|QD/z Ѭ}z4h7 Ge9с32L:?M<Ї7:8`Lrj}IdIdId$kn$ߒȮ[ȮȮ'ȮȮaLvmLvLvLvLq[UL(<٬]lNn`ƲPdZORa%:5YDLc}azݝbfe1)Tj| tZ 4?l:/l\ tu G)dӊs {\^%w˻ERy7RyST^T*oH*J=Ju8=PQ3J嗥RKRS͜O2> F12:NODR==<0J9 !>#qS✃`l<zgA@I51?uۢAaڥ K{FuPO5?5*\dzؽ#i&K׌old乸+p}8yYO\wű>^ğԁ`v 2~rN+VՌWvv]5@ϴ/§)V}>q 6Ɍdm2SdB=~?RnEk=t.scX J??{q~ր1Ռ^pnFm.R|x8oh/aLj/v"("zcSaMQ_ JGBVw#<z }|Oq3ӣ/O\?7p~RR?g>!&sWI?r7sӜj c}}̿)}eN)'w[|uө-I'Uߔ8׎N߆a4~o/- 뮰7\ƽpxOT_V;Ӝ*IY}_zen?>a¾t7d?9'59'ujgs44ezopP4|>'^%(%'1}LX|+;@sa]x/8. ~膢?hcGF`m2$0Mk {T/ԏ sS?QԏMԏnsS_vn湩/;Mn,w`#[4ܴ䂛֑\qӸ^#iAs(2hI AHd\ ZG{Bg:EdM#٘AgJ=uR=iK4Ԍh$!m i H Ҷߧ i;H@zS mLHq=%7u3N3?]!5VWHcfR{һ!\2Nxh&3y|6L^#;ӆy2iĘLiI)w&LLo&òL g?$IP/,~Y#d?"ؑEPES1M2k6l' &ؘMI6ÿ~&hC>3F5i\*Y4c:~<̘T\*'+)˥rϥ:{-lk.هTgTgrBṇ:kGcGQӎ/zI9lB:zuؚwu M&-(]L/7̘Lfn}AJ?ydV0A>d[>]yv>||E/?Op8l/ Y@7,0>[1 g31qұt\Zs6q?q=fV`. ΕKnYY[Y[YۤYیYW?sfHY>f\bLD!sK9LB[!?-7dXUXE$__d[d"eldXe|[t\!yH]<ȘΘ83Cxl!;,^^PCmj{;6SY@r+Gc׀Y[ށe:6ԭ86rRDW KGLo\j}2Gunh3{E,Uh~ƣg/|27}(9ဩ0_:`>c0j.8(cn.: W^٪ueW}]\W6qtdA oVEoU6EoWE͇Z~ڃ@`-uG}!2& uy(g.'8j+?yziM3|lE=^n5S1R(?x>r*9bLBϾ&1 CC Dz(rr1_=yW{ZT~Wc=y=3~_+񖯱X=]fnܡ&Ix̯fәRyݢl%cDXK7'گS9xuqA 5qeA=Ѿ]{23۽ ߢg<Ƹ涺[J&y(07E3-!R3XNkUn)b|[{T.{'=Ye)ڎ k;9#p~̟h%('n:JuWjI?Bg{)_\,'ȉe23k(z\XhW4,ƸPv%k_ڶ:Y8 (47wlg̵(^SSB D?@^wm~.}P#ڈtq9+B?l?[VT}`3 ?-g*a-1u?7.֚n-1>ߏcN Xb7\ohX1\ ܈_:]^ {KIAŗRWҼ|Yj$.uMd2O;rit_"{# nsG<S"c8d̢y$w<<<=.h׮@ڳ_Kި2)#ސHu٩*8dϵXfс3iyr_q˶)9XL>S)?9yx`T6=|Q N)Y#wLJ<44ʉgyL_.x/^Q2}#.@.!8oD>ЗD>TN԰og og-/eWUϽ\@.jꁨ=,F)˰Ȭ14Ӊ^fcj͞i!R.'"h>|MVưQ'jZu^]}Z쳟jNql'cY0..3uu/1 +bs3Vy>ob)n ]"V;ux$Ɠҩ\-7YUOuy^=~Nx!~sǁ:R?ߚC6?t!P=0tGdPZ _A})pG==?;ӜϪ>̞W~oKϥP2$%;t&L^&}[n_g9Usԇg>죙1Y_',@ɼUĥ2L9Is Ⱦz*˨G8S}I?իy&zK2>v 33D b˖:.-h\?Vӹ=g(G&,].r?*O TGe*x]-;idyĹ_&Nl&52|^9#gӷ&]PSrl:E\$=O="KiieڐAw#ȖfBax1ds) p%/>DR]wG2| 32We4C}?Š'q D H$|P?4 xP)J't~q Yq+gޖc'a:xG 8{8.q6m; J?0W7% 6f[t5"ߜKuD2N3LW~O+-o yeO^s;~N@<Ȍ'xqV٢>f.#xG\ƴ"o#^5{\&,\z'x׏'FQ,%\W:+֊KfR/n!P襶@^xKWy)aKm9opp1-^:l (Σ|M'2Swx.~ ,02_ę,57x3 r}㽔W,꟟,<*J2?fyxNzϹ)9_w\?vGZNd~`,ap1v9ڗ{s%d~,qZ$Um`/DZ; 7Qޮ߮8?ƷoooT%|1e\qi|80Zg!7j bx"~;06:Z95=a&s8&nd43ӽd?<>p<>R챾N("ʅ-)O_;YacV; -_)}_#]j֬O=3>zOFGֱ=jJ{fSqi]QK=_i5yzM̀{u.6aTa5?N2M0ύQ0=+}n y?Bm slPp0yy\}\0m8,@S~OעA xEkF$1Vv[?~ vkHNcWvߒd mpfȥ'Wp<?w7$5>;h)'vބ| }{SȺ ]Gl(ɼVC-eAw W'G9~}2 g /NgiZT̎K$bL[{ R cU6\O7ynZ;ϖ|ßخLO 8 xLXtCp"CuAۘb=X>?(/vJ`~g9on]GG TG@8&`y̏|cƺ(oWGnJ%һy牢1,^Rc40Z̿c vul[~i`B? !fx+~k2>6XA k De?\_*31|\L ̿&ZS72?Woopfw ,yש>U_fT\[y"XSd|~oŸe2\z]Sc/*F޷w1ouhnш03Yu}ؔtk[n\˱YP{N<_9ƷcL%q~3gD%5d dIdT}$RIrx&7y7V-pvWYdqO=JJN"_ۈvVrׁN-g[#=Fu:~ͼEv!4ns,E^ʇW|x^y5]EJ_u6d09|2` 8/(;[ViyW&] 7#^ynz:#nߞN0|Uv'350=oua[ݹww q$NdջZٲøyNc݀rS ^zEk v(""bÊ@(" "-@0̙sO {'9sw?3;;umIp/ ?2 ̆Ʒs8oRC=Q X?5 /:;p@skŸ[r>K{q=rv9?Rqf.A[ Ҹ- P*?CTB'i몱w_:#[6.@xĮ,x3b/ț+L8ct`(LLw`< aLD0f0YD)Lc;0 ͘L00 STa20qSbiWP 8+> sZ _x+ƬbWlrXnzM_OcEi,zr]Sxv=uJ{@= R)tHjߓRgShj "ڮRj. "6 jl t@6xh`Kg?<3Ͽxl$;uHc{lkm4?suJXr<>$ 6 }t?9KAD'0p^Z‹[o(Y z7xyݖq$BNl}q9G㵍wNn ڡn{~[N~ה.8==E&3˫Qlo\Y/I;mO}Fվ;xRx 38|dP23.w6ǼTye? |HO|n)Ey(!1YSVw=)zxӢ7)z 94?t)32Au~@Of/0})5>ldZ9#capTOp)ƧM1lpz:O73Y0]$3cg<2Mgn2cGXֽ}g7s?DMTԋG/*mfnqf: S/Yl7Sx4e\vef}@;U88 밁Rf`X$ɛ(=1,7s!6qM:jyCqk@_LÙ%žH8ԭ@B`[a9s).؄;Wi\j UoH3Lm!oycm1hb.S_ ~} 1z8?6Ux%z2n#n.yCCpV]x`ܶpK}qރwoYn+1nwnϸ=އw[:w܇q7cߕN,D|>b+]Oсx>q' OƝ*޳@/o=T/mʇP n+$;y^£0Nx(wq>38<9qLRsQiGצܗ%w#j80ieo@{c;^s2.Wq_+->GoWbz6oصNEWt]4[G=[e A}]32J~ۍy{Jnv3H~/.:6{y.9.cX]b02fQޭv4lco0+4w@q>c3/?Ĺ /&L:;uؿRt8[U6L]Tf`Rp\ K&9ʀ?D2%ukno@:dJd)*}1$}.I_L;I.v:vL;ch1p!V @i2JsNr;<f_} {!vXdx{MEc/[ɿ:uۿ{pd':B!2&T<2vaJpC c,$7R7,d8B=uy h/]HXoZHߝO>]@26m˓;!7~RHxG!ϱB'?]0~ ? G /?o m>6ں g7Pƃ |6sz.u<G>>z{Aqc{br{i|Us/ZU47FW*iv~ =v, __k(&9KQԖ ہ.=.;FbofHN&1'}{iq ђ)o4.4Hc\]t'؏2WXLϴ >mwp$3|ʟ8 /ߕ忯(O%v]9X2S9TxP^N@|Ȳx$DO'IM:v!:s_ v gzߞWSBEwsH]'?2cU|?Wy3S]QP)YFKtr*:΢Çir O{^[~*b}+RoϽOp2%}3}bF3q/I1/Q6w>c)w>/s{S33ܫS{r0sw>B? ^OYp*TTVu;'ner_Q鳩=E=P8o_ەީcVT򷱌v|ۧ@?D˷d 7Isߩ8}h܋eZ;w/l$~9͢oyi|X:Q.9?e쿷U1f?w6JWr79'e@hJm[d#gAı*6uyVV!ݱoxloB=@zRbiٯiYsL5^f #Sԋ2s4&(}a? ɣpT>9^@=TMyYXf^ TudZ߁J-ڶˈ-Ų>heױ|ݒ?{{_il<]a>wxo2OX?S{*R=^.:,h}p2C=zEz`I'`!5hl6$}=hAYggHY Ve ROm_%}i7ty.N-pUU1lc)[rFR׈e ~S( ӎ0y{pseΊõ1ẘo:]ȹ[,C~ |-$>/_Æ%G*)VAE>#>Cޭ+zm试*EQ^ m-qwSM录SE腊~Co+-E/R聊)E߭衊KCvn莊jc#R ,27ԌBd 8>wE/WE.[&[Xw IGQ:rza+\ӂc{i&9YNC81>n5'#cC݌fAe~=n{q$ 3xg8N%'{D͸{#.(<9o)_x%OO0.a<;ε;0i cǸaY+o?k_#:s>Q*zg+KEK/.=Qt1CW EWt00ŸBѩ{n{.'{?P{(4Ol|_SoRm? 7UwMbˀ-͞׷V<Ú4Z3N~։4sx+>Nub:1ޱ{,k{d.c2rϥ@½(' ~g'w~.-pAeʶwt lu8[8:53ߊTN='؟{#y{W7kܜ&yhY޸c%vsZ3O]f)z6Ӳ{z4^3--{'\8Mf4 1 |0J7{>bz7(˳{ބtv'qDlgB1_VcgJ샾zַ4NUeNP>GOu;$;I$w;e'iwt:ᮖn,9}?>{ݥ%.ĵV-wRm&cqϲF1 =M{GUK:s+Ì=n߄xW277ϭ*ootr/WfK{* .Q(~;WY-["+Ώ'a>G딇`2Ob0+ҼΗ;u?)9=D6z{84td=mSa4(};g 2s~cWCu^^ѽ ^jL/ƝE.<59_hzy/W|i>n滠ꅇwj߅y&GX0¸u G9pMU)ʳo\%#Ϗ $㦒qˏ {۟&,eD˜|`p'1Ü;ϭy| Tr2Akho*,cɊyA-0Ü#riOeP_ڙ2,;ݟ;5$ Pa!&,gڣ0ʙ:2h?2e ' b3Fu7 E*R%?/Asز.3-璆XL/w%Y/ߺ$P Jw~r.WRqIV`MS`z-,ݺIbYԿјYE&1Ɍy1_3fGElʛU7%9E2,QiwMT٦owWV8GxD6߄eDλlt}]zd/6QW&I걉캕RmK2ͼ K=MfWgB:UߞeK;܆lN`N@Zmêg>(ԶZuܞӦ:u|SR5.ɩ iӂiIo #(~# .3|;*˧ ;{1~\_d=bGaw<=~9Cw9K^* 8/|R;saֆǠ/c-X02H{? ,ӒT{' g"pܛsT1nvY#R 7F)cŸf oƲG.Jwޭ3cDDi 8+0fzv@yxv/ګ Vz0n;x^sdm*OPqw͊.3p匟Zܓ1:ap~d~G‰ CKVᗵXYf#"v #E~YtU@x!9Nc%G h#hmR;D{OeπPۋ{瘳>-w3ɡ>F>˙^9C}sxΡ9rhF0/Dx?D4n3iYC>kʽ+ӋO~hM]9Iesfy8ZOߚ}궷<O"@oˠ/٠vq|o _ES![]ϓ;oƸF1f;$Zyt`]z;>P1:)Sm~69s[h۝"8ց+Ccϣl[!C Hw; y ﺈ׻zw@}Ayj<=4ؠ ǓGW2:gE#٠9~]C~ZI%.uVFL#@\$!/ÎPRrRmߊ>轊CG ~W3}p9/mh#?"i958H~3=Le|='2(O |p/e- WpA~pr^w0Ε鋓|^)qqAlw܏Be?=?,O \%("J/VKTq8b.|~/<1ݱ/q4O<0qq*vw,cؿx&1Kꭶ~S~gʽDTej/S{T2a4JL㡾2Li~R,:?ϙL>x]jhy }sF+1o:t9n[&I\b&ծ6*qWc?v(k\.n&`Uߤtն=žw5οX_8~`g-]5x*ݏ*ߛХ3.,N:O#ioe0ѽUVPGw=t ],İtS tk-<# 6׈5*gCLTY\}9γI}p\L˻烋nw,Ⱥ3lGΟnI6zәP,3jxXЧU&jm2iL3׀i/=U2+5E#sO$'<_wG-pxى[]+)umEuyacSB5o#=~x+EWWJ k55w G WW-#z([G٥j{:ҺRt[#iL)k uS7E_2ڑ圇1KSS9?HA|}7!vZ#n+@:tvj獯S;:\6uZu]ש]>CzZ\3%UrIF\;|lJ98NFOktݓK<`p.>/5xWOwE#d>K ^ɹW@f2]~X~99fݘKu;ޣx \C\s\3?qřs0r\%k%Ygj}DYyAGx~gޠ{waVa)cKkO4s iIj`? 徬DGײf/a$jL,sQ&Xg0kz N cas3F80|3);O4e1{3`Xwy$˷_<>}FoR:byP>j#c W]6ܤz{nc7i4wy4wy܊J"^LGKJx]]0QGl=WxJu]12X&Y@*:ktO %>Q85=W+me>mX?22 xvȫ2[Q!̃ͥυ6~oB1M3~JQqEVc1^EߣjY\EsC3{<_Ot09YW Ԫ3U/u`W+g8S`8ܻۣ?_N񙄛)[ _EXQ{9䜟}zޟ\/b;~i,mXrW ^dXw7. g1f#@a̿ԑf48m2n#uuEK+aswm/XA]$]zK~KⰏL ?8 8px'Sv|;t qkc+W^L1Ưrpk UOӘ G:0w6ȡ+1UCLڍeCo̸s9rO'[q~u!I^ޅ[C\ඃ7}Jy@9;AAf?iV<в VR47v>AL>x#IxJ CX-y[2slUL_]p}#Wt|̥hIݿ)}vr5@-tKܧ4GE8N W}FS{"츯 H֋g=^)H){pkMpj NQp$3#ߪ#^I8Nﰣ|J<4@*83*p2Z:U_N_[Eۙe8S J=>2q me]ێc\h[)1CoL=Y{q|X/ngYƅ8_)<,CʻlOr/:ʽ9"?2KʸE˰˸J?'IJNՖytۣnxqӬ۟}rǃ,z?&^k YzD3띛}@AY.g 2>'~q9Y }2Γg=Pۈ-t ^iieXPrJ6BJ6|k"k cD:PNw rȁF!qW\s1fwGuye.xwo+rr˺#]ڥ8FErp 9q]f~EҶ(6P+:z**;s,9*`>F%ZviG>'*#Sv?A̢u)o'lWvEp_!w|L;I^5oFeV] 3}GNg(3L"?06µQ_è"]aw:1uѩNJopl3@Q޽R&Wc=N\O}:DuXDv{R){:8rlxS雯rE| `쳑)%M{tcP؏xm¸ 6{2О"nJ<{pQat"o t"TNJQ;VL:;M{fq\+z3g2WS*LߠW} j^=!JA"X0υ½n_wߩ=']{6A:-XFElN3~[emż k75JaI_\mw[U1=fd&6-13Q'ф"`xsݰD,*r.W!05{ݿq0#lۼZQ2bkuE@$b6*5pޭ"mrvXn/mKi΅1oSKK6)\̆& m}0tGfV+Tt7EwVt#EwTt'EwPtSE7Stsx-Zʝmc:cY2fp~#-iepB`θU߀?VΥľp_|;M[<>z"|([ƻ㖵|gd;`9(l_øQ;=N hUUt[\'I<]~[ P fG\e'/$]RgLf?e+"NYƩv{76Eze|%BZE|"E5vBQEQK(NǙ+Qvz/]Txcu4cKXrX79h F42MaMaESXoDSXDSXMX2Ma8<Kccb[+c{Oy&0v6c1{o{w'9Y>eA-H1vWt䕹dPS? rݦ'X~~48U cAݤS;PfXώgZ1α-|,QmbEn跕rKգ8=ԃ~>zl49}NҾ~F4c+yϱM{b/3ݺK2( E)_/Je}Q*[Rz(ElM/JekaQ*[+RZo+/j,xwAq4U޴75tG#]fƑ.đ.[H8%Rҹ22A1e]13AAr0#`sSX/wY>J&g*vX-8] ~ 0gbqIWO{9_h,cOG<'~ 7_fqsWE빇6=Ѐ}I|d4;/ȼMO R%Xk'Rgˢ0X=gO^=G2y_;Dۗ~{=G[;_{WVneޢ_xFcY}RsZ\߸RּhG;Oz/~o'(e5(c1r{ GKQCok㙤)Nf)ZKw\)Yv~SsGXo- tPʳJYgƥxjńլ{_aW{B 6}K<ZWY TXKJJS/>ú6 wUɋ @/Zg}/]&ֻߪc? [O-Cy2?-c[8s=c,ofy;X:Ug.Mecd{iж3P8WMnX"dQt ̺aWטe *my/?@gzdYZƸYYMJfSZ:daOgC0ͻh3d9/Q.blgR-M;D+*o~]Peeݷ<v6DU9lOQGKQ 3F7p9m}1|{r/G:J.QN3~Ҧ{J# MWq|BѾFg^To ^K*`\SC99<܋5(q=˸Fx{ZaxZps"*enO4ys#j,aWS]iyv*%uqi(=aQu7t PW% nܫ 'Mͷ T/*L@S`=T4@aj&P?gL~g~@}_hX\M4T";T=!C+cɊ42"+{A»H}Lx'Vyi>3R}HJ4փN7~9?wxowo'B*U&5L6Pl42(oޅna㌽ɼ%"[ vBW!g; ~^fGJU(mz?92UIfӪ$U9*|*|*eJ2UNFݲ SšQzs5 ko5 jVTu nu [uI$$smuQw9<]x* =0iμk5wr ]/5H(I$Ykk5^ sĘrM X- V- M-Z^բZkQ-^g(-(c(k(b))~y}6)km)MX9k)bu(;PXPX֡fաCa֡ԡ"$ I;Yr';kzTRS.;]R3.;3Re]wvץzR]wգzi=Lza(n\d=Zx듬w득~OvQ̕G,;4 ] H V6ӀdgYy)%ICܐO5z)%2O&cq;H6"x13ϼFQ|~hD(:eY7XV$UcuOc51јdИ쾿1Å&$n1MHv&$Un_Mbl'OdcҰ٘v1evMInRSbS\ƚαͨ5lFelh3*cQ[ڌfT2QlNe~s*cSМT642՜XX mTť} -(.ZP\o_/[E^g=Sn/Z+~^NVtGWyE^PL?a˾;:ZG/wsj0ӷHy^l/s=*ֻgVfϢ\Lv0w^ yw-%scaR;;w|%TW+Tu8Gw(+yy~J}pzq+h|G$_x\c|{JojEe]DdH̑_0^֤$eBA)ٚʭ+7nӚqךO[S=u9G7yK!mL@m~1v~q 6^kmKҞ*=.+yjʏ&,\b}ml$͠@r逿-?ȯ|[-ٻё^?;_ b9 k,gŅꍸɌP; x;M~XOڶ<ߖ鶔xsH~ێx#$Lhg򻔫]~)_nG=61sp"h±])j+(Ѭ3H{qvD:۷ϳQWⶠ \[Xネ9ewS܂W 9ߡ^>;CKJ[ѽ'@ۄq`>֚3, 1l냼r (eI=PxуكxG qk8xW_겖@E7=պDuS30io-y> .2-cz@5zR=WoV~tYwwL ʅM?q*ye[zRz{{{"g9c62^+&v;`uכЛSSC3sv5mzm%CNS )@ iýA/k껀sExSi*_; ʁSrI8GDݪ}̘XdL#sxɖ[½Q>HZ=d%$/7Kڈܭ}) R\ܗ}Nÿ3 g[gzPLK}):c_jѼvziTEqQC͹ޏ(ZSh3H;ɍ5{+řo~-E u>ۦߖ~ʫ_?1AddUu?t1~r6˚܏sa?J;֏3nOS0?Ok9sӟ9>w[>{?v#׸tys1NtEs\ma<0N)n,{^ w6 ^ձ3Ӂ.=3^'=e{zicS]Y֏L2O'A_S#oՋ@O`5{OJ'}{nDuY9Uէ7[kP{CfN=O՛,WɜOf\?`ЋYwYM{^u)~[,3O~t{n8"i g/pr^7ev<~e2eE@Vy sÂE'[zizhEG*OHPlZEy>sz3͢oǺE{3\*>g7)ڥ]6MFOUS{2R80d]Qz;݁hG~R},|hwP_iƯuE]Ư'|8‘5[*{s S}?'$DwHK'~;6;eE[_6cՁsʻθp"vA,7݉屓:QAn2c$3Z ;=npo]ivDɈr~o4#n0!(6Oy_i O/K(.?\U޶eH6`U{ɛPl!_i。d}`[PNpts0aUXeA[{EyyAmER7|Um۴+a; mdth;r:JL Gq2.+@9ݫ0tBpMw֚:lz~9q'e쯩_lu1.+7c^d:A}]v@)XPowNvSj}U+8HWN^úަ]/|`z%oA9\ @; <]c(&ݗ4DQCqC̞&c:C aw_tЃ'@uZm3w%t"#_3eZ},sO*zm8 ?5>㛦?= tTŲw& ˄e "",""S(< $$!YD|;<QMFpnUOt&?έܮ^ou({"=La.ElM?32}Go=~eYO^Ds\:GzPNT1.~YR|LLUC] +YKwom±E0. "GQAmA(Ad(~ Eh$(^7}4Ka nԶH*SD$2D2>ᖎVf.[|շ\ׂ,*#_v),**3Rf~b]ϲT60OO)}tNy:؏KW /Dž~ᲷQTQzyDaGy#7wB^VoyDE ugH<D =fExLouwN({q6Pmd7Ӽ][>GQ@sVŊB9*(G_Vگm>۪(:T*| %Ym#Wū{)pM-mc; }dxP*!KAmoۘ1t DSh XS~/0\c03}Ԗ -k6T[oabK-NF5j՚Spl=urbbhq`Y[øikC: GbH'!2tBXmXa@,}pK,}Xab,͝ BN,}PKvVU,}8AXKo6;bb?R\qThAx zMX߹qTp)s.눷b彉yG!Oko7x!oÜ5D/km]|8|>0yi9w.'ǘxx s."‰A$ \7cC.`GAS>]A$/zsؚO&9/{2~a?9O}o]6^G1yG4}?gc,H8JCD܂~ oފ~?R9%?uL3N%}< ߊu@[@qX60i]<@g|q$XO̸O"70H< S&R T"o(ئDvP<]"/(Ʒe)d=ML"Y O"L"LH"$% yITJ~.NN:O䖶ݻ!2m2}wץ(<T[KAޙplG0]#A@mbK68|wvIh aDd.`['uʥ%}>d} YU zw elc> pE=\p5doW;tOm>w ZŹBWa9oy~!7Oq[\6)n|aշUX^uw8"a([W%(>>{g#/ >&_UbzPy[YsD嚠e xSnEl e}#[O| tG)Wn'}:JO*nw.!>8.0C!j6 7k\OU{SiamH}2+쓹idL#4;.*iS3^"lUgWO1g//^%=ϭ x?-`e8yF[o afA'gQb/3[O5}`^ccEscxMD|_y,LL5|a̓k=oO z(cU^TGy&_~ALpNe{<*[B}mK.B[sZC{I'lS? qW{[QwMShgt,Wp:x-2W2m9(/Wq(ii!]|'u ' 9*Mt]w ޕ0h2-2tK)5mr}㺨h[}ܹe@T'+2kAb{Bڤm(￸,|Їʃr@>Q/ѺApEry̅ @=+A-vzx v.[Q o+2Hg]3+mATʩ§rNu;Si}d* Zg`H&3'PI>4_~I>Lfp@ ͢X@,:h}?1-πא\BGAÖ m} ;0veQ62OI eaրm]&ozG5J5ZHev|s?㳵:6[T==똾(/>MAx2qޠ(0:{v>?v\ eyَ"?\u-[DMAL|}LK*굞}W4}E[g<~8NS N5 sj/l4 <z GPz0st!G{rt}%,NjیWǕw .3^+SNLg`hd_-(9|4S7A#a"/-%>ͤyl# i!-G%>(78> ЃǗw'h_lmFyTUkǾcHy6E1Z պz=YwXʇU֯xF:ٔ(X>]#vq9WC)? <qz:6#!-t4ƞ3^K21 .G N`l_bltkfM>2`\0s~ZuGo&-=C@=?4CF}w~Tfz&ix.ɭ]2/xŠ$8*z7{u}Qپ<:F߼%"p*И]fPΠ290i!}xyӬg($3wԱ]P(|zlEjg1Fח!\Ə:ƍZG`GܴB|g?,x.,ڪ^v4PӮΏ a x٨C0꺗V1{\=/3ՆlܔuYD.PUxkU3׻YcLmh+o2%[zPЉ-W6K`Afvu{"w$sW<=wˣXو3pwm|Z&>mlhtF<]iy:G퍩jѯ'u҉3?.aI:w`91&ffQM)oVT`u^pk vp@95aПn?9z|+GNCƉ Y|? 32<3a7/Ð_[ O:};~&_ӭzݗ统֩<ȅ{ ܾ8 [q Hz H/ ݻtҽ{ h|\@fx/$w PHYJ}мRCK/Y-ZF}TƝŸswYu-8;g?:_w$S}S}]T߸roV9շ껨r3X]vrҩ!UPYTM \7]/WPWh(Ӝc*ue7Av=*)ߩTpE6#eIf6\ %;~ ίyPw54i%p5pԙQf=W|/Hvߋ>J-J}z%>d?eX<y _u8ur܍z?o'6ǦPgg6p>F*˖J_oia/ {Ǖ4n]~r3427y}8G->q/XnppN!sM_8jv Ye~qvVuJ}^b>G"{0NUz^j:UOTQ?ͯyJۑ%o/4 ~JPh@͋@{_tbv^-Ӧ΄| ?Q^w =SEs@C6/~o~ۿPmq/ry<-b^ey5zoK*m6[nsj*wj㦚tj*߫I&U~[M<˫j⹺m,湥ZJ;i:a^_}+zI>G^/VUz^h4QfW3}XWkO@ZUyOLpqGrc~?!ߦ\9ݘU\dk5 gәuOK3 sv 5p'Hb6OMlۄ| Y/9,wi8򰟢mϋtzZ#_5/g/;R?÷#! ϛG~ y\ 4@szS/M|f#+Ȼ ~oo1WNs% Oaܯ;s '*vr'13Go \Ÿ=jTOv]KH#h_­5#Gא!^VV >G\Czw}^W<>aGǹX_Vpx?AMb|;`~XƟ"hVA`:A3/(IJx?q"ѫWsz׶!7{vXO,?5]ࣟ>KsK |hCsu>vXWj7|yB.a>c+y@SL_-kuV+s%?Ӳc!y1cAnh.|j9F +7Y[Q_ (ˆaFQ@Q"bE""B!{BI()@"HB"ȻYkݽɽ8{x~{I S1{&8-6"=ߵeM`<y)<|=nt95O66?.(eJ2].*]4.ksbi X 7[߼4wR#r; },s0|SI3n yEk2WyR1ǚjw:lKc6n"oOc6+ ye\c厡fnE)yMWmmShStg©Xfs ~QG8 O}ۦfqcsmum6a?]tYLX a Tw~ ._A g؟HRyn6,T8 `p>`'i-\1Sclƌ 4_1lopX;'WV8Wo`[ͧɕBP p3NUx³NVH p{N:w!iˎ@Y}2]Zs^ ? RisCY XvTxf(p_J6{ vrMtʼqi aK̯ O"(w|/nvtd~]ŷ_߉_{CɏOKH\ zd$&\crRxPN0 E待ݛ:e_RP;}M>w,g[@.bx~lل;lK~2e^Go2ci0%fM-#Ad]{~`vOw0~?U(˵rr$rךy!So7b=sGDO5,Oye#yR~pwO{YJ 8'g4ݗ;g*ޖexurɛüV{W?ew{wGxdKy=q.0#< Jԇ~=?[h4 Owzo"uJ}7R_qTsg~ss6Mu)l;V`"g}YøW1Ι>(k ny6w?ʯ`;|sw/>.ZkA eYk=fa# W> mǦVdprl 81gMJF{XNh-4މdzmrpDux ײ6Uԃs16Sۥ cƨޡe݉8.Zh8>d=%qxI1^w2;c~6vlpkBC`c>x!&!;ءT>G gF*<\Wz#8ZCG{Yqtiagz:1iѶ'(x e| O'i;9W3F='Yy*ﱐNO 1p@M߲31fԙ1ӔTMt)Od~Z!3!xGe^Sّynw)O73&ཌd6yh8U( fJP1c+w2F0>ncsaO03NޭB~aSi0 t=+}Z~A)ٿ>cxdo{[ǒz~Cy ^[zϾ w x!rgl@vugoOKnom:#)OvI\`+V=yloܸ{.`ιye8 8-P>x:ֻoYF|3ƴ8牅LL0R΢p5?ǻEO_yy΍gkY6kW^E %ьמ[@ k :<]jc+y5U/1$-[wx?Bwmq'؟GT"~{Gg4ƙCf倯9M`1oq0\aF$r/1Ǵhy 55&Samkl>>kGCLy l{cYp$ ۃ޻jgR]([.\WOB.mz)ќah%)hʼn服+1gڛ\&:Gc p_y cIƸx1ޗ.y VHg1%DL27\Bu ^N4u|AQ2ukܧf}}ޝNU8Ce 1ԽT_j+pLS/n`O 6vv:domMrv&?RO<1?=3Y~l2qY~mJg:f L| K4p?rV1Kd~,T-wjd^Cn{Wlޗ,W <"'soKɷ2;RHQofe8s9&x(ƿ蔉kB~y _{9wJ3;yo >|ϩ,='3^v̷,_~r%|X~$=%+9ƍOOT0.2,wlM[4%>EwUp~._仅]v]М*GNڳ%xa_EȻ AH]TMb,yU6R^QZ錘{Gog5u`GG]^S2c|/` %ܗ9}/&^ /2ĿQf/#X=9KV,{wdom]71l^ëxMWݯd:9\/XDM(P~$k}:dkohgVxmݘd=c o催p<dMH±T7})^tY]$GCCޔz)zr'l.)ACo$MX2 La&G[j<$8͞?$S0*M#x<B-;?r<:g6oVy6ݒ} Lib3+M7WV'rd~K>qMx&*|/?aI7}e/d mr: >d/m*'{ڂ/&.UR}tDÿ)$׎vCⷴ~2٢ w{R.l϶eۣ>۶'Yw &t8I(k-e"2`* N4q0|PZ<"MYmYK[ Yw"cu)f왔p}j{b.cOOLw)QY3 RhǶILM`~wjgK9>!og*O1wg~;Y#=Sg*O(Yztiݩ?]5H;F=h_+潠 qkYeL:K*],KH^z^a=gm({u?w)? 1>M%b3/ pXJt{, Pᴔ޺)RWgO<nI%N{9?0khOts'>7Ǹ 2o#\};xf'm<`CW_3jOü,ZMgcpLeTF~&IA<2{;?  ϶營EK{1/(4SW]'v8LoSǼ]k67lA }6GR#j2n۫ w3x=wWe0W˝)BUƊz9T]8d8ʉWWJvFy?L_CG. 7Fz=q\=#\3({l#05GDY?WFc/#u\VF: /zfk|.1S,r>}{&PP!p,ccNwWW*;;-9y0?2pjݵB T3w*̅~JVq޸ςgr~E0e"#uL^n;~?/r]Q^ _ &lݜ ^/L~N}$cC:g~׶ ɩ̏P_Ƽ*o?"]ayËY|Bz1~3z-Ŀ&@ooʼkغCˏYr?s |%s%rƫ٬P;_g: +8ɼxI{% wi b;#r2ߥۄ)}Q۵U|Ef:"3VȌ/,{2kmJ>"3^?CzҌc7եsoMnuOӨx6KL4FKiFwH}h4#ޓm,nSY: NI'oSnLL7nlnpp8cte!,kAQ^{Oֽ2ҩp~Nv06{7B.oTYL5nbjb|' _d >b|/c^lȼ&,YlzƩ8?9 8ǍKX?c[^֌х da{G~28K0_~}P0=.%*3ƄwZj桺-5-2مOy2bx.ic\̗0>ɲX7b, [0>XPf܀7_ZFz2.|=u˨n^Fh3˨gTfP~lA}W3A߈Audr JNʠϠ器2;*fRfwTɤ'3i/aLZC~5bYN0ϗ22,ӔLSFede$9hR)!X.n0_t¬[B˜MI4N~e:eyn4uf~&Lۜ䭰v G{NȺe͝}|u&4<>|@8bA-LK kUKs◨1˩mƮ+O}-HU{/~}='4uqĄ)y)[v29Lk:.1qnpN ,g8wU_AN1C Px£x ^8zjEe"z+JJ= :+?y֢kXcދlw`םy/^dޫ cW d=@χG-?YiluTfs)Ì|Z7ȼA8:i>~3A|{ˇ P櫢}lov =z_O0V|!!H#Xn˅CA_lv C/%H=1J^o.x1_ Mo2})WE W8|9p5'w6<XsPg~x4:Yvfi+R8fψkM_5|>1NwV@ X՚T{?4'H]mwuR6O+}4_*rFYSr3U> _"-!*<Ǥm8s-eoߥGpO?J訽:/>LwzS]ӹ.17Zg]>*u3`@c2Ϩ0 X'MF2Ǽ,S1b~O_rCK|,Ͽa+ջ ҝ{+{ QIEpv[y+,Ϲ*#16{acчs.2@~6~ʯ2 ̉eURX>egw_vՌQۈ#2[U6AׂyCTyI.kE6J.,7Դ~>̑KCަy)\ ~dj8d0̿1cҤ+{~/PԵh8?'}Ie }w}~⾟'*w|/WOw9ݿGrx1]sR ͡59c‰b]k'u]a]n']o']o']/l']ny>Ƕ>}].OߒKCr) ߣ/%Cv:F\gcso5wa[cΥ.tr/;~$_4@| h xF9헐o/wMz5:aLxhJwyfIɻ՝ rsnG8߾sDlù;?s(}Ugj/e);|7ڠ=ziG!n;>XRQ~TI cyV(h&?leێrIץ Txpn d. Xn#ȝew~26˼o7h.i!3"$N,5@> E |mwQͼyEt/gNr+юc}>־g8M.bO:zrt? 0iR8>'g/D:#j/XAdounj;8(y^># So; o)%sɢyKk~>heq~+;ߔ==gZI۫|eto9fkc?oI?=X\DT|>I?w E!4JIͤ]y7L={ߎu0/1u=.c)q?_Yc:Ϳ; Ng˙=̿Q夿z+ރ{+-%q/1nyIɼ}p^6˨w\s/q qے}V k:]ިbƭ O5ig>N4ghw3R6|/}{?L,Rrmn_w ;>cŌe^ҙ/A϶}+,zO6}nG*gv*yg*9)U`V6ݰna~O}`?b۠vY1>lE{b+"5zڪz}TJk9/g&Dkou~ò2MMR{W4EY /jInZZ'%w+$\x5]7c;fS|)TV~kͷV@sA h.hjG#4[@@G* oRw|m4նG|*gB?[;2;KcnC=8vmr[8 ^3ŰB]߱ qY zޡܽ6()n}ca&oBJBJSs4{a|>ڒxw'SRwXtEd"s&q9vw('z/Ykv=OW(y\ >j sFCb˻ve(~0a> k)1e֧O5^?5㊨}2eU_Ǣ'ܟ+x>9& ?[Nx,0{bx%'82y,sdˋx|Mqy#U=0B ղ|`Gv0+쵍m:@"~w `?3ƴ-e8|!M{.lŧm(uiD1W1ṹ,͋Y#ecj>#UlΙ @nTw)bjS1mM*vav1 i.,vKbjNsʥ*>}n?Hiu _xxSGȘqçfn 5ĩ  hⲶ >O=%64i}"o7w׺~|9xзW);a m(˯])wnYAC?]- ۈ7~U[0cd_ܣR|H2zvcUg O^e6G8Doש5=0?Ge\/S>J2v*727wËCCԟm$l=#WD8{)Q6'pT JY7)Č._}pQy&dEU\-G}?~c\O~G^_mS/d~WHN/ E=~NxA[Sb:daٟ|֝-| Kᵌ戙g 3qwH9;e܊H>G|戱1w<>BcU >jNwυ$.WFyׯ ]vǴE[gV:} ]\t|@ƜeٹxuG_SYKkAüjMAk>/pOlLo%{G9cLj?;Yb/^w^[)[1F. Vx(,:X}+g3} w1yMLWx~R*LwXw1uܼo9sH7l{-q3.ṟ:No4'.='k9N1?ǰ⎛9Dz|>,2j7حmǣή^<4xuWC=8N1F;-e֋b7S4x2KZ0!xy]Z|Sqk i "7&eNq̉Kr2m Lwy[Ә fozN&WS_xGSE]Ԝ•MlS}\8}{'q&'>ӹtؿ;x]Q5# ռc,|T=V-vpLS#ow/7gz9o]Tw[$7ܖ*ݰ\ț21Ztp$V'DGH{ WxfN~:y,w='_H0+Hg(S^}HQ'g8KC*GZA޸Q9$W!Yִ4'Ix$9ɣ'iNI ;Es`sѡgO UxRCTCW_tK:.BL7a[HGm\5稃B󒘶Ume#_;6MPrmmOztv%(c`Sms!s2, ;=_r&;8zԃS/o9Es = فSe*Xf*MStzV)P$ό:M }Iv=]~蕏?pn>I/0'?ߒ?bG2./0m۠<3*w9ض_E^@ɳT=v[Vuֶئa>Q$&G=wwM#[EZw*,2-&t+2%͎紹IN^;, r|F ވ3vXr~F{_e..{Fz\ZӮ϶[3_ xI,uzǁsl\L!!ܗ`ig+Q?Lr}K_ǜxߡuƜj|Ƽ;ym@Ƽ;~~\v~aZo4x/?mo%ȕ3,'g}urq48KyڌD/|;>jOT&{TqA:0%*;6YSV>kF>ayVA"9ƼQgTgJsW7[y)kGk|g*-)>)ئ1eG9ePtNc*ݑ>gap3uG&%1/mID{8dIWN<;=gyB9GO?9*99ޫ%6>pM;4p%^d,m7s?Ss-i {2=˝ Rw>dd~ɸc]Hpv?3yM{WXN]7z^L <'p +,`lz1w!EKa=o9czތwkyg:Oi<8Oiy9qvg`d@_ .Hv ${=^~do@^0c }eaIv$Es4H<.TY<:<ϙ'%'K5,yh]"L%%:Ks pT#xoz(Us.xLEϓQ'{؟}Ywd9tf~ŝ*c*9B%"G5]D%f?0Dz[!+rYwY"w}0sn:_Wmq/+ߪ^뉽eY]X*q?3aM-N051gS¿lf}ewJ0i@w-xeb|FgeVƻ[1ծظJ)kʃ=V10 aeT*׬&.p9*Ծ _+eBt]u(:BpΣ$e:GO*Gmmx9"ǵcGyWUÓpp8 !wU6QF53Y#"Q/vo=/ZW> { ygBKWz%Xֹ&7o^7yo&*9 KѮd$Z7veoRZDZI0(3> M3z$}`X8ܝQLe*}G1u}L=&4(M7ܝ; C"i&Cbsb83Aw-swW}ZO(fP\I{H~w}Cy;YsOhM-ڛ{t>;0һ_ƨgno[qA[捂Bfo[rLᬣ/Ւ`X]nQzПbzw*=UsT[)sO.}sf]*C"4 ~LMʑ՗sC5?q^= YeI||ˁ7ƛ"@gnXwxN!3MjMY>L_;.A]w5,g@@ErFo e=цZ2%/90Exfr;W9ٶ`E,i->F6XimHcp[@s@Zmv vZA͸f?bHk9~o~;W\ 1F!&_mw}G?q#{e;%>Rgb%ׂ=,3Xl.+SP^kΖ5zWm*=:}ߖ*9wloF1axz#-G.|߀Z ORwf9ꪔ[g/{?Ю#o'/z˵w&v/㛎1Li.;%}@]{&+i$H#_}/;B{y%|=SewGo.k箚1:Lo,_xo;tƟ띞enk{8=5χ|;pjeyn`mGY!Yi:7qEgqyo\?*vZ;찭|}wu|/kSO|PS}@[PQo^<$;MlB4Qz`ץYfp3{T~bx{GU#= ynڧ"HmI<4sX$\ް~)FLχ7 C}h6kxG(ޱo=CmGy¢rx^aC'\){>u9Q^GTZ>;:2#ʝ \b'nnnnn4;1.zӖ;>v?5H7gƖIԣ{g*1N&Ikv IQBܙ|uy^T~)G>:BZ}{Y~ US9^uX]vm겪3r9HOxӀwtcJy|B=rF`W'%7o_‹W-ҋ};̝eV%F+Vqωqyv)|BMA6];Cw`gV>"mנ͎՞S_ڌt2eK#~\.3V,01㧌]nj'FYh̸Hs֟}bj6ut._ {nS4FaIkh:ఙ_s>g;Ҷ+n19Xz^|gdH&gЕ{gm*L?MJ$M5͸NCz}6) pfT8(O\|QZsU7\''s‡l3Wz&ƱIXΖqy^'E   2 gEsOf;hw1'w=ԜÍB( P}&NXEvo}}l8> ‹27N²3}g?aYޮ^8oOZG]7hfwwMt@  D}ʸ)_p{B]w 87b&Շx'ŒÿwʌO¿z1D>Tioֱd+5ƹ|pOǺ'vԾhwP WٖBGwtG;̡]> 7'?b,гe}%0OQ>$>xI%F6 š/lo竫)ur/M sC>8hZ̏JAO6Iaۢ]/{'v1 Qݡhn][Q9δCLdL󱢑4ŋZa&ވy-ľ~gmLaG7H6yg,7;wuͺ[u1&<޽Zz!9\Tϖ[޳lQdN}ҴGj +Cz6Xn;Tnhnh#Iz[#Z}z#([\f^`]0džy1)yb|0Ͽdߥ}a} =)CHR:RF{:m(E]>--ϳ_-Y]oգܷayuylxaRa:ezZVzG'ɕ=Y*]q;q7TO %$2'wÔ+G4^xoc۪?BлX/Jw86 v"#*̷\z^(w뫀sdh:~2R!dE&p+[C{BԔoEQ uѼ}e! av*|N9a=w'W C£{d/,rC{&6hp{N xc#܌1sc 7op3nMYL}e?7ۋgs[)3⶜OxoԳ-E}aa~ Mm3 T4A{Cqz~.u]V{d{X:!Xu^unYEca5VSt!5Nut8ՙ{'L3"`~.u#V xq|[/M/Nʽjdl9hàMr]#4H\lp z2@DQnp3G{U,i%ҫAN VX{j%OѝYcX19Aqj O0}!@v1w,#4ӽ=d]ZgL7!OUYM׶%c:ycɗfe-P#HNi曥jH>G;Sn`0GYҖe9i%i0aY7>taδj4.Gc\/n#O~g'-C'Ađ7"G?#[o9ߣCR/r8˙xy.&_-Vsٖw,7;"$>-/ ݠ*'Lښ):m,(22G}]KGy?`m`Gow޸؏ؕv j2ۨ^Ӭ}SWM5)ͺwDc#)Oһ)imLn,7ע*Ğ$3>d䵖H=-l%ֲ>|")"֞ o~\0_Ca cxO"͜+UF+XtK;ޟ+6EpE[q~U><$c]ܽ rb  9*xwL5y 7aEn5nZxƍ]7 ,^7sPlǣ>كsOzjyOj7'e'PD#*e7U}|wŖo/I's^PMikm&s^2oxj}_>q?J] T"q9(&K}Qf(3N; _|=|\BS\^h"/3]oGU։wo}Gq|}18vAEASi̲9qGoli=[ovz$SlGikGSsM'v|4n=ᰓ|ݙKC~!#9AD5ȌG[ b(,#/?Wqɤutʐ¢W[1/7sqñct>^yxލo>z<[)zoN}2}lhzܟmpW+he,89.!W$\ e>g_‡Kji~c ~7:pr;E!{{o$ۙ>/H;1uc'z-&D'ȸ`u+or89`?z9|NwI ) rbڴ 7W+I,]%?K3}=%Q_{w9T_凲|g8*cT GXr;w^ƶ*:ʨ~{ 燤+|hOA|T~Wp ׈m2 Ф ܷ{eͺ_~IqO܉;*ĴyGL>~k׵Oǯk?<ދű$֣XVw"v([O7ön? q`yx6*_;'~>iq{U{[g]D ڛPwKY$~,vYiqLArei2[ƺˢT|}T/{Dd~غ^SG߷=^" t^yG4޻i8$8Mb[2ntoriMNIhoׁ1~+qc¿c]֬>iwyϤ52J+ea$!N)I^=c|c%0d+=9%WwxQ !Avك,pHG<5cL4ȮpW0?=~E{~cae=x7sT,_}Rt/ܥ4U h75 L'W?d]s'->Ev:ľs| hҏS0'68b7Ϝqm#>ڛN(2gtʟҩ|Jz^x1Fym3D{ `yX,o!ۘFeKgjx{0F1q>cm dspspCWd4{TFӾ4o8"t$1$oѮeHSe-k`mMYH6eq)k{@ns0h͓ѴeXɘ2AAL ,Ix) Ye^F*k,:yq~Ncp‘VV;h/]x2؉=^A&MLJxWdb"[iO:o=i"r0MpRGܳQHM5܌T0 ,!uy8 Wd4q|x_ƼE,'2R,e@+koXe ME12ze3/ykyv\:5=vܳx=MLDy:rg\|AK_ÿB̾%났&W<É7U6iRxo\hSd3'w9ÃGelSx_)Q Pk*N- or)w=?(^N^RoPx(\Tppc]'v^:k˔=: _EYe qْwW^}0#<'eKm~ e˹ouf|^c-3ꔉ׹B4SK1*N&cNWxI&3ۜɌ~e;t?i:jƈ'Îv]g'q ȠݠxL "2d6ފ.!~srg d3gL6{ɖc8o3d> `sfV`2 wW8\ >y,Ԏk|gwnQ֝#o4.P֦0 4Of1^8\Zq߁+Bb{|z7gN zg5&cV3+ԡY}f%sNu>3אּ/uMEdt):I zH WaVcCxl-] 9b*6;*sh'1mcN`^Z'!_Xs( cL`GT5Bp޼ kSnKطb"_ωټrzllⱪX㳫ӘY>T[Hj^p…M~1uE3EQV}O|{kw8ڵ#w-sw>VQKC7nJi/>a,ge{#;QMNwgif>6A r97 k>_wY\5I}Ѵf{o0rѹbPsz!%s.͖wW0qԡ19Nݸ7<~>x:HVS\Cr59iNv܌1}Rh?A;·|#bt2nmyˡ=rRpBN9)4&w4rƎ>Y^g}3) EEWEu.*sQ\T>K.<{?=iE>tOa4nhthnܤܤw}ƇM Spq52O=o;yUp}yh,? y .4Jp p<?}h+ 8&mnpP \.h<Uj90}-ʻ(&壼[/Z Py_4cğ'?Oy(?}K6iC~y0?|df.@.`[~3 (;7Rw GThz.9mA*ߐ aT/Jr{;kv2 F'<1@ho.>3 ߇YV+pZASq/W{Htfφx [k X⺅q,]2ǸuNwg<b< ^8M!jS|9`Ky:f`eX& 5hc?7Cn760;(-cC_Wݷ~MF(<ȵz-tD]W0>bu!ho,w^.9=< B~DS|LR`:1)4Q) cEL9ºu1ӛ>6g&SH])4lSWRa=c>p{pʢf>-kQ3q(_2U>43-`ɜ*L1зRuwk-}P_{p=)L -Txœvx2}Aqo(2r wfxͩ>K]X+[0zs*ژqv[[;%}soa/+0ݗ*}3o[ [ʃq=;Iם~Rt8 U_;A~b?+Xf?4N9¿_}^vE|3A: @΃1ܶS^ɷ|iNi8e݆)NxqpW5_5i_ۺ͘r|qnwnqj{?)NElScж-7},b[B=N\q?1,%8%32?;_.>N>Vܴ5U ߶5:˰3/2_ L syؾ(kUJu⿺mp߽%E%^;k/ali.ި°wNW4'qz<w .azs{y; .XJиRjYWxA_ޟB^ٿo;P1.o;<,6{,d?ʂqRpY4?3hh:8χx" w:ʖZhpOKpvE.@LIU6O9ɹT|_">5~Om(loFq;N+%8ϟ?(m=7+;|o߇ǼkRҬ3b)^/} xe+L};)α4qe8ƕ4ssoˀı,zd|-qY>c]![[#S[SasPޞ8^L?{H<Q{eNu8ZiOMy?1.;׊N Wn9R ,qJg!c}ߙe_V >;*e*z. c*_pFwgevE^,c^^U N3vP\WW%*侮+s UjLۢԋ%;R[g(r+}`.c'w;;U Ws}r6:gi8Pwq_Sy]2-Gۡ%`~Z0}+|Xq>/c`?zr``\cːxe& 'ϪxXbڳ&?aeWyur=gdb_o_I72kvJG]'6lJ./}؍b3p;9bΪ~&־a~Y?%;,tKY9$DX.$D貥,Nb9M4Hy]pTp굵^ꘌ_ayO2l1+R~> Dr?YE˙q\jq[r4Ӭ)g~ǸR8:BqC98M8+=K _{|$ao}93&8Z ,r4şw,ttpQGĖc}*E}SrK̸L _YL*oQ²#0;53 ǩ2v '?zqf[IS7\_Ow#Ә03V([K-sН檶[if翏 ur+]#3G]窙ީ̷@L1j29&h x?H7XѼ^6r۱sIʗyi\/\llo[MWAEJn@22\7~LWՊf峊feJfel%X%+ƣ&c72=])ڻo.57+JdǬdk)~Y35[*'M{i#OQ|Qw)*+~žlSwv9c.t̵RpۆӺ ̯e] #ѧ,c-! }?V;Zbgb W!ef٥k1|AJ+{}> >A{p*VղA;xרb)9?,΢;)dmf*.U\z#K&9k, 3D,'1+PrdǦr 8Jcu:NyM7.s9%ї} xV V.?#JZ6~D=&jϪ'иS*^ߵ/v8ۤވ d;r>Al<,'HۨR2)>]峏TL̗屉OIہ:3}L$U/.,'#tîq/'JXlF{훟ҼgDyϫ=ҝ|#mUM<:>]<6[Ump"OoH5gthُ0x]1VF{&oPlFO>usjf==9uSZwg_ U@ Jap=Χp~(9K 㫙8[O^\ VnpW76B%'{ %=vr`m5Uˇtݘ6нyORuxR2771zGx-iܯ0@?0_+.*y{!Q3ؖ=c-AJ ԃ4|=MjиR 7vY]dom``AϦk})7* y\DB/5۩r4e4MM ?b`Qokyc9.A~F~%|Mc'׭i65;oƼɠf\H.WDZCY~۝|AJϕ._פ oc;ﵕ]sB?3&wh9qu w.Q8G1S5͜y|`?=4=a GE C xF29@]A QmW>/(cX7oy ѕZDڗi2"9;&U;mD[qjtKX>4{;3?'kQ-_;l{#u}I=`ƽձ~g[Bf@]fu-jw6pȹ*nzTUJrO_?a':$.1L63 [f1 }]h~q|9Ca.0K`p6}]/}PS>a%:4{~l.j$t9X5VTQ ڦ̝[};Lq_Xf~<ѯVN}S:w_%뼘_z{ٱ?t끧 G7O@G1F}zFnb y1m_1‡>cX}?CysP췥ߑ~J7rP]Q%%g]-Y֨ԥmulغ4WLm>6ö́,7[{tfw7U\SiP|OY|Ofα= )_~}_;퐁-(F%q9-7$țA)" gV0 [E4_t%yUnW'y": k~qƻ4DD{kl1^l 6dpyО{Bƻٰ\ z| (*\ E(bOhP $>m;Ho:COj3gLz֠Mb[6ŷD!KU,]C,^ɴOLDYWgܱ-@yJL"}4]Dڦl܉/ti * &{<zQ*ͺ5^ W-D8еidqNŞx答))Gqo`#O^X4yN} oӭ|7[Q6+S%(8c3OYOȴz/ۯi2*ϧԼo#QwFf߀ͦ8idi6 |oiqinu]cfS89ʜn5y qFfL/MH})qwFdcsf:9=-HC/K'@nǼ.s:ƽXckt aktجlc=WYKm]G]qiӭ1:=׀=[h1}#0f`]q-$,7eicsfӬmL݉'ZDƉfDy$ETqFsej.|xAʧoRO {2ӛI^)q*x&iq1G9f{mJZ;L356oRiDԭ;ږט/P53JCGsg1 $_ @^BjHZIi%I{,iu.)RjnRqWwM.=>9o,.x=_=\Cp .՚Hh-[x>輕G&]0_^/WR|g+wi_g_vD3wVtsyp׌|_1ڬr'tK_ka1d,D}ܕ'(d7Lv_|_;豕s2ce<[@VfvºL/rcTz`wTr5~Puԃ==􉑭 :Fs[H+s٘n jmwڴ6;}Z>x cS4Vxn[X'>ބT.kijϻG?ߋN?#ȭkNp[3EYAU&.GignD{Ҵgjk"YІk6cO}܆ةz3}64~ %g߳ژ9EL>oc#{boƌRX4cynD[ch}7c)^-NJ>!uO2n֤Cn9]&w絶^qmf8Nb62p31XR%^5cP~˙ok"֜_ٖHDUmͽ㿵5o%Jvx|??hNT[|O0Nx1_limngdW2S8ƫ'4YC}?=₺ͱbۙrmgy)EOHϊWqiĥK;L\{X~ .cLrWiLkQf*ܱi- v&JO+y oξMٮw6fХ ۛV{(A\to0AGyuY{&}koqV vo_AŖ ~moW Y*pýޢqM*[VeaUag#[v10S8CDƸ>5iimt]rp} E>߲Xs@e ת{qjsxfXힱ0n}J6؟! }: by6[GSbMє:Obz2Fe;wqX>w`>T:cu17seuMYm1<`:c:e#cn7duus:aI#A'O:Ҹwd> ƺXm1c)!$ǎ6\?ћQgwL8) ;}9aLrs.L\0/Ln˕q6gqEܺ*ݹw\ ]mEj؉D5,?pƋc81.и߉\jYoJaƽ+Nv*`35xS;}+;:Q0~dW'owQO Ƨ3Ƶ VjpVT}AN %i WCo*|] \]Vo)H _Ug>㩏~%,Ư Ubʯ3|`wI{bӕ\6;2~7kGbr6]&vln3ܝ xq2ƀnvvcՙ Z.]>jScz14vٙC;Zߟ |IW;s=6Ag/wŤUV8rYgP:P4=\a.Ӆ[<\fS" 9zNs+pŠwXsάp&Ʋ(.`0k4]xAYNҲӕa:nSq#ͅ~gf)jOʁ-|2_\2KE|׿c ߿8o5Nٕ諫t<[V&cc_M]}Bٿ/C🬰vL;e].WLQ( [o\m]kODnBp[io(ٿ<0B{<6l'/y4u N2wNN\LYQ]7f֫o<1}όyNO`JѽfՆ:Qϻ]?8.rVD3L?=ޣ k*nTv ~7 Qiʰk (|ñu7k=/Nccݼ"枹!=9ؖ}JP|׀S7?dUmݤgwjkw7 hūee2MG':%$zpqzdY{ۃڜVvf)uӽNڈ7nt{fbųNP֥$?.WX燄ؓ?=U*D W]+ZtK[—: k~>C/~V4p1] VWn+8LK;R/:?% ~ ӯMB.L2ewC/r,Q&DQ W@Dho话q2v/ۤcަLwm0EDt?Bu:`mZ_~f=*p(>$2?Wa,OۇU-CkJU}i~\yWZWZK;# E7+CEHo`k{30qk1H}\o} =x/oͳcw}hl -7tH>W'T ˣ=HS2vZ&cl0L/pg!Te|/F[ ؇yÈ 8 c4pVv 8}y^ԛaֺZKBe῾Xlm,{qe4@s R(gY#q2N0s0YoptrQu)|}e_c+yǘ3n4M{{ÿ+}_Q' s9yy8Y:U:=ꐷYG_nu>ЄބZ JI7+GY9JN3k[l󣃿|~̩Ñ~ȹedi{}VouIXYSXyV*,98{Xޟ֣5=4cvեUjW vխUcYV|ۨnUO2v qA;A+E8,?^韠_xM`h?-hyB[B]ײ߉G7-eo6 HJPaPaL KcVGou'w7̚;aZ&/5TB-ZNс&u HL&w'^9-)P1 4罤̣|)(_ʤ ``6Y9 ^g*,7Pm{~*xqPAfouyܰ5o7׿!V k?2E8sYd&.?ɭA'/{d˷Ly7t[O?sy_ ̳Yׯ_h9?~7 6y*'aKhË5.Nׯ%Gfi3d|nj(w˗꙯fS^k6$tx;5ѹ8YuPחICw]8ƫvY'.ZQ_$>D!WIn }yY)\C&jjM< ,˫Av{M<ǘ9|wMxGS=9iO_b%G桡`BJ'L~Ps&6A it>bi.+3#oK;ӜOaZⶐ'8QGp0۾xa/6jGfm3K8"@:z h70Vm3sj 1.R{sCEeiɼړZ nli~-4Gi MVa #T*zyw2h*9G<. F\树FBRj2drL|ߠQ&9 o# EY%S/FN*OP_7ai}F_y;Fg]<ڇ/9I~hrV>-Ba58FJ?4cL}8^}}4}7?%;j#=~Ŀ'?r2~|?rg7|?{;?$ұ =b7C7W 9 LgYy9T8s(ɟ2jJI/)󘷓C?t2d/foI Le?S"Ps!#9MsϝL/ؿz2dɤ_cI%+ߘLoQ5Mџ}Iis~;?OH^ӏON/Lؿq2wdI?M&/]2eɤo%3y?O2fZɤ?]I/L#$'ӟTuT2ҶJIN/ ˺\P\L&pd\tweFO&n7$We`7ISu9;*ICZ OZmч?P&}#d?e=tG$k">78|}%bC^x->%>I&}$ON&}4L&}*%4ӧS鞞qb_]eY1o}Wd铙ecٕI7On6rs+y[ezbY~>%?2l*x!}ALE2鋲dgɤ/}iDTOHQ~YZep*p*'Q>=ȼ YnU%ZUXVw V8*_85Xh L7a9*FCx_x pxMUx͒HG iiDy{ٜi =r*h_y6,wJ"}rO"W]Nm kp^YF~CD|r3oe\ٿ)SzO"|.AJ$ꏳe{?2i'p$foe,aדCOHwO$~ɤOѿ)ӿeR.y3Ql.?YɟD,?aN,HʤcaL?M1=eu}1쿊?PI"]*/Sv# x2?q/T-J"w%-w2~.MF|YD7\,N'S~%wM&*3A2װdүcɤILI&/[UnKug-8oU8%lg,{%dC/Nu_{^{C]xI>L?gɤ2*aMUAuvk/?C?(}&QLFMK gW8± gU8™ΤpF3(^t U8©~ER8R P8Bp= )p )pn] [ ?j3*D ?R=*|G R75*|E _RJ wPnp[+J Pnp+H 7Ppk+\K Ppp%+*\A Se.p)K*\B SE.p! *\@ SU*G܀Rn1#{͟Ɵkڲͻ繢 }0޷=_2>H˲{FݧdetK'w"YL ** n'LuI cn0dYGUÙӹtٿp ]~˙2`Kmf*3-3 ?L7!XC_:yG<|*ѿO^l7%!AVf/ G%;ϔL73N08[ e0Tg}(ˠ%geN0D y A{ yX$}%Vv;*# 'gG 3'9rN|NLGsF3_scoK‘km~п|*|\ npY[*>ޯLQ΅^}9yL (|V7sϙnE =۝Du[ =fOI350M&ܕ g*0/~>CO2߯~^\/~Kb4'=ׯ?`?h;q=]琻963߁$ܿqN~ &RpRaI/`@avzJ|w?DaXR΅" N, EFf;tnh(N*x(!גHO,޼sR8r%bc7T yll+pc%wX`~#|rMK{p(1.nʹ%u^J{ 03?z:ۊLOeXӹnŸ)Ws 38UHg+^ _W8|)TZdT@Ex1L(Q?pV}>4qyS%  ~t:H? (s{yxt2q%^%ir3~ʢc@>SlpR*6Ca/p?I6\S;6T x廦ww)#ߍr871Υfˌsxl .ˠpIk)ZA O@ݎ7ٿ*O}/ ~;8;B'o8,gzJC}cT[k 5p-|#mho x.$f?F-B߰0HwkTtud[(ɀLVѡ tCG,ooG@a p9 7I!YhIU#T4g-iEtJ"z["s?Xk nlEXDw}'wx JoB ;:۶_(>yDo tK'wɝ:o1`oT#_EC[SOvq;~*O"aQ>`/oք;{z/|Plse9TZg𦾱sgIӘ O! /wv.g~WX(+J/ӽt@`f@Pa #&0N3t{ Zyx LKLQ6,;_v@"б~Z )Mƶ.1_XJ/Gy-#ĤJ {_8/ub~OKs'&_-{-w7NG_#9@vTX{/jlYen-pxk ]{Cx(+h&׃=P2sB2'B7<ۈ#"r6`Wc1c0,~ A.S8y]f* *~Q4γK ߫K} ..4/UNs }jIOM= tGTٳ3mT9]/k2 UF1HAB*O)Le|v'ua،OɇuIeEd'kYubvľ!:0w}ԃ) [He6Jk远8Go☊ܔº\ |Hx2C[)Mͷd~tzCR 6zc:GMg:ELk0B [ Vthk _.; t7y 1\2_>o`}F|)U|b_ϨtQny.t1nr-t 1S-;|f.G?-7._8'>3B{e5ev!5g ҆·M#z/Q_clx~?#_.3].%e#ϓH:?g T:`Y273 nc<1_~G#F+tUtخ/ 0wR80{LW, ОLf@ i l\lBӣi[޻cKO!6Ǒ%&f~g+ga:aE#Ë}߱Nm+y/C+};kt'tT.Szg|# [jo[/%U?7e&Oq>LIޞGo:Յ Y>"M?b߯t9ъ[3 q bN?OWimM4qpgU4̲?Vx*o[E֫h}7 _} PmIC?㌇oUI^௶Ea*lm*g2yEdyEہ[, Cmy_;?NY٠n]b﷘n< ^O dioӦc $UQޱr*ӿ i[ 3˲t\͸=Oq+N7%ގ:~ӿ/]Y;fodvI3ǸQPke{}>PUzD7סȉar.vl3p2Gñnn=#SmW_17=,yBN~y68dpTD᥎8|!QNϹLBS{7КS`-ZY÷_˚G96uh^`5@|; p7 dv\N4mp ga_sEdʒ.sUn34ģ㗾q pJz˸eN<wt9B߆.a*,\䓧ʳk*ϯ~3>^k??CXg2-~Dx{N#wXH&wk:W#ud;"צ=4w&|=d]o[7]|=ƒOVfMz&S0ǂl2s c\{yyQ7֓>zzz/gmX1@=8G@ 0e6a;P:Ɖq21h4bpӾTD;^|GuQ4ǵ!z}-[ f|~ml];<0RpEAL%aD>Y7К/z!aVǼ@0vsWPX;ܦ.bQhΗjy!i@3dlS_{_ sii>ܶ\.w&05).|qZRi^X.G+1c+2%B+*^/U:<2ENL_VY=;q9;|RVe賝_ؓB{DF~R%l4 [,SdK,/oc[-Oi#>kw44hzM] 2p7R=a x8GSw㰯{l *%\`#*wӞl'r`z~cnҍԟ6R?֡)n8nki#FL6Ҹ=ݿ$eYO\w8&i?E6j(P Qµp-6|]slU=}hŘ#/ϚGI8}c~k+V<˭ x.ó6]ޑ2sd1=iߟ~Mo[TƲ']~}g .'m} ng6y}Kx-C: =|5n!C/;j[Ͳ43̵ɼ+YdecY2btH>wMw6P:LkY}Y?!D6)ҬSq7%M(yo u>m /n2ƄrzHPyfc{ެf l@&^΀;l6(=9^h f7\fއCqvuqm=T]@7S~nxC7= cVȑ|ilf\L㮀-ϸnu-t {]к-1} [ѭB趃OUoù9l {>]BH)=crL`s[4[MW^}'^n cYմYZ_RmA[:9Aw U^Sm*K pUpd~Y91h=S,[3;?oM[ ZD)zswk -lK _-:cInͼmO+^3z+ηwlKag?i+S$V2*7?^Iδ*?/qwl L|Gx#_. P|:9aRs7?`^8Fhx oyy4;dViٞxRWnC lO\z+&&lO]wڻ5rzW9h_27{JgL~ ]nz?iDvmg65·r!f?{ov{|%wFw^!w\\ aX EsI4c݌; M͝搟vՔAO{g?ߧB]?)[b6?w'^v,3|?ɤ'cd7?oG#P.,afLҏ2}ӟPd _,k#{Ou.%ϲY.H?eϕ$Y^NXV5/7^JD,9sWs/KAs_Bﰌ!?$?`YɤW_OXgI[D~?nb*+[nm9~I{_Mߕ P3+^' /Qx‹^?V#?Tx oW)U- oVx_+Qޠz)V5 jPx;}: 4}ߍӉSDU4berhzA 2fs08ucWO2~&'^r6L=2.11U?z9:/pb?ǫgZJ^2v|uoc+i{Ff C8?LoGo42_Gk>^:2]̅M`V@P ӵ# '}?µNLKt].'>S65h#ܻ܍pq,'/Ux›ޥN{A T.;77{8c5cYWC=ot4v{H l^gI߭{hZg躇Fui{haZgawl3C {i!^Zg(ju{ia^Zgt&#Ew(QEgs; pǀ ]5 Qnu,5pG;aFpq{EipqFp.զ&?ND}텱D[8$* ۘ>N=pwT s0{HVU8HmUV:HmqڪevڪR[u UA:DmUCV8DmU!?wnW?X7+;!g)L/hjkhF0LY/w=˴60Ѧ=L&v4?lMaL=Bi]u#}~C{MGHGH#(|(ɬeۼ}﨣Qi2&Ia;ih$L3i4i0NXNO᧌)m}xkӼ43&;]d?iIt>T8CCh 'J^Λ¼T3N}vi#Mܬ3 U8CqxƔaL3i2͖3rƶwlYzhK%g>K6٧g6rl#gO. H_mCA@L@W WlmC7*iV'ۍ&nYm7:ni}ɍJ 7Srpmi[ U/PZ@Juu T\zځNx"/R;p"-{f(\"KdDe}޲m{ї]t/{2ٽ\!<=z^!+d޺By*ٿqW~W*ٿG*ٿ[ٿm;ٿ+ٿw:ٿկ;:ٿ{:ٿٿoٿSnٿ ~oRWIy&7)4rna7M~[[2md[3y>f[Tޮߢv۴oo[6mt412NrnmST mmUw(w(w(P+Pߗo[.6KwMf]#G~q[vտG3=<_1_s F'U]X~'?r÷ Ox 3> ܬ .O"ܿX> 0V>Ǻ-WCkq/Tqaڀ[Ns_[=pӾ̻Yx.˟[x//@ao]c-?yKI1(,(v:؎c4a`28`(P\v@Zotx?:< 9cl|=`礦x;ʯ8=?c#`C~hP$}W3yz=xf_f`O,yHyfzHoQ滇%A : ~j >G>2E7_̜\Gz^iw>0=wQWYQw-#J 󏏌GȤl?>6qȹiᴣsYc*]SxZ S۵1h'd{~BǸ'd{,|B'dszB6Ǎ'dszJ6Gds4JugSj?%cS9xJ6ǍdsxF39Z<#gdC,~F6ďȆ8ldC|N6DdCN6İdyN}Nus?>h}{Epo .msZ߀sZG9W A紪sZmq'`Lblǭ;wn)7}o<{<`)RF@ߥOZ.D^g=3u(lcbXp07UR ỳ~C*.~J4J_(.P^غOU, #O[/0knjW3G1 fcg+c0oX>W7sS &ڜi]|x%_1]w+(ywE{!yW(?N'|]|*g[m@s?mM3ĚLv tCAsHYDuu!q'Dyϩ,d,1lLCۂaYtoRZd>FSl7r0ӖO‿|H<|6lmU.Tge _){uhtp<1H@7p^d_mwz܇xiR3F>@xEd^eJ9hߗ>~$`Qω?{g ,d]bn-}cGӮ"ݬԾ U-MPjFMP}ւ}P[u!o U!̴GφyTaEIϾavDz4qbDv`/t+,ca- 3up{kMF@n)o:]N3픮G82shyq!:gPr>"-9myۗW=&˸lQγ}xnzbg)2 mkXNjo[cN+]efh=FPy7'Ay6c>iޏA"(G<(qCF1?|QߊEgeՏְQgQud#c/)u* =*) ,3 >g=u*RPhoxIV(so[X䭡U@mWw )s'8s:8od y#;RX!/nJ|TL>L8N3j)xyqgRpuCn'2ǧvo/v*!+dG} .X ੌ|E_`m;hoeӨ[SwNq^磻67""Mޥ<%c̯l$a fIk{\,KcF{zl15l,ro K'ɟ"Mc?xIe㤅x.ӏG6FG8 p.@jcmK%qZ3 -#מq(஀`[h= ۜ~Qeɉ+\ Bx>xQ\?NMe4,ph_. օ@{|whӷ.|"/Kh3fkmۼhbb=#cnfpb9Ѡ6cϽkJ\~ tmmamg"T?:ۢpk=/_M9;ڔ-E1Ʋמ1KL}h'[;t !1Ic<1)Hq ̓,SRhc>ب}q+q)N}L/Ec/F x\>fj׸}8 KNxICSXwc>3F;$<5&HwbLs2}+8/ () "k cʼ'Lmi`mT\Oe"w]uk:x7Ƹrb7$,>idv۞gdϴc`;6]e4Sa{ԅJ<~ڴW:ܙO#o>+4f](9xԝc]~j+}8 e(\ÿGvl9Ul3OƆ,ڌ 6s&ܾM8</eA\dG!ڄda^j3gIj5kk]Hm'M[0L/lr1OuAӘ4&;'IҘ~cO1}}HZgNKb__1cmҚvlhZ3n1c+cj8۫i|| Q:g: pt|l:׌<ϱp3c"YŸ Mixcqa:VΌ>/Xx?r~@MeRgñ}>rb#84o+eLGc+,c:bQMLޔɌ\kNOL6ˬuOֺ1MR+C[7sPOO8޴!o7m ;7}w%cw1օ=A@\g=8Mo>I=/?[~>;è 4πe# {TV\z3P\ֺMϕ=hߚ -GfNqhV\p sq>3ec" Nh&\6A1p`2p:^Ӎݾk3g2߅NǂmtC$}{y2|RW㵀;1 x@Fn?ecVf4vΏcb2N퇱 b#u&?m#~| 3q`~1fL< cEC:]7ð?hoȈL_sX~|  l&_qԙm\ps,sQPYEZ-,n4mx?HmQg[ײaƨ'yd/3COL .:~j^4Qkw@ٞe 8_m8tY8'b nD7cބ\ GzPܐwVn?ߡ=5({U$߃_"֬OYS?OZX3 $QfD}:Γ#F2F%M we;4 g|-^Y R8毌ٱ1fc!\`ufPڦSX`6*]*W1޵<߉ݻ-ؽSEzmby,X{I {!֌D W1ۜX>a_s XQA4{Ұ_ Fim8k6͠3)߱_+xq kU?()_2G/ e3u%'9yOlv;2 `;ޡ {Ԙ2J+y|)鱆یMK~aBIֿ/34Mvf>; x > ߡ)91]}\%0Bޛ._HIzw=d۔mR͔/29Mv/Mzl?cR֒5r>CMFٹn-i'E׳Xt ~OYuv.:;)ÿd7{v>˗M9?\nf]%S烼a9^B9hUʌioۺ@6-n9Lt>%Y/4XƦ~AsnhʭaiMZ;j/cSqp^f^>4(8sR}VnSΐEbOΙak[:\ -K%AVA]=KX~NϤ[NS&{4e_NS&G4o2&0Fݜf-PtKRuٙ̏l.I;DwPc:e^4{.uI_BiǻYWJ9KG&[./Q,WA ea Bj{1u|=s|?QqL/M @ y2&<׌1w1}_3=<n>}F>gx!EngІmڴ͜aܦﮓU0MsgNc?ɧ?x}̝VuX׶c uqIOCqyyM^kJ5ez^;'"W#6$v-+|p^jc.3[~}פ紼&=~% Ƴ.3˪cg٥3.3k ܫ&qr_ۻy>)32s*)3w2j @3<IkN}SV}cWܔY " x?no AkEzo>?|/| 4 , y!}* P]|/*c]j/!+^|^2o"=:I.R{;H |{~_顭} 3< ɀvՀq>6-U!8 O7|pC#xpJT(ƊBN N_)pp]p[Y!|c@z΍΋a8? f|Pz{[V< bJ*_C]|E2w-b5RlI=nb@W?f;Y(g ?ȑF[+$Bm u^w`ڀd#}dwQpFŵs2x+ޣ[Hxѻg2ʑ_eVd_^U)S!,xkSwdcgF20V?W:IF]<^ɰNH ᝨ OHFV OJELr2x*@K>ִ$L%3# O³t37fRx~2x(  Ŋ>‹TgUd.W94e\x ߊdA JXHo_/^DqXDܥ*'Rjq퓴q啎'?USgQt_2|2(Cw1] .'CkJ _MFE_7Sn+ =)* &-d}89ngyi4|ϓѾ9ou`# +(a MBUSףI(QW큎LFbTnrR+ *=^Io:[Q(3G?M6fHNNNQ64odKWv[_!_dOyPW0U`na=V!Oƶe.D1݅>̅ޕ~~΅~ Pk]It@j خ۪fV"?vkm(B6lr~B5[.*yAa>y l NrE^|5s# Eٌ΅E[Byq(*;ΥϢ1|yF2( iP_1ʟ)(b xIf3Cqwrı(Wӽ0_Ff/Nc;}1,WT9峇 r٧42g42?.id.ge-|o#達VJR{l +ݓA9䲬\_)lH~s9[瀷2]Εaؙ@mlKX+ߡYCD1{Sp:dY3Ʋ&ݧe&.CvV2dg*CP.Cֈ21TyπY +ZxϙwTc{iam?m}x՟'ȸ w5/0~~NAv4w~EdGc7J`C|9Ly7jb]z h=.e xQ ]c}ñL?K5 'KGR{ڜǃ/ѧ|8Xn}?o;E}?qE{]r/ʈQad]O\ՏL?3TSΤ|VƮ_BerCL7Ov7qCZ%|K_0aq3pv@f p:YiRUS{Dyc-Obÿ7,ULfwE=e_8Yx^_޾'վ7/>x w{c¹+XSYc}039%+s^v8h`U!FZZ!{_*>}@} V SE JWި"*]+bmEY_M[-VmZ\Z]Y{*V ;J|U \ʮYe weCYl¥VU:U쇠8m'Bnpꂋ"Ex \|a!lpòT'~C3vf:9Gp .޿JQYp̶bnW,oYoU>ߪ|^Vdp~-գYzQGeuw=*֣z(SgOy_>}MSwOy?>Ɵէ4>~psbOi؀:sJ (o7mـWPn@y7?79р>~UdM\'_CXққ[!i!i݈M&hg#zbX#zbհ[ܿŹ:8~5cUi.]И*p=K .MbwZ Mu﴾ۄ&T772[*wZf=EwZkviє4p;.~M n7pς)^mFJ fToFe~>f4V}6Ǻ.<~5>k pqoE p}^YYg.jcϚ\|5o sZPkX܉XV[W.cxN7e=vL ,_&>;s;x6p{}pZX "pKw _pq p K 2p7)-\{6orV)b OPa\3`c 4rx~}X/u w{MkQeިx7);A9_ۙl:ɞ |d|ûDR߳3rC#?~(<SceM̟d3j7~o{:S8 1U7e z畼FD.DQ Q阛/;ƪ;}(IJ+m6wkW [vm .Xcx:?B鶞o+}06!{څލ;Jtςmۀ)ܕlݕ!n] ,ߕk.35ayRa;c f,P_|b*1#÷ԑΒ^R>;[|ρfcW37gW3/{]aIp~שFt!u_]esCLZo2uMj7t4knWd_e*wEyϧ7%Gw6- +KUt-mϷadzx~:H}!;2u'T!Z꙼)TR]ȃk-.J"?ޖ# 3ϤtC>G⠓H;t*mɽ]]>;ЧaP U{Т }{P9{h={{AvdIvZdUIvZd IvL3?[~z%#^+09kQ1!{O z=dR6t7|^  s7-Bwy9ItoJܽ̔Mezo*3-{06{H8~=Hn,qķ'8ETYo|¶X˵RaUNgҷcL߱ Gz7c3O7:xʚ< [Tύz(-J W 껪/{ U.p) ۬/б>) 9Yd& ogW!n}*:mdb{mۋSAZc??t˛3ٿjuث=b7ob@WT}xԇXX_x|$t=fd#%ؿCNþ ]k㠓p=ہLWO׻dN[u^tnWByWU2+>_Kt{. ~D7‘4? 3nƺ)/ʮ~4J3R*?N?ǃD~ݶ֏Q~v?jۣS^?]=0?=p?@.Y,3d;εҺ"εthP4hwaL#N+܌Zi{uӚyQ]0oZ+9XS hr{ലj=*岦|JMچ #).;W-vGׇx|Ld^Ez|̟y ˓nK) īwKzwOO S>1.R>K.Є rǤRZy,!I9ԗ1.goru֟Ls"J8[R2qJ.340Ҡ):1NDVzSinw+ۉo吏qO1lb 3%i?3X1L9۪< Ur>3cAƨ|S^Xn4q\ 4sm x]3 O%3 xJN`/d"/f\U¸ौ^=|j{;qŀ& =_uAHMc^o:;x3|Px ٌ)>wn|zӺG1?%!7]o-tXBb6oUib[dbڊ\Sq[QxVef\puWU,BInD  >QPQTć) %RbG  Bo~;;{w8/\y'sl3 mNk|vzun &d4ALFZ$ "9bkq4ccq_X<1AlE6lbPll f"-b&*EKmܼ-b&5܈vSmnj|t!3,lkyַl<-v۬8b=/!n`?cYnVQ] .@JnuAAoݡ52$I~ mdkO^{^zwc<KlY/` _s x`yOb>/()Mgu̹/l%97DQ8j2S"[ufnh͞uzf+Tqfoflۣt;( Ir6mNAёo2o`c{d,lvn7Z,9ڜnc˧޳)mM~xv=H_>Xۿt(ݫp~a#zn4ֆ0=XF$|IgK,^o[t.3lǛLЁ;Y~CZtGhD1u]^Ro s!ӝ̓u޵{(h NQ]cL[Pz9+z=tmmmrZnm]1}T0wq-r= w4wA۲#h{ux<[)Ls#:ߢ:ᷧ:&,c ;Ӝ@:N~R ?hc:Ox:3bZv3+?]6V ]]3Ox.g`w;U"^qW׭"nhW"ySWɝX\qX7ߵ']bbiWˈw!~^h+Kobs*XN _V֢>QM nR~ U S/*HзPCC .V[iV0p`x_~7Y>YOdF'7>vOvy]OVy$9Ҭ)4 G=ygY4f7\b (ޟ< 8s'Om|ˣVIOb|¦ ;$'0)>a:?O[Lk5z4t5&FC׋oY"ǀW17o x^}ǀW7AόAEˎE|;n7:>^;>5 |_8r| O_x8 /\a<|_x#^4px™ L/\~| :p 'N/a|ȾDjNBV~=nT y3 y]< y=: y=mF#=igh)`L1 s`˟;Sw s;\c.xo wyUo?gq_g<><ϼ|7<χqQ#Gs>>r]`@@^ a@\ ?[\-Bi îy{!tW7 'IF=B2_"Կ,Bk/n߸EoP2a, ^K/Xb1ƒ^1LŖޖm/mn,m__4]XLXlXۜ]X T[K_2:.AKy!6 Sas> 6/ ULJd~ s`dNi2 2q?!+\z.GĊӟ}ʓZ1<uVF+'+YǕ%oiH:Z˭ϬomVAWfWF[*SyVCV\>!j/b`LX}Y}{  k s} ʰZkNx~֜Ad-tٹ^ ]A˃:sweuhWה(ߚ.mJx;- 7}g9YaFoZ^ N B6m ހٔufi3t t<7N7Rtӑv馯ق4 M-yg ty#6i[`-MVئVs.Oo7hminм0̀< {w+Am=hs]m.i{_\ރ"t/{_i/n/t:. w>}><~Iag9M|@oH7ȉboH^~:v,ٟ]s~d=~,?*9o)1rL')_I4Ҝ4g=4Sߣ M49͑31 v.6&&ҹOf2|5Xu+WW믘3w]ALGՍϺxŜDϢ pyx9Gq]5mUԠWaW[U^}U)\* yfâ4CpM!AB$]E9%8CV=/%~:_ዮQx*,Yׯbh/}\ι;C!Ʃ"'cW`t{4ϣOk&x#+Np^DmeB2j~D|nhDc5-ȷ4lzoe}{QO70kyDGD^w YWw |SMuyxR йz5ĦR|ܫ=yWNtLױg!3~ m3ᓙwiv߿TdLvB}EEGRҾ?6\#D^> V}d^B~M<xp~G~Ǹ]ӏyl>[ 037gWtD>s|gu^svN?OtONtljOw8kŲmKH?A?sEf8e$}x~Nr{IGgoY b>sOTTͺz-tE}\Y{f6}u~}'Yap k. {wiY՘R<6gERYUEwET;K~vOnJ7Cz֨t{.ۍڏG&_ouVP)ǰ og,umP S~+(7f}HtKϗg͖vt? tl3R묾h7Ig͎N3./ZԻnttgMHwV"D*]5<겲W>Ëm?XSsX8&OͫA(w~|9.'l6j7w}tltzVv>z> L载oܘ3yE N贤_LA'M9]QtY 婃ktz:3$DYq}S}T>OAN=|0M_z?t(K3>DOwin%k(Ϲ;O N)NT8}>xUAno{O3}~Ex|{/z n-w7>11s k}oq-|p7R &m|C65Ÿn(ŷ}m圸7]aޛ z~Ğt]\_Jqa)pm_aXC=?(dctg!aAw+jM}iduuP5ZS,#+?\'Jwqy?Е 2~[mJB >a咬t#c~Q;[m,ӕ`>]N4ӏSn_վlqm|W1RNWf/NUWt tx_Ƶf_м8T ma)R[e8I{ڡ5iJ)[t>w@SqNO[?iZG&}]4uq̙# dtvi}`8x^KձBW$qV|^4 +a4P 8XAaAh{R:NJ[e?Q'N_ߡ:}|3W=灹r!k^ꇖWz3ݛ|י-Ck꩜G0;zީ[o2cRyXt6پ$5UMo_a|ynSw|#~f7ȵyOSgɳj>US^z}%R gemg}/-pPt_Nyfؠ;P u`:?[Y;gϗyl.l|Ƶ\0G+)q gW|_?0"a9suR 1˭p:ch݅q R|E;Rxt>obOKy&>qB?)Dx}4>ŧm*n_ r8hASt9ڄTxPSVe|?焧+ 8P7ϧ2E>kST}[ ՇWB`[*떌SYwfS5US͙Mx szB~C|g|5q'2N>KdbDELh"&u]Wڧc"?8'iS~1NyScvQ֓ZQ@m{(|RQQ{lWشEYg>+xDpx(Q7(&:lC=e!( . ,V,eAveQߔPMJE(Q,2DY\eAv{[MMESdC]dC]q,DYeAc1ݙi( 0ʢ:YQZ˩Kf?\ K.W-Ο1^۲m;+|˸SDW'k3^)LU)kuR.9gNV~Wn~Y[̡s(m="}s(Ё *X=~e b.K7%*QoӜ/_|r)W̡6Sz_ϖ6~)Y7lHgFqi}˓M<*ߩߛ{PI~k#zQnեЇ[uzmJɸluX{[6 5:H7?Bՙfſ5a]jW,UKZiKT( k/geOkݭ9-xC<{}݃d1yM7'H=3aC97'3an0:'˒gu~`kG,ȍΥ]˶/뢲[T-Z{洿۬ҽ¿kX 5D^CkOڏ.<}?uʺXnؖ3cS~aؔqS|| DK_u{}{xe?Էn[tF2eJ0>s|Qoceܭ]4G{2=Z9iO{T)-5p f 'T x2|=z7EyNZOsTYj9KͿʦb]Pk7ɇCPxb9+ :?%ix|E_dOF4ҞK0eu=KۢH./%QB鰲gzdIUj>aŸ-~{.g6Nߴxm֩\.)I.:oʑn<+\p!8Q?R[duLH #'A)TǪ|d>e l\D }j }>qBۺOe/(%r6/8`@6;2gc+e^5χWdߡ߈>-NXΑsњ{,}B=B 顆Ke Xx [ G&?!b SS~@*}B? |] _x' |o Dp9xy:Cׇ44_| =dg |+xOU6x&tfW$8UR7; |w#Fn#ʛXC{ MQxؐz6sx?TMj>X?* >KG^_xa{Lo))|dz !iE&96_xL i'0> ]w>5E;xtͬ5*W2 1TU!Wt7\_rmaØNW65c+过y,g紻l|I+2ISٰ?ʉ O)e<. jr.9}׌̣e}_||_e^),熐'㹳!G=\9P(=_M˘ X{vs]n-FKU_e,J'BUu}mg ֥/˝5j,llJGTcxyiY"ۚ6wVvgp'BTY-gn uMju/ww~ i՝}lkNR:m>yUݹ$I916+{z_NVO/wRCr*aٽ_{ӑ|ld},דt}cB)yvMsοޙsn5/ OG>Z>|P|!7O}lSDv:ˇ;U9`w wI;bl+NINPOi;y_\('/k!_~jaٽ ½qxW|ʜ<>YQz3dυ#ܵwc5¢*#5g[GYk#YEI%g"mVbu~{aC',On;vv;[w k^ mr'54 ;z*5bOGj}y,?PgPwx¸Q/l;T;,x~fu?mbF FRs/5wF ^&?_`q_ӑ}Vbϳ$_S,ėI}dw wf#]|ir'5Y.=ޡ5Y Yh\|϶;K;K-TJQj\Z$R' 퉯xg^SkY7X>ulcb-kw?hQ>`mQMJGi'ٽ½}U>;a#Tvp/8{j~mocwO wO?c-'dsiGW(!0;6^u$BuROM>f?טӚWVųwzo+}կce7_Kc|kqu--ooߏ-wU{=V<>m?dwC鈏}G0 Gd82צ>mc„{Y3g{B _p>y&8܂s )8삳 *8LKSCpg*8Dp /Opv65-?ূDc ~$W" #[o !k "d_$|NYg|JI'' >.ࣂ>,/xག-x]6[o/xM7 xu ^#x8$xX/T/P#x9g %xu:kZpURp ^&x% ^$x '8F\s􋇸q(+z>St,+~NY7{2^y{*8-.sDS>}#\ aJ7czŠsO}X0Ʀ^ֳ{sw w6:~ HU5;TbT#4?Zczx ю:[]&,_ ~ 4G~/7͑odO,UYl;x^,;yulQ*iWq/ J>~ >a/RɗVgdf>ʙ*wsYK<>5䝟jNKW}<˷x4~|WX /|NjRQPQOy|ĭ.&LɇJHK\OS:]od(/8-eu eUu'YE]oŐH74K͆W/;_orq᷸3GJg* JW]0Ћ76-ˏL}TcXn4(e>yUr gnPq]gM׏o7xH~Sa=,gXo7#C?'Y .zs,<1_}z1ڃ<;z-ꩲ8ZC(=W_bOd_WU-*Y^E[%eYtҐ@kex`A㣾{=ZǽH[Xo}:{Xo#ZN}AzWF릾^Yz;@or/f\( GXo7=hCm36%}>'joƻM>;qiYk!kZZxQ1wbz3Vqqyqy;z bC/ts>r5*g-p{u;oѿ߃y6%^E>zޙAqXw~]O5ed;*8-wFWֻ Sz9,J ;9ٹ#Uy]ֿil}xVH#ֻ9LaׇQN}[,=<#`_@Vs4%?_3zdh {bQO͍4cG=wtF9b_:g73YEh\| _X?+3XsW}ǼGuϺZ:ӛ𷪳Qz9-M 38zXT{\5/+2Syf1l'턧J_?]UYYMQ9!f/l1s6=l,Y!xv1_jՖK s >+K gڃ78ѬW6R.4<}"۩|%<2_W5[S +J|a k^M=n#~/Dًe>jۏ|,TX l zw7!|A3囂R6c`igH- w-vY+sMӖpsFoe3e.1 Yv!e٫,e[vZ42+ihe~Ә^i3|يlњlVkM6;&S[]d㦭mV6dk@ :ՆlڒbmVdfX[moR9ۙR6>+.(-`c\5DwjGJx0}VY-\u\u;!9Pg ]ʐ_s ]o!mo[: 6WT77à 8wZpDZ~3~##]jzy$@cf7 1qz*yJxBJ`ؖ1ż n{c ypÙd> C(ġ3 ;.ӭ:ygE`e(3~,-kbo.'n,d3tݲgo{'7wtV1:c?sdK&f*2%$W),3v"v[g\j uotj w/p0nw?mFN=@[S) DߪzƌnMHnfXB7I,)ZL:k+Kap?S{f2 k?|#D1@f 8F­rv 뮭­­3­d;5!ߘi7^2n B:8i7X(H낝)-s4:kLi'p5LߨtY%?ttoJc=?E9u:ۣaoݯa,l[:S9pN m~G5Isu{NS!<{XC>c`:0m. `a[ν, E~.$np.o_dh5107~~~ưclڭBO(G?è3@VpFYd@7sxQ:~ϟB ,2 l+E<| < q\a~CmuW0n;EVi|=LhA[h3+1b_˙r]׸+zt~Կ[ߕig~xUqú{Hnۯ.F绑=[pEՅ+7mN:1p}06u7maё Gkэ\dw/̣g븻ȌeFݥ﮿9s3>3ڬˌiТ}#>8&{pOd^؃^e8Ѓwr6y]3{Şf1%],\`5I~ ܔwn=) #m}mawOy<Ǟ#)G~̮%!? Ğf3fO?{R>u|ZӏzQۋw(E4yx[  \+xW0C) W0a^F}g|k^aDpcy0֙Wqȼt?7C@Oёv3zSlMqr7.wЇYhW8_a&}~>d]MI,;eWl<;l:D,NY#_{k}̺ӯ =N~2g`?aa|;/GcU]n t{GWMG9Rv 93tM Osw0ofvIXa _62vDRy<\/ K9NVwmi{3 +(# Gi~O=o}̓H2طm gvy ->3`2TFߟe~<0g07t -iAvR3x8qÈ~6z7 a9펝:]eWGs'Oq#/sdZ+΅۹v4ϔDMqxx2Zsȟlalmf$ 8_]S|6H}[3Cf/)p7x>$Ouܟy僂u5[NFA`7 py$pSXxʩ}U 0!X@^a eǿq&s9/?vXz A], km| o\1d{dv&2BYF!E6}>Y_u"E ՜N>: f,c9z[o3i  t6z0 08_F?|fgbu(缟떭pA -[r- [1[v8RtG; 8,Gt8p,,2SDZ/2MqP3@euȺZ(n2fɃprKONau,@~ Wx e 5ag&"`ǵ6Na*"Nnn=G噣aGeG{Tì!ZS l#n3~&?չ,/p?޿qbo5s6öeȦ|O|?ќ<\XQ8?¥G\a6WݺML /JE݁NK2Xf@\Hcgi }A* 1g|ej3僨s4|7ᕿw.3aMWe@~W #x]e1eY5;!WSa=F>If&}M/H7!z|:@}uW]wEP=0 "pk)N2pC)]RtC5{:XP FqUtoa0z;hkFT0z{a}> %Z}owpV S}sk8 A{G#}7#v2fz/#0j>Hj:5?\Gzu%\ HqЦQapBp-78=Z{6\µ(?:(y1\x+ݘW,cZZ Ș04eTFബ2&shSw0 uǘad94t 'ft 0_7Oe kB̌.0j=33^Φ;gP$C+OP~#Y?&|cq~9:̅e.}dǹq>8Y͕q>N?*w?a`<;S+3j9]oD4%ʭanmxXX65>ͳq|,zMeB49T^7DSMhM4'?`3mQpP^}u.<".Sw<)~drCyQ6MPxir~e&yhl4Gy'W(#Q80[<w(\ \57nPqn];y_Ey`0?ʻp=U(E< =ד?݀1n#-8GQ*Gn 3CyLt+dF;!(ȏCy|P  (Dy;Q6ؙMqQ((x6/38sow3v~ -(0ӰJ;! ˔?,i5aw f/+v „[l+oeI?$!C^*Nsh fț-{o/J-QFa|B}qZ(bM;biw˅JRB-i!~| WM#ys-ģt} Ηtu# yE5 DZ,~Hn] q!딏--Z"  LCW{:i<7 {= yk`! EX.|FX[24JCBPPB{۽WL~7 =Ҩ77bJ?BagA~,ĕk %/۲鳭UkzU88DaSiMc!(m<ꍷI{ש*nXofA>\ϰ)?BЛ=_1oz~~"cD[ߛ5՜|6Y_z~/[4wkq6z@`;|2w.ߵ眧O8w+&^bdݑshwyA>X-)a?|TgQk4 AsY78hN5xl9r4Z 8Asw| r{+DWa:L#*z}WUJ:]_oRRJ_U}YE8-G_IQ6[۲ښvqiUNu ޽^ ߹G:^26S=p)X?\0u7 ? ? x7NZyy 3;Ì w]7{|5g{Ob@e~ Ÿޡ-s^uS63Ɖ+,*,23{p./SOTMwZs|w!Wa}>P8NjU{:.>vCAI`#"Fqe;( 7蜬\/g༾a1p~g4g?5 2HycqgHi''[NǠaZ{:i~kWj{JS=µ8p-kq1ZDf\sy0N2/JJ@0q/*w/B9?-Aj0ݼFkOs Bw ~u,km',;6 e8pw.QN4-̘&̑0(>1z zB'YxLWrһ('ZL+ӸS)s8B8Hmn11_)(4^4;m:.0 \ xe%`m|DQvr(7 37O}HV| pר8J#8esa]8l W~ߝNu.`m:M aF1 +LlF oC i?*`9*m`sP538/#`n|rwgNhkFB2 x gQ1ShxL 1-3a ed<$!1dp=x <yax`aFڜ6-`7 <0'Wu#ԉ̤tn 3 -+;SL$&6jKgr`aI \<1fcCsUA]_LN#s2GavpY\,1xr+,7!WgQt/Ήrh+ \l?"X ;L6fsܮ4ml1VF~k l.;kN{e'߀[̦q5 /sټta6BmnEM:b}p<Cu?\d'&!݀c"X]ޗV9~ ZÍxuu=߉1NIoq/xB\tg|݈jO!c8W1ߗy|ZT1Q cuc9\8=f?u:.ip c^sk64*xCuLp=uL+CCu9ƌwڹhoX\+]yAS1\Ia~$7<\d+žPI䓂|ޫҳܿyVu:qjB=Y8 dyo  L 2O vf Z.O ױ6,_L49ޮb46x@U, (Č<3Pu8_ZA+s :s!.pA{*,D)ۗ _]o[hýy{0[u/MLu/r\.ޗs\m>Kg}9.\,oi'Ps2W2j`=.XcCŲ?oI5޺D[;\:sG &yD-.ׁ`;׬q?=|:R%`_?ē^y*HWEgJ|W393Kf8fH^2.:h1pzi:?8oI18?s7|׫iM0ΨIm"0 pg?L<>->>-mysY(. EzЎ%X@C6N}-l[xNU"}OjJum'w1-?ol!BB[} :˜g. */'X<22pawR46 taϯ\N9ZK(v7\G_ 6pp#e_Σ*oS//lg0fdfܱDύu㾓%73ߪ$n)$̓y|$sVY!o]3<{X߆1]nnj-L(u]_flSFAnbyXs80|?lduΊ [ͳON8:c#aM(1Kx* ΫULߖk* w&ot}فl:<2|`O.v KK~yf󬾫yo[)?s ,8p/T&'0-m"g)m"W0;dDYJ}o?7My̌ӱ?kAؖI袊sUyƧ q|2QxlJ De@ >=l[Gb0'Hܗ0o>ƌu97 ufǞpp} t|' p1.4c1/n9]B<'Et ߳EKNx3s(xr3x p' ڼ3Y>Q>z>жB~~G'n_5e-:Aq<0zs\AD+x yoc=7ՙluXܟ TNڋ6q_p 0؃ lևT<~m|)˘*Xƞ,_|$,oA?2YYܡ8B7:'u݊>Wu+zsƪkf\s ry ``˼JDಱxQX~Hsٜޱi,]p>6'-'6cxd>R|FÝ)亐 PQ[òyE݃'b<>ht/ WUl{Nfb8UsR0W]1C12וwUI`ϼ8syMx|8`q"v}l[2~π3cS9U|6^b?'2kE9 s~VQiK9okuT?*ǭ~駐2WY3z+!eW2N*龖 6 :*kQ\o;N`N|x3w2}erV3 ]- xf,{?|~I8Nq]|86qʧm5  P]\so5ϥC{5%qj1,~ m}݃/W 1yEVwۮUW. 6/{ [Bj}p.~ x$n5?6X &)ෝg7Oﰌg98l o 7)-BB,\;/E?[}-5$(&YۉOX 3s1fk?=P΃[CgO^CgO<]CgO8ZKe,b-6_g3v3.y#]{ okȗ.gZf8:zOm1~[Zj> Ns\u>ƥy{Z}t}6B-6XG* FU;o9̓A<8;7景_p32G))X/ecjT߀2J%dw\m3*zMo~m&z֫oaBʡr)í6Q{_)͓p ehΤ2GmE']De&*{6S˾^T5LuNfdڊLo&[;6s=OǓd]<Յ.\Oux]W$Ǔv-|d ]P>nΗO?o![[ydV{+ٜHnJzXO˶UngH6loFoo#9?N[)?ۮQtMvV9Q̘zge;%BvJ_ A\g@U2j@uL!;vRFma0y'}`N|n.r.r.r.>/lwu7ةh&vpƆ$=aCj!uCS=t >en/=⻅Ƚdg4^~~}/o}(\}Q]GyƵBTzp/Oe!i?蜿~d? ?wpEF >H>y #!(;"?" /~d\ ޤ\eIwa=xU{y)k!GH#o PD!MFoW-޼t{Nנͫ]55]ݣ38q y)%R4K8Hq0+`K%f Y٬ul:A6 N'NNX0!:isM 4$IrIJ''< ?IaI*+NxÕO$r})ǟt:E;EcۑSt>h):t):):S4xu=M겧|кy?i]3NS',7Kn<}<SYn { fe7G"3T_k,7u@ӌy0_MA}gilgKl,?G#+ \L*Trx67WU~DÓȝ9ImHgxlùk;7'&w*_+yu?x ?j^w+zٝ,$61|1%z'h#_~?eOK~~7<@/Um6%sY)(5VOJe%(Kr_}\2ȽLt}*}<,w J&<꒎(q`?:Y<>+!' ЯkYK.,(ʿX1)XD6~V:~$Jc n.ogf)~nwgC^Fyq,[%^,[M/|kůk[5:k:<#`3)o]o;7ZږB\z̄n36)lC:8Ob{OkY#yWu齀e6GH{wNߞ³vbE{)Fͣo cBX+܇<9jce?Oή9o_V^ < 6|_);#mWA l eeWA:9#_%SԖ_Z{W0~幏;"OODD]ׁ"%v)"?e,]v<ρaO>e߅Mo':b8Ai`Km}^bg@o7ON|;۩ܺɫ};6{cv;xO`{Z^ x;H+Z_ f5 tߎGw1M`CtKB;a69Ѵ8?KW җ{ *?`kץL~h~0ݭ tuVu.ڟmzG_殬x{%%wwƓZwNtY0{uX/Yow8wR>m'9;ixw}QhYo9ຝ=Zܭߑ|E;\'!~0_>^Mqa%hoڇ rX)+סu.rceo7Q.ͻ^w Nlnes/8dm`x&7r (ҹW/5VzN} }/)'Зƿa>˿Ҹx[~=x*'G!~~ƞ^?Q:Ngdɷ%o!=t0R_k?7mhu|x[m>}?'mK_,߶^NAN^JtDzH_l}]Dg]G/h+LƋ?ew׺~Sā{R5$]F^͐m}m3e/Kow`}Osm}Xc]D>jVyYRgM `Z۞_>fӵ7vRcmuQ~ԓF91݁od>\_q}qOVopz^D^umy#~7oj7{5T^5:wzM^ŭL%G"jOWLs] 3?2޾I ]'ҭq>P'z޽:~De̢nT3LiǁQH9TObx*12˰| Ϳ Z6@p s~{| "\F7q߻wۿSmOΉ̈́pD&ȴtzڃ:YY},uáROR;!X !BWAm,u:K}*xz.EY[ ĹY@cyT:;:BI!<!́Ozh_P҅iO渲4o!^nO=F ~J7mCig!mHmHPl0םOmK*K)͸-zB=ٖկLjG4vF`/W3\pl712A<ґw'yo< ;gPYOɠfPj'[,*eE.3;ʐ*beM V2Mqxнô'IQ6d2$9'N}ﲎ~61^֘?|%MhX6 enڊy&^(99}at3'Gtѧ`fm0i~Rݜh_kOvM?ᅶ9/@HI.s7"s^-ol]x5N/ng$c>u̧iV[uGSkwY*o1zKC$Pj }ڧڛ]zf*{^;(3o>pTAŷ;qnO+4Y$h4_9;^9nr r5 Yzg"|]"1.qdC@6]G t$;#S;-Hc)ҷ֝wΑ>tiٶ% ynU{ֽ xa9W]ӭm687[`+Vo8>6gm\鮫i9xUy @Ȏ3|Q=m?弶]:qÎ4oe?v.mupHK8*ƻfC51zb* Z@h[mC>ϕ>|BVXC>2]6f?og]gKfGl+?=\ x,Vgd 乵&C;.4^ΦSy7Z{PE; `]~#ģU;ە_y|&]ѲbWKw msZ>kCW˯UG -ݴN:?v{=tFxtb/t؟]wlЕ@3 pi7>qn~nRx$Zvm_3{msǾʛR~%.&ΔEx_ޠX>.~S!swL V0#) h7f}'1q*]~t?C~guWU Nxmk.4l~JIqp&uϫ &淯{|i~o?= `K;i:)lOL&u A>Kw4g7;hM2WY~[|ZǾsM13."{W{޵eփdу|L|.sYgz^aA oAr!5ރ Ego%ڃ8MaV_"JK;iP3{ ڻm[9bxPyt:qiw]=}mGOA:~edK~y z-Z;ς-vXc1?&+sY)Ƌm{ow|l <{Ǝ=^;Ag9lW00GlbXz9X MyG |W_;$SS$sɐޭ}YhJSߑpia<=ø7tX"ݶww{"1]FwM6ǻ>v6:썞+1]!Н+`7aK0VGǞiE lucwZYx%MsP>hCxR?Ӆ\/늘O}e6R{^sK<}T1?ַ$_+g9~O@MkuzS^ F_< 9w E`1wFcƯJ˄X}2<6P"/sVEð>x2Dz˟9sz[3^]L7e̴͓?#c軣_]=R>auq36~}il+}@suX~!t1gC}.幔-`~m}=N$=p?~M+0ay7~*ͤqm: ,}4G`kEQ sn?o]k|x ՏuT?{Mm?椠/Li|Qt](=+~y2ue:nC,xdfmye}֔l_1=#H ƿ6 xh[yY2|;cz}^R뾏j`n G]{0ŗqCVsgxb)? O_xO$}%7eGZk shC94O^9qX>>k>wx>GdnV㿚`^`Vo`M@w-OR*z?QG[xx^3;99߃Gy][wgfwU2 ߝ,~fImo_(χ0&nǷ|0fD7SNZ@~(]Cu^x/qCuƋ tjaSG9淌l'I;F߲3Ix:yi@F:2|/s@l3+;N tw|aPu?E|{9eKׇu!Wa oN2ʩ2ʩ2ʩ2}yn՗1~[wL.Fπr r=yrv^f3QK[!K}XF~762庞ik^6AW5, w򾰘m#wҬoNm s޺e1݁}(0>{nI:-xff0g{LY"= }Hc Gp]wBx[k>>m9Ɣ-yM޶ FUtI㙊Tuwc =C+rR=KP#pɯE3ʼj7fel?J6/c8~Ͷ.qr^+{0 cF;|"E] )mƬQ_)RD}cIztko﹛6w0[97fֿKder簖QO|4wtnaYʧ 9O5D.u2L y-0D;\y):۠>=;8l$+\FX]l9aP^DZ\k?èCs~*' |'o>9fK#͖_z%s_o}_o6u3p<ϳp:f2 b{&ݎ7vr5K] ժ78o,y"QY֝O~'zaq4|kƺwx7!6ο: >6Vic'nٟ3ilB_>0eyΝ@.{ &w!v v ۧccX.mPmPv`]=0al?3pis *6iCP.meľp쐖 u82J;|rzal{kz(Euc3 g\z ۲qHAMX#nҲ/}$\ }wü+,r>d0ma?d2 rǮ:Er_/ʠdif:/w`7= )S6Cf !5Re?s;9JcM2//ϫR!i Io!G7mX6oC[z /joԿsW+N_k~'Rj͗NlY6<#Շy|*޼gcKȹ'r"u櫋'k4a5@_Z;ֱ|垅k ̹\fB5\KƼF70j>6aClz>?H(ph8gmCI=QzkkG c牅e禆rau+n-sWQ K;Ղ?5>Ϲ8%)A1ŏ/IA' > Oڎ|cmxߑ ~m} %0%~|%# d\g^pj Lk!yFS:,6?1*{{3=|4r72mIw%⿞yldw w%_D`b& f?5}vBly?'$'#~^rz$I9}ńIN-I81FIZ_O3w I~yn'K%/ dDy6<$߫=ڧDopz%{; 7wqR}y. }|7.o3OkR;ym|>|m!Kl%#6wKt>:ߖgǼZӏJkN$3BoےhZos:ZO1I#_;\?'!װ$aggwvvy>19;ӷ%;BI߻c'0Ԟ۩l*Q~|?|c μ|ۋ|3{&Z*_Zttn>O5r%M?_K_$ߓ&%9I>ׂ_׏yΞa|? lEl9y] ׊|h!cMI?6)9|(D9gP'28"QIǥg:\m (1"ICK~hՇ' Stτ$.[OcgK?@7<~o- eOr]4~|JE>ӓG-a$?srrDffo9[sN+|qq|"yr>U"wOͷ8{_VrO_0W;z&ʧy<9v \Aj~c"n4$'-OK$)$Yo`WjooI+%}ϼc \B.G uSkh9 !$cϧ&'X=%¿g% ǡ_-sM+mCsz r_f\7bj_w"ϸƼz&ﻴ$݌?"q!}L^Q:w-Zi+G}NM<$y M{Wr'bڽ6 k8= Nȱhyy`/ /%QGp?B>/03kׯ0~^e+H~7yNoq}0VoNӒ]$_WXIhGjsϖ$S?cw3/4Ƈ/k7"o_߿fr~ߋ%Q.mOioyn' g񾌐V-0E?ri\k[gf^MEd g8>:Iy+&^{%'&SgN䗡ei~?-S[=8}LƯcNBνo?+[+:2O7OV+Bʼ|z|z"#Z8ɵl}\Z=Їyv+/~K6R x xO *Yc[z%WGfN"IM43W@wOUWo g o]Ӊ?n}$}:}߂V2 MxnA;;L~v2hؿL'0^+9Ӫ3c.E'}_Lgdym!OOsDeEZv#׮8UL.eƻ{0 }vvof?*;txߝ$SA^NtE;C7[:;Y2]J{to0]lb]:'KxV]Sl<s{70@)x&#OD7r|zw񬄾}ij3dy{'nXlG~wF,N۵`ݫUooL7 8礂LwJt?<ǵ'IA:gӝtoʹc8b.Qw؂\b}7L7 f͊ߖ|0=P}3!g%ϔڹcZ/}ۘ?[Mlo]f;ޞ4ɺF`3dU[x&RNۘnm2w~nw>˂Ot B'# ږ Fd |;gǓwb4=L_ӂ}>IcG3~h[~"v~;޻}Z sZn.Ota]zci>ѷ$A4e#hoZw˙Ϣt3$ۓѓLA wWe-=Iʫ,'g[O1_uNn#"_ ^<'^W_m|<\&1:Y-䣿w_ۂ~7gDz7$?0Mq^lY/tO:^f1<֎t0d%o*\0OGk=ew4neǥ1\|+sSꛭ+ Ǹt2#1*a5sRxe'[e'w]x;w/=x0߼BȽOYs-e#}DT}y`ӧ\Q߬9 ;%k`SNw6Uҍ8r Yx bpǗ]|Ø-=eS cF"e#hzW.ս6g֏o3|=qVBTI̡KL,c@Z%ۇz̲ά1L%tH8xwؕ6n10<1nŧ6vƊ.FUv|ka.JLaWdg?K1UsgƴL/+ִ6#}?=iw2Vؤkgg>t-9S]eTu#EM7s>!mBua^'P\7}-? 4s[u(~ t_ʎ߅}z3$wl;DC{6FЦc+&~-emjی==go=wⰗ;WߐD[n|8rsرC>_|_X63pfu|m2^`{=t *Thuwվ2qn}AFMipA T 5"x=o6eSe< ?rU[d+m`=볽vCtVAX?dodLПWG>h d8g C[}jYm >\eiW0u\,K ~F{pܝ;ߤϫ:}~5>}e 5d א~m אoF x\t0gP*nTf,c8M'2k娵|oJTOI-}iknZf} ֒KjI-𻪖,_wZ}N+wr?=xOep%\Fwg7/0051 5PY^a'Ogɷ!N\=\zyg,o-NH1m`3|u_ |u}I1\bx^wGk{ϗgA8=tLWg ?J  7dtg]tn5._?䋰|9c( `9m)\oX X5d{06o^mg6^Xv{FJ^.d~ʫ˩4-L ?)`Dϲʉ+z+meW..6PS?>9~i*N_)d8`{LCz$]-)w?G;?glzhO]t7.^ۿn(C qo ;_ vm.~ф#{gqOn=֣F2=BWlϧMjŌQtU~ේ|5 8jN81?ak%{ i+K{bMOxO6n%[.(S@O3^9H{nxG{ex$ޏ?;7A5W6 !{ueODOx/jir7柚|Zh4w(sFXEL7jSϧ2_IΧ22ʼe>y|*sylg{]"U!0D׆wx"O!&L[+I8/iq=3_4F "L}{0i6FC̫<9%>7]_~S:l–bز ǶdbV5ݯi!~2N{QqB+"NgF΋P;]vf\ô1sL>~ô3m(aQjQjE,X%qWlsQG[Ϸiuanh_x+?EkKˈ˔ PS~w)Ĵ1LQO R68GxXs /#3϶p=Cs91se/[ϭ =>廮]7ggm>WtM7Rz*bHE71vNV|4mj L*kH~Rd;TfZW{p.˱,OB8r0]Ec.])O~dVZWnoŧvcٔ'`m: [9Ok;T= ȫm=I1oae~hBJ -Y.r>xVC=5|B2/p t@[!Axs!C8 C8™ tAwE!!lp9 \ a#7B].a;oOCx>/C+ 7Cxk|yfݛ?0}fgi>mfyP{'ְf,w7 {dPdktL1fFў$Pt #铘~asύ,s?p,^c{8nnI:E/g(|cTkL,@<:tP[-2~ζ ~<ߙ1% y6Ҿg Az5]Hx+r_aύ`ƻBJ7$]oa0FcC2js2U@ cum>ӯ8SN/B|.T![=<8.>ȟge8MLm)Mɇo6{N\ȍI^5_Lls,ufmc8YH-`G`Fn ?oӭu;uh#ԙ;f[Z_t/Cܟ.γag.N# -s Z'La)FĊOMB^jwɋ^gKnmar܃鶈rtzm`/lz ѹv G\;f{ӷpa8/i{k5.^B>w%s K.<ƍ2Yq m'^;{.O6wu:s LJ5{.Q60/l%ٰF]^{ |66za<> 6Թ+& m ୗPu5O ʻ{W)[3xɕjtäuImF@e}1QAxy4[3џ0p5ws w?s('0/.ukgw1bv륗9]#hF TQxٗr}@./4W\K!6.:2S/J/V>Ʊ)G嬓!>X>swhf z>V7]0K2zϢr>!,{2vc%NMa 6S5ga/C(iN5~uvnO|:Ii} :s9Y߹ c|rZve υߵ˭TW3ړQ7LxL;.e m e=gU*'Y~u8v|=+_Su-Akֲp/.=-r # #m Q~ c-Oy^ƑYP부;.P223dmK߃I|+?ut\y嫛(Yz;8ϟjK6̳֟QaߓiZAm`.n v8>yz_PWoӃh=tkZӋ+ Qf3wZ :5xu s#ybٳ^RQGZ-Ԟ,uVpwwpok@XNJ1Acݶt#wS]AeeޙGJ;o9}#x/l:~6hw3GbW-`)Gğa,a':C{ ㏰ɭ57R!;CwON p,xezŬ _@{8Bi{,v ^eWtMPy9}-=PHoI"׶B ƛ,Cu=~=Oi6t1= _tߌ2~ 'yf:3C]>`zM6]On]n\Y?,aWv+xxޙhK.?l?z=U:¾b7N7vӵ5%}R?g'Ϛn[mrν< dޗΙN׿.c:Wٿ8UnZ灸 ykڗ C61׉,m8m^(=`9}>W5a]/y';nLg뾉U4֡!1mI'!n*k nuO)\cfSk E |Ļ}^<}+ӌS;OM N9hUN9N9_@Q9f[j^e/)_vQWXlfx(TgC![p!aNI G_x5geƿ؟A+l^`x6~,eb9\atb)?|)?F ~@5sUJWp{m#pVCuT׉1DYv/,k*`rpx/Zm6=7> ]ԧZsfx>dz& =uXi]gr9uM5of}7zZX|BI_ "yv7U.H/]59^[eMuGgYw{X_hKo+%,l<ؗ(c77{_ވ[WOjn\:ϣuj랣F[j:a6vOzE2S)üf e~ ~ݶD+)ujQtjߝD˂![ _xtHp F|懴39ms•9JL2.sMhc}a{ld,=33?}OeO]vt0g_t3Jh8V mݛ gL=1GѦZh|Q{?R؂/lz8/T9kZط*Բh9:%om&k`|ꑌWxp6H2X&j sϠ|:R}+};Umӻֲ5Zs=0Zu.8T(XGQۏA @\I~]eky)ȞB#ʞ~lOSk-=%g}96Xg9g OT ,OSp67 )7=chÿ6lR;eEYp:\Gu־NXZ6&Ugu`Q˸Ӎ~Vߐ|A^K3<؈m59Q[sQU 11 3:JF6(t@1c\QѣH01+r@j/1"XyB~ω C(A[>F2.<#|j{F1U@U%tK |2iG?ZpcT 2ΉTEԨY4XK REj 쩊PJ_]?AZVvnD DOpE* @ G{,i0jjo(?p6 U@AW 7#zg)1zQvV\5a(7℧BRzϨ ÕF`Dp4a:OQ `6 EkyQ3v{Cc~8c U:osp:}+127ŪJ !z0SHEh2E >o=I(D/=2u aۨTzj~HUaxN9nD%36a̔CgӢ~֬7PA"P_ ƃ`QPh2y\alOM\*C5]5qF" ̤4b}Uj#A,0ohԁ*,Xjh5T 5UDT ^n =UFBèA2ΒҒUTP~i #٨F@^EF1F>Up6P9xސ'lDaÏպ ; ]?y ھڋRs>[dC?X[h> ū(.:[ u*ӴX1noUS=RGQT a5 Dy^g$,0EjQ@ ;ѐsAU=aiY28mnUFZirX#E{RWV? TbGg6AOFBu`y+IؾvYBMlPw^j !V8VEUNSFu(TA=0:S{vV.hehV`mj4(sw닸T}ppsȭ; fP9Z!?ѷt4[N^hsGH< Gk8c RUt&P>z/6̈Q` mLc؞U\V 1.Qp(CV\21P4=փWg4*@cm Z5U!jlta Z. 6\Qj0lFIxE TVEE5Zn9:|PC ArYȠGlJც+"̥~=)RS_EkN\z謁cWD[(O@Vtd*0U0Ԁ=#詡^<`h A[VQ>(0C& @aҖCB*f(P I㟈M۞t!ΩLr9 j+*τ,"s(^iAU%SHiWVVi$腁Zꐫf>WSGpZ#^PeD۹ V@`s{]Q_i! hաS A{V@P(zEFh'Ѐx`Lj!Z`lUeri/ R9*P)$y'R?h; ~j08)h1`E4xݞ$qF%>p\q9+9eL҈ox攼<^7+ Hj=`" t{Y*ZUA]5*`∾!a>&P c*CEQpDuנ2w>˲MuCWK=`>Mtq2" k?m̋WQ77T9$L*QpJ@gG:3 lx[6N?Q(:_ S|TnUN(|TZUg ! ވ2BO(`ԨBG= ?E=5(d$FH.l2Sp•`Fi0ӀnjGGLGOᑅ乚0ohM^bP!jSO}zTxqꏗ%x9?jx ,Q VL\}~!d( Pu ,ךּQppUOF, ^UX5ԹhA7"[c#18Qmj2ayC9v Z h7<:(?I@5 3 t:(Mvqix҈\ZG`QW\;ț4YR,+TZ\8|cҢgyA~iɤr0YΊUC:eK2FAIEd`](,\בbfVpL55V^C/ TQ#h1B8 :`C@ݲ[Z5 ha'` ||NڃT y t8iilNUWpNwͨ(5 'L2̇2#TI Nx%& sHyUi+6>sx7ujS wVph~C{$t@&nQA|zRZq@cك+ƌbU&J-6JK 'XZRTX@IiYq qťeg)S'A+,)(̫(-vY^W^S՞0eKVT mQղJQ#0yv@ƵzT<7N`.?4x 7/TRkp*t^T~5e =v_eAy\E'a(ڍkV` 5Er敔u x*(R@_:< cjZdUhPWh\6pNyN!h\V6loJsuQSvV6Ccq}UZ$)NS-%JkGZVf2tOEk#OՆfFE^4gٌ’)T^^Q0]μi1eyӧ%r-a`kTf(@\' bxT US"l=kq paW\*sE8Xir+zJ#x~_3v]QxCQw|q!{v3H(fj\ji}n(G;7; xUQ@V z٠A866<_]f N#B RT.n.Așg'à "910[1wvN: F# dąap YuQ1VU`5y܋T۵ˇ).R>sFIq8ԏaMԹNG>k%ꭨuA0)Nd3Ng8XЫJzIft֩8nYbq j WկZE :% W2?tm\GH>?eIu%9O)d;oU@X(Tb\nPPIe%m䁜m ǛXKe㣖OzAHI2lnh@0+CJBQX(cWop8˺1e+wzʽQ* B␰_mwno , r]OxQT;^w U! h:5B7n"ޖrOkwSY3mdclֺ#ڼEd4Y+m>niVʋ n qq{jRS=/KDw P՟/x<[iFSoeIh"ˮÁ,cr1)ȧfe6~ ̱=-Dp p !Fjw˂g)\ <䬐J&Fn8l ff].kކbGJbߺY}Vru?VjniF msnAޏs{?VA.b֤=N=2J r>BN;rᐟ@v{P@1IX6" Hº&?c2 |oI F~͈ͼNLxv'[{l؞F2y֝L3jSAH5(SJފRLt,yXL bPGN1wpRe8Gy@_)','lKKɥ8_W.YԪEո +G6+T@ O,+.*|OOA1@(hN:%+~"0ȴVLߥթ`jѱ6{jڮ,i"Iɴ! 3v >%"9RκGf\[ ov:TƑJ*]L ;NNآc#Ƣ g& 3.Sɚ>X(t2`ƒlmG!rKGȄ${PЕr:w,HB_־u[ล~rkF)8WiP)8t=%fRarCsOLEC-|?2%.BL]{(bP ~xVQ1r:@iPhʎK1sRÑkNY0n 뗮^pWSU/QUO/U%o}6׏}s2: pq(KY2)%ǎ_Zӟà8H?JrpG㟉Kӊg7h{&laAfyf|ar)a8rتQHq?}Loap5e+RK촻u-,T& 9Zճ*DR2Rm5 +_#Lq^O׷U\ B&Hy(?*{/M~ reIEk I5)27Id[+%,˅Uw3B&W$t-=0N Ÿ?'T'd F v_/s@)j!qr/ԴLܓg z➒S(4W~i 8cuי;fg`NlޯT_%;\%BBaBE~le#ѻT}NF#9ؚ>F564ꮪÞLF.c) $fa@8(T XI/:-tV2*aFƨ<&[UЍ̭Cv"N1\m:"]}N9c,iPaPӏ_ Q}_K7S^/XqDÓh|rT6I Ͱ.Z :y݅Z7g2KuZTkʻl0ŢR C,zxGx{.dX*ga! ~5zVym땥egivYZGY/dКf" [yk(izcP"vܞT߻ &;+8NSʘEAL鏆qc6I3nغͬ\K&)La x٣. j&kg0Ա4S93aE HLSS˙f-'yM#l)ct>mx3cSji6GɎ/ ,ɇVd©_ԅ2;a8/!ހ2c'!JdCYT=2;\bLJ8N$grx*17h{2w,/Ia@kk؏/s=:7Q1wjPGIMzNl|͠AU 6yGrxpeU7?Nh?sIJWw*%)kC҉iv/7!Qqr=;pp{i0ѧ̾gqDGkNK{]mv+f)^Cx.J"s(|oKbLG[=%c|^ߌ#D8_w $bI'zqق9Xw? vcxI se 7/fJwu?3⸣FT~C͠?Z6bL7Ri<[J(=_{cA)ZI 0PI3 `EiYtގuAڑ+G>SVqmӯ0Zh7g.!rd4oiJv Mdw-I ,|)%IFFŢ3b?_?]"n2\&|9ՖQ!D#;cݝN# =R%.T6e{3Nb p#h^9&h=.j{k7V_RJ}\}t~6H$e5DWj:Nۏ5*-a#&@#dtrq[~[cR2cݚFG8fӂYH_^#:z] IP(T)RƲ&v')TeJw(SbGl5Wۓ\HP^hH2^a( vD\qQEY5Ox0`ELGnpN+۾bҕZʋ3U6ؿ>HxLAW简k◔hB &'5Zd&ag. W5.""sNx+a ۥl[x\UN/G͎҇məޥ_q߫ 2R>,P}ZFC>?7!7]2za }'S˩:SiZs wSv.ފ!ЁUhr ^>ü :D:tw9jaX1_Y+keUYίل[~:`TQ<L \/@07EAG>i|/RW$_![ͰV v]*>\x)e'3fq:y"/2I^`9P,@4DvaK4kZ^;ʫxwI\6/<ȗyqQW}0gW:t8IʔD|_)sCu `$Dr}5A]P{'ϩ٢yrقEmAVT!{C2WN*fI/ʱk;J@-B%X3,= $MwIJM[%NG)5c+"Lo˄NIFӸJ^KV[ ]̹y--|,r/}6߉F|AϤQ5.a;1u4ɳC_fcDUmt._G$N5 N|w TWL ʱ@PYXCXRdaa]"O,.ۂ5lm|@?eyc֦;"O cadA,CgIko1r$ ERX,j~# dܙ(e0uFpGT&JIgHGB Ī ԰V^e'\~ nT9S.#B4-:TvRHX)$5eTsĘDrxȪL3e-+utf-GgXJec>\ϖ(Rv? ' borS_~ d =\ځvqshza|xwykxFUS,G8Z;`[Pb&4c[Ź&j˰r@ sΈta@ f[Ìlb"?{eEV@9~Jk&Q~yE= n2)+t"UN:HXW38<:0ٹe[+N^6b:\=uH*  S9}_j# #9Kp2+h f5CogtnV f~j[ !fy҆M?*q+K G0+ ȝ?0Ǔ#|xi{#ϕ }FrVD4#Tۑ|WY\b@25ܑghP Š́hɑ4N֦[8<jrkzou醢npr't`D]t:.PucD20{0 :B:\i؁;\/@L<1$O\ÐF~ɉ B ӿn!p;L0NTࡋ 8p}z$02;@#|D(04.š*nDN0՟ǭ&S8Rϡpqȸx2!zLv&&>,jQ+Ϫ&VPXdnlYn' ~Ij.:rQȩhHgWrn+RD'"&V{z0"䧽#)Á joaPpBC˅r[~eoH ^0I)4%unoz>qǓs@/5I.RsUAa2n'۔֖ݳ(y:"|q(N9݋ٔ[dnU?tSRHk3W8R)1re Ҳ%5fe7|%QrN^ƫ*K3l\âC9:4or^\ ]M_KFkiusC syӝ]A) Jv%'\iQoXF48 3,b},{bPxM4,"QBhT7Y ¯ǧ5OWKED,VB`7P&s05_>:1v, €a^RUaϫ{<#HkNP.;m(DLͬۉcHg=ĸ5~]i8bӈ7İ)SpFpRoJ|)m&+NdKO͢2f" i-Hgg>Z1{߄Ղ(o#|9pl5, .&JaTs)-y:N=٫(Z{Js X>F ]'Dh zC(}py봿l%|Z$*+F)x=ioo:rQK2Kv.pNM=(Q𶨁)7LS9b*F؊vR=-` X#&FMTۂ(L1e341޿)[@suQf{n'|nO!ˆ+&}l1}ֽ^Io*c9mڻqi7`d=t'agHړQ~V-^N bzM]U#ZYw8m5IGc 泥+3t{W5niF<|qm}z{;͕Аs8~~dI m5rlW{[ ?iW(ڵ|ziKZj:ԇ2 |3ۓa<ד8\&<=9f 8?gWt6MRe*lU Y9hmHnk}1q H)<(,Ր5)j0o>U~jЛ%\%= tWyiW3Շ43Gʻy8ZIkܿzn+W{T^WZ{tzQB9f8ЛNGV< V% <>|)_/vߊA@Z[U(o9ҮA#[RnO3,$x *U'eUWN{ ԩH U):OJpONt|U,43ߴj3)l !u pmv7`Oi{[#0Kv-r;PDa^W$nO#ˎ(TypyMð951de/+,>R)j#q>&8b2zdž/d8NzeLWII~te59|>vX'] N3 xb]uV͇"ꡰZ|#2zyIV;Rԃ*)QAg  OܽE 1ïT(ZDZB@iʮ_y!eRPNvp 1VgI{ꀂz{fE2U0?OoG˦\a!hDQm[ȎÂzLKkQ~^8!XhEPd{P k:vGܵE|(j $NtTħ!>\q+m^ _"l er' ćWnGƘvzLRA =E 9.vʨ^\|δ\&\(jQz }qfQJ^TmUSLsCa`JJPUDߘ9xE17: p;\FƥЋڸ#d ~2ï>Rɪ J=q"5jt"n[~2yv9%$,R @::`<6[2jMp 8ΐȂ@ȪT+ٺS<W%! < 'gU@ csѽ[o%ft{;^>HMt.{@b]Oq'P#8z]4[̱'8/ |Tf1iԠ66aSFbiv$ǥJ* 2 :y3FhR'~F MDPK1~u\dɚЗgFX$KkL=W'qgp Atbu;66{ƛ H茏u qrz1 q~n/p[4Y 5dž v4G7d+pQP50Q؂z$Do$^%c(Gp^"Lq s8⭌q꧔Eְܟ8l Aڿ]xFC /J3`2m.~{Yw DkOa*TInr JDeY :G/{~bPIJihq?6*yotbgpG win+im?dPL]§2-+TSF~!I6<^Mqr3wnrbqh!9&IjMK=ZNZ9pWXXClһR?=99H,UٔB02TQV͓O&3DQށ bF Mq>^$Ǥt i@a0}r{#WNi/&JrqzRݴj/CV5 :MSd>Vv00y۩*7t(HctQsR7\&tv_{}ur&*҈tz¨I}&pJB34y֚y\, ;N9"=?MM0SbH}L9Vm9,ft/Hj0 XՄ=UC`R^ Ue}闞f5Ds8Ɠj7Qx!"e {E~Hy՜go vQb._st1̡V|͡ U~{[!br^*mW8_ KHKNh*ݝ#DM ֗|\*;[E\Xkޠ1ώyFJPAIZmi$YT!t7p^>*貞<\ N~μq q5e\ gR9$=5h0PoګlMJpDf6~xF'bW6s"/(>z9a9\gC;Rr"~^nCaQ K5-+"0lR+d53 ES"y=aq&ն7}pF _'ӉYD[8-}&f';'!an h/OI 6˜cfAC򑬆wf !7wn2s@H M"֤cdcB =H &>ZT$IjZCn6V8wStv~:L&])9XLy2< ]NN=/s%(̀k/ bW Ў2dU3Vm'ya<Ŝ̓bhD*|*o GUktu]sV1Ͼv w!Њ#>RѠ. Y^.B@3zTRU!axz+"̽rx.0 \{³;n$zO.|ao*5 :.J h P+Nn/9NG ]|Rˍ3F [$ɡ,\KH<[9<ָzS)LtEQ@: dv ZO#~G.=LU3 YEpqOB"E -_1P|醙% +ԶB RGr 2d{(v~*.޳ prV.x!;3懣űpǔ;(I"ln݅|)AĐw rܼYUhwY{9`M C`'xM}2/| +,ɴYUԱ']]A:v6cL]OZaǵnLOmL .Uh%6ictA`A? !`S#677D}rP&ƀi/#fg)i2N*$\ĕ)fQ&BHR=f 0ݏLGlLⲇu)CN^ԧS)'_LISbjIwE ٠gUa2aIsiG?Can^K95`ċvaTN6UI@0|`M{$ b!v.h ʗi~bU9Ҝ|ij\Q\IX+9!igQ!Q%qbc9N4ϴ-faky_t$d bvPB>挂QtOنDQu)p<9J- Y;+~$GcWHvʥN}a𺈄Vˁ܅fjwaHoK 8"͹"*v~D,"QMRVCQqqZ.A&rkVt(2b\c^; {M+&h>v.!撵>%wmKcm:{^CJK*FJMAE֗;2<'.Lܟ,o,Fxxa?pYpS< G2 oO{fG[73 #UfE5_V] l_s^+jK(ՇD26 鞱{TVT;t3g;xV-eQQ>[쑠9_\65*+SGp DʅS\yv"%=T3;F.U#Ѱ>vK9s:XSg (g2?QX cY д2怖=Ñ0ti^#!cMbO 2mNuMc(ź\_ w׭,\Lwgߨa ^uP);ՊٰqUY ڞnEQ3aSeDz7 MRj>*;=8|`Ϡ Y}yGS=2x]t}$M;LC򶑳<u򶂊,axL[Nz[H| W)31,xS3֘25}(SZIg4b97XB#/\N0iI#I+@xnw#RFjNtIQjRz l4iW=>Ndg{e릡+*Ok#44t6ylQ }+{C40.?Y၈f 43<@8vHogzdQNW(aT\|0)6GA1U(NDz׽v}8i}2W-{?jմޤM3z-\$ӶӛNmEWsڞU_VjYS׌MNʖFHutPhq ,wx?N5zk84Nttڦ6 ߺNϦJ}iʄz|N^~i~Uް[/˵x4]yEccgYFy)+ca[I ,o2,1N֟-ROyqk ?YQ[GK*wakЭ7;ݧ\}(kjFﶦ!oicuC1Z38PI/pUxACX$".Ouq5(iA\4.=e)T,ރ%_*!>@=A n@Y%a{#:pٵ\C[CiB9WΩַԼTZŘZªS!v(uػՓDd9IM NN3=GͤY,fN=o|m E=~;r@]1GAJZVa#b4:iftـVtGF$Q Љy\"V%F,E*\NɄXtq֚3C :ج@?.l{`B$ RMdY Zkذ6/9j=g Qdgȉ#FGC, ߛ@L%w7p\7fM5ֱ夯Q^ b?y}:N} ֿ.}zn* TM"9mE(rц/-eZL[T*F7eKڄz٪g(kPHn"ȷ<}pwz> k0^jpYqaѥ3 ĕAb9og1U`jM[Jx}P}LwOKV5ucmuHt*Mv_^¦yQ@(JĭֆGi2-3} =p#]/X̝!JM ٻB\qۥC4ddlIqQR`d >_Ryecq \s8Ou".ʹiWXD"]`WQEdoMe NCP!7yɾ(tUgOgX2N^ ::E* ;BН zx Q#*עTUfs.RIǽwѓĽ+;}zׇK}fԽ!2]]Z"N:ŶѬOzgWǡ t4װmaدi6pOr7b;sjW^@wIW-Ue+y;jEZ@./Vǭ鯕G5gh_9j{2di1,&{.y\bUښnJgsf2u4.?~H,[$?vFQk–,gK[VT|Ps  @vѰT=kӗ_wB^2`5u ?:S|vITpR64d @@5VJ~+xl-ɶIC*AŃ9h[DQJ׿%7g-)6/+Ǿ(垉: 1εVX<7Al1$a IB\O8cR^4; ORtA +zyk+Cq)pHfir-ձ\ɶ}]2 ]0 vKUoKp]Pu|H; PC)Aad .Z6?Ȓhx1=>ùgݾWdA"T.7K* NEcOjtcL'GW`_6f wS {.4i >G ui} M=miM>$//.F)KJ"Px{@mD~8+͌4N^ɂY,|>.X,0ӈhļ [69n\pߐtZ8jM/,ɖ 烪@S9健f O% v4$ xVڝEWRr1!o<+Zn2:*2J`vR(ȿ'~i@؅_TrV,祖 z#~TNvLV#hqEBE/SSG/_I߾B*W*|kD [3</ 0cz[pFp0*A^Nt\-h qj~`٨va eQY `bjt#*R3c/ &_Jt/Rv)Q`ioڙqdsj/ݩfFuWJp-|;$5Q(4AJ'Bŀ&!9vG 6;9MQO*TsJAV=duA Qv)ǽfY3MpA;p%͔d/| ͦ*i֎̺^>SZ4R kOz@QR-bP3GeK%6j wU*{C;˃o^/2yQe;?ț*Yo%]TAkH? M*>ZdfV:l4FMll\yn 8Q򩔀Р5^APjJ[(_SW/h)6Sj{ʹ9Krߨ닂&唴(t`cQsGrdEy*3Ϲ*ڼu|;ͼS,~ˣOvBm״c--nbs >BGԲ6j smpE"~qc=;,\/roҮ!i9'TTJo ̊yr:쵽m+m͆(yM/yN iŦD[3dcNjM}~yԜ "xQ9$F>OK)9l͠n|9xT?Ag/Y`r ,;|2iH۩Mt&`2Kb~&UoVc4bvvq"lyUeiN4Ia&Ti[<)~sFYۥ;9O=?-Im1j0% ƪ%&g4yQ~%i蘬̆3p+rDy\FNYS'mJ12y"Y)Pg'bujtP)يiG}{yv{!bϱ;λ=q ].Ms鲸}3}V@iȴFdDcu/.UOՅ' lp!*d 0@ `Y-,I^.Af3Woek3qR8[e@-P9Txm| :h ՘YxAgLO2p >YKh̄2㰋`礽xq[t0/ D(8uMIkf>F}>0:= D D#P]\=S< UjR~RB`-vnHpah,Q$z![֊Dy1yz%pAπ3R$L#p.y7vh*R~p9kTUJ8 r0- 590]y 9V[?CZ7a#W'T!k2G%mr'+ruCEBMN~n5_ad$u:}wJ>Lv@{E<ѫ@큧8fδqqV 4N`}x2/sSa@H>VŠn ӷ'oכ"8fDRapxR`EL Pgh+S6Nl xQpL'q6(ZF΁A'տ,a^)xBFCerRY,HW-\`LPȍ:B8Ciy<[@-\O0nAp `Ipa 9& . ٮYG=9Hb2}fnjޝY:QwT,B+{pãMobe<;S78'Ɗ= M911;_],,f~&p˭:mRټTYBI?}ؗ#D3ӌP=r$V3M 6 !2 k4Th55quk Z .3`sg V/)0ǟ3'$hw>nNom?yVo5Lw3&OBu7؛> y݃XNh\;,a Ȉc:g>ͼA"0t 1M#(cv&/i)@Y7ӨbB|9ﲙ#Ox\dz$!6~{SƤDqq&M~^^)LhVŀl y-Ʊ/fLvmjU]VCJ.49/Es:O =*dg5 CzBȂ2ޢr~ogU;E;t7LBl5>Nt[p>;,5 ɓJO&s/nh+1SVy9 6=a.'_s'3"u$"[i(}J'U`Ys=1\RĒ[ϸyчt'A_Z/Tg{gvnd_ޜ#^Xe/;d~-Npv* tE$;\Y[:f2ٖ2Pm69mG&[IgYjHc\bЏ[# P[:ǣ(ڙKh?ȒүNur]uU)`cPM9DɪW?KATUuɬ `q7xtxj?}tSà>#Ҡ5k?LL߅mfoU=^I jV)Y6⢓^vYYZEgwU]Vч-:qـSa0nuh ;jר<LѨ7b!ŖS$Vn"E63q̗Z.@FLIY(HFqL-ُ&zx"zšemV^cVDJյ{D'иru 7&cotq52bCΩBl!9S4ݍhpLr.((+hdrGl,u[ײQ:PeW%X9VB*.| Hm_S VsfoեDQo#paX0N.u8#ҶN<c_m*_UU٠gxʮȱ}re ]ad)--CIV6lg|XWp3H^)}ȑǙ`t BgíwMֿ'%w0:f3SdŶd9c>\iFa8AdluF; ǡAU#Ə;d2~o5@E;gYʸp+"XFEL ·*`*gYL|IUgM䝼 {v~_(pE,8W{KB브'3}?2RˢL raºĹ]Qu >Cwnmlk-XOt>>sݘgL95X rCTKGMdiRfPGuM)L[ijzQ`RAӃ[Y:~gNȥ>8-+6- C,C--"`&(}ɠ.̧LuPβf!D=ٞ~*D|9'ox4-0 5A$\>E=A |}"eg^N- 7`>X}pO AH:+oͫVv#(0Y`|7|UlU8O;ǭIZ%0(Dir/U@Il:Duo{RQ}!~KwΛ6ɳшkрr`X/JHn! [@4"?6[qEL"j?`WSdKIm nL/{YRʏߒ'+2WWʧ_[p}wkTݜ?6o>R* R#Q e "kQU׫#HkaR_ד7!IeV_m j$ + Ƃ&eʅ[2 !]TZR|15+>Q \HVv\F%M:s/pV7Pr r)W=i5'}@H5O^,};#ꀎ\Z(h_@[0Lw P==:3rga#QѴB!9֢W"8Y}6p1}ΰ@ia/ %\/ `ɓ-ʃZ|" 3<98dC20Mn:֛טmR;(29oFpUͷ6>mAئ(r* R0uQRy,y\iCR&sL/ 35xK_DԷ&)q;JYd r AXhU W}Û,󕷫b麐Ya"ʬpB޳۳dYjBAMTnl©]A_(Uf}Lu}Wa%j87͵^iС: lMҢ-IM45qh-+5kO`CiD6Kls+ȉ S+% =C  AE`Kf,F#FSmD;ݻdb8QrRN7 E7m9{ؓ;flQ տǯL l 0 tܭEŎ`[m6g\ҌhP<=6 X9H;hT)݈F'm9JیQg}wVFE8tYOXYAgh:') Y\`g8}ڭ¡MW]=p#38 VÜ &Q%adEvزDd4 2#ǘ"0@^q^tǏ>fEFaҚ&{@7~g.!ߺO!tx :ۣ]o2~jGƏ> 57uܯ tto] [5U]-hB6G-$T'梤o5Jne]#p9RoȢļnbxMڋq|d3&A=BO&vukf kwz,O%ai\H8n]ve4KogW{k8}T2CzjR=Ću{4~3N7Aɡ hrGIR]0z{k^4']aR~դB^Qv?.6cSG {Q>bF T|_:6Qo[=Bφ7&+2 ߡvih~2, MXZ 7%ݶvSBo3O+_N5ͤIcYnovIz Ԑ(+4AMM?rN#jnl^#}UR\'7fDkަRw R7I[# K-GI,hӚdWk t9vsgm1i8⩻Ji8:3x2j<wZI~:׽INC]/&jVКs꙯xn whԆS#j cC(|bH|5mxƉxY"iw XFj?A³vJqk2%8y\.?\׷F5¤;YnpZ͎Y8XΌPjw.D+OQpohj͂J(7LL=6=inp})/>ԚKCi~r.<{8JD(.q CHPXօni0@[Yw0 爼tjgd8)25!E7-Xzd2Sic' O)gXq_{jvxW9vĨMu;牃nOIMUtJՇ#4%뺝(; {mmekBfQ-τ6ioܩ.Fp\0gaW2+I6ph(4%}U>n}@tW14ʉ6kiJ9Eth`8Yb CoV&] 6"Ma*. )TnzMC,M~QFZX]:xq,;ml1VOLjx>9oiNR:w&_ӘjhkLYq;*s=ꃇ*O kQSY^|JJeىP;ܘ_M)^ aIoJ}u$2&JCWX+h87TĶiQ}pOeF R*u5ɡP%c\]4FvU`T٤kDfmaocQW;i=^TP!lg:*qӖ [k￱uF+^IU&X66Bs DԨ7 _n+ú]b} .ҍ9|7m9v  yD8^Lgl߸qz;} km󻛌םiu*lN"FIj)X_l~rgpf;4njJ%L?I->oש9U`lg3ČLdk8(!ؘ<]:r YʧwAjTuڜęt $S6 Nd+4dp OTۯƤ!^F}D}|z+ks+az׻e.J𪹍4-W}\}j(uI#]f?I#uCu7ˮ\fsyn[@)L)fAP\:>0p2J| $.Ѡ8O#fܝ1/g3jHs? wI.qYg"o}.xf>jMc_|D+.RÀsj28UQʤLXjrRQM\ %{@X˩Yxmn=asY0TCB<מjBvGi#Tx36.KQ8.E)yt# $r4T0)=G%PQOf9POަ3(/Άz$aF= @n>] Z8֓n Vqi)|*";Kԅ11Yt^V&r6'30AS0 ){(f9Wn\Ã%3 *dmt 'o؟ D{9M2|Tg}xԛu, 8 K-iL?Cԋ#<#@ȮpZBuT1Oh|͒e.[K(e쏇xȖx<Q: C@Qu|!9[c |)v) Kt"*%/j(|LGXX_Cfv4pLbU-STYy*q"1!GTnM`"&G:UmӮ9p U/'{K&d'-aWsXZY}1`n:n/M]@==M44 zX EoAuG\DaxCT\;DFRs39嘾nTzU[9*U ۄA^e:}hnHw'i44^m“Z.<jM?KE!.C jѥܒOs_sY`Ӑ5'a@q:wx+ݣ*q:4 E = m蔼jn*(@+NN\[:&KJO(j21 zjO\Seo{ϋ%J8E{ħ"U_l:܏dyJ%CbRWǜAC‹yu,Ho>J G~^ӝʻ$_UEu uFSǥ[]l}OnGmw< 6X=eKnDjO-ʂwJރ3o,}[x,q6"Ōγ<9| zG߿&_ F#P[V繞<^O$y=8)` YPv9w#6 )QѣӶx|(v;鶾Vºjt<]*gx%M׋+0VbuXRIwcٟtn]̀msZo\DtKDŽ|F~hT2N{rhvP S^S]˅RT%Q W.tҮ|(z)Rȹzb]aXkzQn!*-mR]J)$iwi55놏&W3s|=3^5df{fמ|T]#w3LbL6$_0K$w'bY W  v^s:Ȉו*([XV HC*1D9Gayz_݊LDuQWgt~}Q0v=2 ^Nƽvjw?Soz?ߐo =4N& &z /rJSt!ڑiinOgvN^"eQXQ4׋˖R9^]^ԑ7z |A/'#K.4!ʕfO7:Eey$98YDƩ [<'jV$+s_)?7 (d<34Sy<)Nj%ώ²iZ}QH[\*57v.8MHFL)[bUWT>y\=³_w8FgD[pjE O/7.W>xq'hm{q|n(o+v%ީħJz%~;{xy*TJSw+JZJ*UMJ\%LtÝDN1|Q>(75ʧsK5ltv^j[Ty8ia(¤id\3yel#pGsGdo;It>rO3Cd}B5 aDGW>1e;R/=},}/Ppii*TTJC^v0K^t; DnUj aV5$M=9# U3`+=LR!&03k9!pd!fί\7 X?.p6oD}::b/\6unp1Pn+N4Tw*l뼙cį*]ew5#_o=LAe@;1EN\Z8Mʦ)TuS+zl"+jqt̳Qőx;>ݙ3cb?$@]ؽkz:7e&ygNn^6$r1T!D$^-+IN9xJ}[uݫh)-_LKU^A=/!1SR'Q.[OV𻒣pߐu-+THnkz}g !)_x+d%**'JXkwU14z^`P6: cOӼ*|:C ^u;-&~0 QA _@VrF:E:5}呫x^W<\y$Nى}It\j s[ei5ǾGIkP1MN(a'pA%w8i ä^ Qe>{3MO+)D VK brY0^D&Oe<іK"`ՍޢW[ˌ4sT\OXe]~@i搨.]z9|Zv/GUk"sՔIcʰ^zeIHZ$) (kꆿsN?%JkWl(o9Uˮb+xN)ZOdAx>d[_]"[g bhOp[:d̜Gi;!%ݎ]nʁ -*T5 si)ߨ>o_ t[r[9hAdϗϸEDn;}+? /PK6qrAd y672Na" 9q%cQ. 3nfWL䁐uoœ{NzNHR?]d&0]U9fQ>^V7f|G8rٟvh"9ESV:LzP4=/H$Lecm堃&:Qg#O5'<>% s0'|apoeNuZ%}72CG|# AB*6OB8~Y): )MN K ]`x6e}))ZLl–L_} 3hhPNZe{@[ ã>b ?/uϕ >Go5 w!\')p6]2ֽn飣ڍ$[xs,>xyདྷߨ<6'r41L[2P3o>*G G٫sƭvo3)уY ^?>1*4 ͙& N?% !b33p?ҧ hqM/8"<7$˸E ,X~d~jdSCG&$lĄ>Y t~V6voRJɳN^rlT2V\PH״:bNגE&&+4VֶX ~R!y>Rsw")x%)7?T#Q^ejAvhzp}.݈"ngܫXvnxT4 Is1d207X9-44L,E{Q8hB +p遬8 u'qm'ni7D~!r;DhO'!>o0H} e $,u?kENXkd:֡#fM3S3s CGpL_ 4 >{kq/g|xosv0jL'n7Po:)W!Uj,ԉ>wM lV8MO$I gNg1V cZ amCsDGiᢒwkZ5i?`hw8Bͩ?&)\8jI؋?QɹS)}>nDɲ7>,X˰{n.VQ /#ra=ޑ6P?T1;pgF@PRQcZIeJp rx >0(`Mfw‘󯫃Gv m4ʪ>μy /C1Ìo`]qܝoٹ  ! *3 qs`tVh1W3CW!!ODT M<`魚o3pj*+ Gq;Q[q8ȝ ۄWF+2_[X2MΠ1#Gti 0@ԋܲS/72yghW"T!޹f֞62Låh8^FH\h<$ttۏ4֬$R<⾫NFjukCrVZޭL` VkrTh ܂+exg(3rs5jZK.> 5RTK/H/L&(OɥuZ5w׮zSUVkҺv-ʳUZ^jaͫ%W7BXK ,W'^$k~ d֓\@u>fi-6ئ.xe*BѸ6+_$(Ӷ DuwueN;oK콓x^{^&"P.>iGǯv)\\WJЯSi?fj8*ٗ:ocWv2eIS;w=[A(]8ά/^%igM'taS+ ?k,~4Za\d z?zQ^bEK~?ߩ$HND\OupWڹJ[7jjJ6oSk<xc7A%OuV;L:?Ϸ?g*UjrN /d +Gl\?9?"9PFo 3j7Its>K#ϹFk`X.;TT†l ܜM]W!ծo{r^ ?kCJh8ԎiIV>7uʇƴrZ_NwOho:§M]8c'Ǭxoz7G{CQ b%H:-tD)tcƾgkytx;|/C%* >1?cOc5X?VZԧ֒JbY&dg?dy/ǟ˨Q>>ZO,%}-PWJ:::x/uPՑTLVՔQsxԺr4X,( ߪ7"z2q]ەh`fR%~,;CjT~5y4 a6s> {/yݮWljGuZ,\h!gSZ]dU.9mX+O%C#B )_wel696;wpVkD6tN_BֶP5eԂ&Z{ؓMÅn}C43}Kex ھD(>82dP5XES=OgEqQiнK8Oshz2G$, m-vfv.;H,Yc:!;'Gy|v'hLd!$9 \$1+!yvPl if4$ЄwE;Hd5B]88#cHӲeU=!sD=Qმ.pSUaՈRT8H {/:q2a?&8|l6; n9;ݦb**>a5Nlz_V#ZCl7 +JwWH E{TU1m\]ԋ'k)Fh֘r}и :ˤ͠Jzخґ]~z|V!笫6T?-³4 l+5/PO 5,BnHgÍ(՞)"$V>BBWsպu|k3iyo)"fYX;#b {F%["'ԴY|o-u/2Kxk[W6=aPǔcU j&QPB D!Ἣ4$aw\+rzS=utk!b(t< ] ?{*28~'Ww2grh*+GZ"v)6e8 %1Bia[qD;韮п\I~4~b>m "^!^j&O3Fz>͏ow2މ$en_{ DO၌&C {f@4"{W, ZWR'19&1uҕ§.KDk.ư5F~҉^K*ׇUA3L zDzlsMY]"%_G{ Lѷὅu4.|w"`q0B)=6oH -gd9"O)PJByT鵐WsC]L#ꈦ(kv~Je`磃1Է!De"×"'B28[q 2Qc$0<ѕ昛`iƯ(>=$ytI5-rT k-_#aOHN\~u&ߧC2(H‡)o(bDic.IpKj/T,#,=;UAc/|DŽNg/[_D) J+(%#-guʋݱF4o>S~Sw' o0q3{;CNv!æإr􏟳 s|"4=]k@eb,2z܁b<_8ߊ+?\ 3R(jSmuZ֕Oΰ$\wkR/lO3ܘ0`9,*tExcK*Q:<۸V9F g r4MxG7ӾtkLJE%@0+U!7T`"[5Nfr1yfKiN2 "DY'~-X5Op ZL~_qӅ MsZȱ髳,p_ @;ջ%GqXm|<$UƲ;8d>҇pI& 4̕u%oT1Vo8w T9g @WԖlj;-;V& t %)DTGa{c:>n:eZ17cMM/]kj5ʖDf3ʦL‡΅vփGգ󵤾Mq`tM(I#FbfGa7QE p1$Kj 2yZңjG'Y'ho+$w)CR v r̳݅|LU.!/[4ovZx*̶V˜5 ؀%VW{;,>xOػ+`WX*/Pi&/vͣN3E6[|>OAj-ˠyFJE|V(ki.X Y91^,,ymigGȨ̊DGa3DU2At1am:!yLxbydK)=$,ӛ[aNsañ'(OauSC$9ڻ~S M68dۃ E>PYM۬J";pV7nMo5B4j~R0e r^G=)T-;!ݽȥV鲭ӯ"Ov,LnMΓz>j:ؘCYN\Pn=mv:nO^;p%M9G=-SFeJk2H*ZOW`^ ռo^JT7a*@)*EVo!v9A[_ХڅϞ?^%f;NB\-~igvRColCG%Av@/èH/t. FYnpwE:ݶy)P\p[o6k=^:.8; I<̨NQrVؖ^aqA" S2hN( aAfV)TUn]2TzPZ//I%7 מ=JSQ*8 `%;m~lYܗgɲ~/Z-G %S'KJ_SBTcPsTK6ހLN/9 }hw F"١sG/;IFI: δ&J# Ə)k͘ޯҐK> ݤP7eM[Sѯb=5ii!+ V? .|c;%nYp*Czw2Uڳ5m75! ߞ dAC̳]00)XPwⰩXԒI4z2 %z?q+ײKl9>3},/}GfbCEaMY7 3.2N(q*ycm7(8yቊK^NvE"ENKYbYRdNBxQg9RaT1C3= LRƼ_ rG.EulOOCLCdE&6q`(74(5]1BX:GŕI1m26t 4ԠG1tU DzƵ-y󂌤wb"cp5Ύm0dcd4_wLut|TW32J /,Kz+?=󥧗%dl8\vO; .5Y[٠ӹ=xnH yGS8N#W2zpQ^,!;M0ju%,|Zۮ`vwrB]:}ϜN-&GPq%h4k_d"ZOŨ眶ߠ&¬:C)x,ǖ:F,?|JҡcvxsQ 'T{p8/dZ7;]>)ۜKZhcjq>p]MaFeMꭶӸݤD_Qw5s:{/zf#X.};|pp:e2[_nݦO91t ME+ % olkzq0|+;G`ӫUvUF+.&AdptzǬ7+=y՛Y?9ԺԁqzWV,lGMuh\LWRL vS?wӂR?8Y)JǕK;3ETF:9I8|{VZEi%q۱#!+ӳBllփMF6XRWXJ8wjD eGO8*gh MG_)f6Vc8zLݘ&4 ҳ߾+Qj#!U[gT*a 7iXWA/n / 8'x*8;7%"*>^=A*)ΚYvX`r T3=13oSkR+*J5ޢiuգ4<i:{!*ۋ ~7Os1sV s9+@ef$y]I//6]8Zsl\o9M-BOn: 1bN*A , ŔPxJŊg?|G}7 in'n*(ӑрr"2_[1]xQvQ>qG%l!̀[$)r5ӫA8J):Lz N|" k{:fy6Q(4% dpsa*:!M<+vwpjHlB)c K";'^#pL"vӚ N=ȄQUQ[o2hYk㗟NNUuspv6Fi$Htw1""AC3nD۾ܐzʁ6l͜G2F^zo6ܪ'$,Iv.MwzvTdV P-m TtbjaxNgx3lm Ǐ>_:_ ӅSǹ@7vd,ʝf*XZy0 zoT<ƌvdcj؋  NiCN;"AuO1+\ME]K:{S. FN]^dgJ],',R7s0oV֯qל;R:}?#qpc2w\StͶhޔ4w36 Z8'ygZƦa_/:k9ôT/Mws7I\-@|o4ƉTo2aJSkq(x\*'ƓiZPOret»@FweKd~|E0J#TWSu^MnꨬiMRk^jIUAo;&V4NTX; wRhW_N2#j6SI/Et oK1aͩ{L`<@oH-Ae ’,AkJvOF;_Odh(fX U_gމʐP@,REy,d+H\ge4m y'"\ inz*eKD^CUL bzgnW$>}Mg,vӗ[Ϧ ?^Jm9$aa08OO٬Z02`av9GVͳ*Qq,UK,Y5Uӫ dR~ dG%N]u>j壯&ǜL1G_*g xRhiWNDW]+=>JGl^ Ix1P#|2U:t:m3[zQU\h6e6 ].@z Z]J.w@L^^kKgI)s8gCpRqgXj|Bus=MW/Ps傚ܝrUb娯h&n!.PW̡2VKr0HeϤzJ+C@[K7K1'^“oajf-; 96볡ޖ2٧qzED>6HK(d50`%΄*Ej"zMEPjWZLX"31l7xr^%9os}RN̺.n[uʵlG:Z> XŨv$٧7;*R{НtQJoH$8:0N:۞)o P&o GI^iҞ,]5K[?Vq>.T dC86!5Wj2 NT^60:o=0K~<jF4h^P$iTAW.Jv>jP$ee~sbM(4K(mڈo0㊘[!Um/{ ޏJ"u=| Մh)"HgÇ+R?ҏyּo' ;Kg٭>_W없VTGqw >ޱLsy3iũd^[#@_lMd>#I. dF+>r;|dbZetM?$dxGdl Fn~]r <-=*: h$!cci=.;}Z.̡ / Ձsa;_yaBu%޵T:A>y Tޣ[ /Gda#GNV˫5le|F6/\<2c)R|0G4[= Or? MSn$-yG%(8nShE!H)Q4OzJAK?Y8c:ϦVEK0;:_lUOkQHCz=3g~YD0E#q :TL誜ߌ+]p ьr2W @0 }@-Y[}4 5\)rQ^я¦s憴7UP[f R ]0m 9MuE[+~ X"ܐ5Y{@**Ԝr6NJܾi4gBdt]Oo˧m xW/@z27Q̜VZam.z\cDؐ@_q|fvOԂTfdH.^7<?2a\)ye_`GvZ< zbʍ#ޖUl+t$Q.ϗUѓɥ+/E~0/ו[Nv}h.bǃ4}f='H8I`o-9gyChprz֡peyGH"bz L푌_VnW.DsB_>0ijzxI6XcMA7fE)FJg~w8Q>>~>%Ԝо]^cU] <64̤d%iqOm]zR'I1천Ldwp9l7Wu(wndœrٕws8>Bѹ4.&['4+ qOt Io`P[]f+gI(dSfl _*C0~ ^vvYťT`8WE*ğ(߂¨Q^7oШoz=RسBt|^JÀ d{Hp=(zj#*Fi odf+Pf}^p KX>hj  Hx>7-! h+/M,P!xnf`hPO<y+y@r!D!X%z.%'I᫪1&GmjhВEJh [&܅sz+p0lMG ||oMb`oGwoV0zړ"y/ f )ӮˤYFL֪>4 U%ذkg.nxKG:fH*ڂ?(:g'8T% e>z GmT'ژu7+\bw8j}2wnӜꀥ`<;?P|{=r .֛§qϿEX?icrP^"j>T>+8.1'h޸ ˂m6ҿԕ̮S`j DڻoaPG ]:5(m ;\gq5G-̪a*mP8pF͟0Os`Kug#c8Wm, Dž-oؑqgP?iGtJo6$BL]~M$9hBp@'QGT!hGΧJs%,EiqnqlWrzO?f&?%!/º$;]oQ}{ ,Bx]Hv ~IOFZ/rESY@KBY4_¿ q!WOL(^Zk@I?7\o| eS y߆vbn!j*i Œd1$O9cWEt}",x9o酱hF Rcxd|Pq_rjS cU2Fgl8]IźaS9~6S6iD88H,C+oAn&ih.d)JWISd˜įfI Z=Ys5^&m}XV<` ITuUqe)>˾W`,Eoĝ;k2NTv_!qlkxŸ'.\lyvN|}WV*,e9 o}}CzzeXGP|[SmE&M84\/-w&6#N)IˣJ5?Y:P n羛GF>c@?UVlF.1W`w: ?8Z[OrF?+ú4r CGYw3~<՜Uv"k껹Xwh@cz'mWz3Ը?-ƨa~?MO\gT_fU*9wjdi2740+s}4r~?}5&)o3n*SM {XhMYx}'{[G^gh|VP=_Z\M*S=3er @:?3ur2+ le2֫J@Yr V>Xo^Xb"~ʣ mȱ]+>o.dhGe*s]!fUF 8JC;VBF)1Cָڜ3v:JMmZ2s5XwOF,*6g|Ƶl!$aC}%a, ACFn!mj}-1lד?mWSꏕ6kI+7 xmL/oWo$3+v"Lez[e}s( ꑇO)DYTd|zNƴ\jp,fa3ƸQIcлBt[f``̍{eBҎItwC )q^&toaZOuSGbM蓾ݮ$;*F<Ո0CSC5CBAEآ=1vuJ?-S)NxUۡ* {kt糯G7y@'BD02}FtwDg۪* @(+#0hkiw^nGJ|_S$H1%o8Jx)4}Ɵ oF]Ex&l`WECVU7rIl̽'cxj. !%x[YEd pނQ~*=ۜOpOS*w|L73L~AqwOPZvW$)HJ_iy觑ULw߁afD ajiLեHY j*BG @)xܕUԜ!A=Í+\ d R 2qʛOpThuETs)j#DYׇBu7Ls}㞀1G$)r);FRTvw:|ezR"}Lop3/fc7-8,Sy2Tp^scJrwm'3=,Zcz- Uxm7Yu\( [ʂ۸Nokk:79?"Ttū&UC*tν UZrNJ~0E ]r5ZKan6੊B' 8V52bkY-ѺzJkR\%aWK"1;Mi%{M8e9+rZ&hS3esDwqY*?ew2zPH<#;L>nMp]f&ۉjJ^ st=RKݮrkJvݳҩyh}LF)PQ|U󕠷^5 vEve֮/T ݻy @k`ӀlU:ӛO,ӥi Exԗ76+UuͷnVFo$^M+Yk5~̀bKޤ;+w0.䔻7eQDX!-L"Zq^'߲j~Պun[U'v\uלW) XEMvH/բNs iOzYX UW*|qH*qQ_mmVa5 qwGMzQg2+TyV)2Ӳz?UgR-\(Lv\cLwVtC+!)|Gn$p@fYY^e b[%I,.㗄š$)/vuaᙃ҉~|JN7Y 67 EOknH(PC ZV ri1;ZЖfUDdy~;ī2(* FΎsY*ȃy'UcȢzm()o|C&n/!¤;*>#xɻc\<1>袇t7p:3hRRw6Jisv(g/90ywPdS-JGp8EBt˥)ÌPx|y]9a I?3qbrn^B{ 7LS2Sඥ$kgyiK'Z /uc}Ѷ6b+yvEےQSsLӐy~>-י#6hj$#G yCm]nDT/>h5 ԖF[g/ EUw|;ѕclf$c ++QT7K9yOϠ&vln1`kS]%D$#=ZUj|-snwr68b*)g)+ j/GEI/Q=^  0r{o ̼)ny[VV<>I %qJni4sA :t, hxq7%*^?sS%kYMmn.6v(aJ}2wK(k \ اSTRaWM+Y4oZ*w#&+PPښ{ԛ1Cx_>9Q$ݟE1nUX/p\|y&6k 4+*ES)!% J'JOȧ~{&A*~s sfzG8/f38@;ɺ{kiL]<*E|[,B &dJP8¬ǜXɵRȶKb׼ɞu )\b =yfٟܫ_a.*dMfHr݉lguRwRաbȬpݭ/-kL[erT4+~}Z~wRZ%4N_ݔ+֘JVgz7NhrL0sZz@SIpp̈ɝo ֗(:qv~ASq>:( d.JtE LY@p T95ࡃswŮiN!jUX]?>fǿMp J!|!S$ n^_IT'űI?zߵq7o>?<ڎT] =: & ի -HT6S6 Xg i 0AsHn>JE,kT~m+:AJzp@R(p~Zd4V hWY!.'D C㮔!w4g~S054G:AI%G1~x'ɢ PQ?y:'Ju GYEfvtWg}kkfhH`yc9̷?fѡ #@_gLůh9b~OKEEYGAtsaٚ$7F}ov;ι pD@uY`!ӺYN .ݿ wmy <߶sQ:|^ӃRZMm1SwL6ĸ EL`LbkbΨzv r:5(=SIRN!*͚mw&\GCAt \XXr*3&r$n~Z5cZGSAT=yHimOoԻDѷi3GC(>`:u=ŁɨbU1fJTX77 [j &+7vHԆG*2g98" x(5f/V^Ee2>l38eLQ$IZD޾<2xn(M ;~Y4;`JtHmW\lý8:2n*fd4[}6(K aҵ*-񤈨D)4t+1EAi/puW~6C͏9sfrQve2B>gEڑV,Qك$(\uRF LTPehQ)5TW W-ȴnAnZ-֓& a~j[67n2WM nnM@ܰ84^ο >"]((3<"q]CCjmh:ۅҊ I2S7=ʆߨFkzWJXq3i;oGWqD.#ڪ  9h${to:>wp C}u20Lr@7{8$? ͼht5T`Qv`wÁt~U_~NW0.ed9F_p H\'<uf.X7kd[$oӟ䟇OW-\wI~dZIdbikdPiw:s4۪ٓQ F޴_e&h+7z;+Yoh v)_yRA7(h„G!KvŦɽ(Mz_Rɴ۽V g~|uƵ^{AY Ypp=˿qXh֔0>sJ`ؤ8;Mi&~Nb@#4Ҷp?gih^2mA[s 6`mɽ O`3ɞ7]~N5F>mVǢq+Y9 &;`_zI~rq Z-[gp#;T]ʯҲm"I:$I0ʧ 접RK |f[]twsX\Oԧ̽t%>GijU^\'A#% Z3qx/N"=y8Awyִnh$Ğ"ٱ7+K $Rٳ#9EcCh9X@S Zj(ЏSic_eFv<(iA{˥]+gVW[i#V^鞗 TnV*RTd*zTn_/cN$'To X]Td=V5dPβK! ʼcGg X8߽wh 5M_"j%t[ ŠR/o)2$]ܫӾ[͖дcs~š5悔Pu޻oɷimiz^@n>b!UלTiMo,Ed*P56/yp#I&DϺ}-t}><28gO FW&گ]xomnR!=1|mo:77qwҾ#BnRM4r<ۀƁwKk3}?ݞ;~|u ^-`w|FISrp#qіּ ݰ[)VؘRb]`.|{/!]8Z\%XJB`&,ΚF>-z7}d@cvg$Q#\Ͼrilƣ7\]:v+2t_߿An"!= ČnYt ½Y2wqlӋ7@7m?܍"ܶhjESpӂeVGGU`e>@qXC9?0:Խb,V2u 'y2w#Z:*>vʠqLvj Ti z .9ǯv,壺u2e觡?DY b+0l+1"; N7<,^Ý dQ( 6@ϧ$ϓ`śz6'm6?m2# O vn] ju 'jl kwqh\g-X3]Zr$G7w!QTgGphB~ 7 vz6Jwq~&Ώ,FŧqMOwiپ Ӳ߆w[^ PJhcͤ_܆(4ˀ1ݜoIк&X7)۱0^R'󸲎[:&l";65tj1V7˾K41+ff+OmH>0]%cj|{tld]xV6y9g$檤*uy8xr8O!;aJN_{o.0ғ0%* xFO Y[U5<~ qFޫ [|M5ןI7\{Fw3M\|kbHzWF &[/Gњ4t^&,2Th_!҈ ap6Qcc*#5j%"r'UAcu!隟_xZF?@΋1ݠ2ꑶ4'Z2>GΆ[Ad6?4W8xl O%Ų(1Fe d(AG٥)w:,@z^5i=?&£}dƯ@gNs|<sxϧNC')[<u=O_F]G#"[MȍQ?U\<lʵOCd C0_q2u(NkeoS)LHt<]?ThnZIf{tdlI8컃A'Ao|sP;۔w\|-wQʺI~4y0=sh'<&zϴHi(BԚ7gM6:rF""C+B8Ήw+9д鷙25e9 +5F^϶65{KZq k#L{҅iU}<1?q@ ^K%(]'es^g>|cO|ѫJvRM)HT7Z8P IAiC9!#(d>-AX в _I~ Nd}8ɣ my {#Š[r-_w 9m$3p.'\n'Y#2#ep&xUR+&nʄ>0񁐺WLڅA0PC0kUt?:^5N uU2\,ƴ ۟w26/nMHG0 d Ge AV o GNwdrӤqQUy8ױ(QT`5xvP=7}@S=T(FMBP ֹb~g]>,Gf .je;]=C{Bb*p#8O-n=9DAHD*٤\95Hy\}HfƥY<8*pRС0vn8V|l^m_\ pU3)#dI DJpx\;n,D[Y3bA,ʖXmLϾ@o%4Mpy3@L0pyK/Dm.XЬ(q/HO;2|tuH];\ 'UEgwjZT{I"شnbTZ X>jmPu["- #(Bv䍍طZƷkpDiEގ #WivNnAL 37Af-;yy \-L$Pj4oat^D](φkLoFp5yoPp/,{@?,::S;JO,*ozs ڬwBxe&?W-xgFKph#m(f~k`U#$};UؾTũ* &ɇ.+%;٩ 'ܟKuϻ"z$uS%+^xM= b:K,UfgQYݎGcro %o|é2c:9 0 5b: Dg09Ƶ^ z sSTa}zm֟DlһJ盈xZQt:Û_z7aS{'7Qюփ'#))xxt3fήM^y8{ 1Ɏn?l&pzhndK'CdDKDUv8PCtE!՝l\&UQff7IHvkΠX8-T8,PJ%HY1lQV­XLcXO!M+r!x?s!ɚi ,kTpX15j8N<\_  Iq;cS's M7xTneq &sBM 'gՀgOH3;C8Amyø|և|Ym ^n_X6#DSSXr uhzF@" +X-<+ ~Gdb^/6_ܰV4ȴrr\>GX'jEXSArZ+MH▒U16ն8 8'=PRV! hlSe/TT'b -'J^^QN[¾.`J l f lr7m'wT/e|U5$#gCoNvo7 \ZBfB6050'NwCօtf/2ĥ?#C/ɻo2o{s_ݺ,}S(GX>2@K;:GbvQ `F'5][TFJQ*tjPIH2M%.#5APrN)+%d*v(KHa¢<0X&$ 3U&kq1 {@\WG\Lmd4z4 4F%U 5;./xќ}U^CQ.[۷)6TlrX,zE"-]u(F1AcąFo3hBQFdѯJPi˥"̓=¬B^ƾe,u$u,$fǔQ>cA.P|9QԍN:3YmӪ>iQQe{=.dy<,"?].2lch36y(5MZ] %>M(B't O~a|}(ǗM߰+AI#XEu.xp$ d{`ADKHy5Ս(G찆a;/M)п;&] W=u]k+Jz3E(o*(Jbk斩vbLZLO-]ΞS]`TVfOJ}i'\a ؿȘ7Uy(bJ&mW2 &+ҩ5%F>i?OL( ܤ?,f0ޛgRd|}5m?ďr0Ơ{8Hh'`S8Mw"?w$$zzSי>vEV)M(}t ";H D4rs//3^0o{9v@mmqw8=fvAydG6Wٶ:?붬52j?SWp[hJ@%ʩˤpe &Irok;]s .(+MO7>m'dfr4gvR&oNYkg[OTGywT V7ND;t#TZkeTHjb6}'S?, JUz|Wn*ޣE(h$[ xvu >W, Q 측/ n:$uࡓM{S)~Uq-_X7WHQeqy'6q^³WZ=Fpo Ƶg%33Ml9exē]7f|Lp-|̀~o"}=o6ܔMȤRo)_-a ;q>Pb:'(1NPrԜ|%~UI?v 2v##j-i.#/!x [5.뎻ws1'w&`&E4Mv>n!.d;M\= -* pCЇ{Jl'KW{l/p/ї{>]-cyڦ<,:^nWayp ٕ0SRd(]Ok^HS*]-42zy4¥BBYVq7ۛ]ؚ VNoB |hhā!]MjT\27p8想u|%+< $ 9Դvm+EٜN7u"S v媕I%k){*99BԹyQqEA; ㌳{ܺa12Aj,5yb谪iERxroKnDG:Rm6PۨIL/ԗQ( (g%_ٶt 5k,CMՖ`0TG"jN'藗ÊB[j8@PY˦إ"߱ >.7]ȻH6I:(r%:oԠx~ -xxp/@ro• 7u-'a]p!ˑ㢜c."⪄}"24 {#vkTB$Q쟼|bBtQuDUDu[^cu DO˶hwM|d=0g:adهRYVδ cqQc!RfCXE;(Ej-1D`C:C_|ۭxU͑' 5gH3w{e%^ԯ!!b)h]wQb\'}*kcZϘpOTSGK#xsT|*guX$ҧaV7s&>sԟ.{sR?Br0|99Ƃl8M W-hq[wFUwgֈSxȰ^j)nșsⴉ u.y7j[gܶ>1UMu OМ"KN)U9%&ϒHX]A^I򚏣e;O`1-&8&gz [#64bY(nq,嚕Fn%VM XRO)l\}1)?Ӈna18K܏7(z! \}beP!hLq—?2†%NSοU~^JuLG%y@0_}8ݾk?F\HLr֖*ˏ#oWeJ_pȴ5Z6Y { Wŀ0<#:t`,&E RpA^Y=  km[ iidZw$@0#uXG "np|JTv)#&W# eXUo$NF66:톪z6v M|pm Ut? *,8ccWēZ[v.(Lh,T_>dE8>_k rJڤM-)؂v`R{nUGOi~; >\l&H{ #nP-}TP V;7h@w>B 4'/1/_Hî6pMl#4^N>L²_[Pm,ҹa߱. .-{pġ-Gl5| 6Rθ oߝQ͈߬(wQV`׼^EZV,qV} !T9!nYUd>ШAͳc f':Z19/@l6ga[,F[^M`s P"~0QtwQ7ֹrYnL,IEhهjQDo *p9׽OҴ] >C.1[]^-" >˯#>QhHH}b8z5ꥐ}8Z!m?ZIg'|[jsSp,g7eP=>á,2TQSg -׌:q!h+ugBA_Z-ZrsUgb7qs>-ϲ%VY-$W|Khrmi*bZYћL^ Mwt#'(!-eۀb;K9) LUD!R}jTÇgpB<bun dU-B34Hӊ\~!CQU!m Pẍ6U`bor ߶O[#?r9_IJY?IU0x9*}(݈ib0N*=8;"wGH]vJȚeAKJm5Gp u͢GҿM=Y=<42h#NW4jASu0BA:L OGcߐٺ<ۨTx-d/S_ND-ε@ԲF嗒^Xz:"bT/'iUϜP2 MW F{^$v,duM茣e};Fv;Ud"Cc}DytwۻFp|潻U3w] `$נw޽Ti(-ډ }M?_Vq]-(B>τ@r;7p`&t렸wU{C/6`@m>1,fOW~60C9sVկ^ `Kg]X&f y-l0MhHCdQ}AzB@2b0-^'to|36ۤ\vdIZ.QԹ5T .'ʬqM ZMd"хMo,ܓ )-s0O{Q'rԐaz,b(ť_q-g@,cb,N)F+ü΋?Ӕ~iase{uT\̏;*Obl =c麥럣ޭ|A˵rGV,ي伍k?yIpt3dcmW'R͓Gy=5!J_DeYWˏ–D)2{:f,H0VCXp׬Vٸ`zΕU 9U*4^jau[wk#E3<@Gs㙦KT芬9Ϊ?>)rdi$"_`Ks^A֍|zׅ *e yBnx\LP.8qQv4_^?HA?Uüp5Qߕ/teWrf!},MRVʱ bDΫm"ie}(,¼=+hDOfZ4q$ ˯;ak{lx[lh3WWV♭.ᮆ\_8ԁO^BNk꒔δo32yW#:bxMH&X( "2b)1QNgn|w|k({ l͟ ٙOLwTd i¸Jn2vKF { 'tS0=߅hrxS}oLmTQq2n3J m'c1< @5Ziz!\[RmDq.K 3yڒ&P̨j,>R<#Ӽ*9:5\7:f 9቏^x(++ɬ#(Mȹ?uTY .`U4J:drC]* ;rr!qwl-K5 l=l(aCTa'ڋHuQ/ۨz*n=N\niCPQIt\ h<=*V _N:Ut266t'NP6,\(O=r_`?nZ8sAsɳFhھaIM H؁)P/ws gA LD-Kb`;sU{)V7&B5|ڙ=<Ӑ5zjeA?|Qg5YD#~Xл@. 4C2+:,HJ[Ⱥ`Kw.%E]sN*QÒ_݅P@/p%նgkWqB+Wj+2Q}m. V|9*6)k"1E}U/vՆ_巃 Qìv׆ wFAd; ހH{ey(~M/G3ەaWkKpOO>O󩢲]Rv6G۳Jad)zuoWoLo&!5]3Fje+t$ w2zcjTz_s3bՍz~XSjj>S\Uy8׹b TzO Ň3se18A8Z+ ܠ\z"\v~nSz>OaW}@b bR]M( KM&{XUpem=. ;Val#`Ql]\{hYc77xP e1/"7\LCԦ0ey)]2r4[S'kUHj>Q${ȹ.lI՜d"D,˺;P҆B; 8 qw!* _U $<;iLaˮFaYO]dF ^@9B7QCa0EcGܸmo _<<ʌ~ p65k?'(D(@&"xN1fLbOpۻ{vu!d$rTCw*v OFq~0?+Mnx7_#~vn{#م*k OaZH^y2!OW*h/u5#5Gok@N`]}7G*&ް Cx!M4`1|?Gx2mU2Ek͍2KQ8a{ 8*_l漧TNC+q#l  &d):EPQU>)k 6xc*p'M9״OOn56mAn[xL6-8^B<3 a d7^R^Tɞz-@"2flUZ\{!VOhJ fDKmKqEc6o܊d =@֒OXVŋb{6Ll#ˤ+csqGR V$#Q!)`ub~?>NKA>&u[h/ Yz"_)p nup?S_..S6 @>-QS[ZiWf7MS-Sg^_RQ&*@:cEwHG3׌]> N3PA=A `zP *3SZ)k58f 0=#W !amt=^rtk}PӨkadJ'yb&ͰY73ƀQn1IpF)CBٲք Gvۻ1]A>V)tħ9In6AZ(u <ΒdQb$6ae%`<+GM2)=z܃O.ܸѴ"h}t9$\(qyx"K`4…{u|cGR+"X/9Zȹ='םQUV=ӹA-YšoGF!'j]|PVG9qQYz [n%fW0ݐ93ntyh3e"B~:.z^%($4q?=Ӕ.< rҥʍWCg~IP95B ,N/-xڮ>l5-.@ ".]w *>o~뵥/pPR0QyvCqz4J. NV]3! 2`ANN* W4i Q&Ce/|2Y#)xk6=$J.\2y˯ꄌk"ȲӝArTpkם;I"`Z2qiݾ:{ݏUk zX=Jft*0dV|U :a#>x;Q7n N7h)3|yuwnڐvsmP}O3-ӖJRwz(E+v{s9t"/HB[AX *3Q 5Լ-Ԑ֏O':԰a|'Ӏ=4tj_]y)[LHVeRtM< Ӣ׬E]yy :nbcǼϹGqӴʼ0޴؄"{w\W!QaS}qB% IW:;Gχ??AF=))LGuVٟ'X+EhING4@H8lNkӛՄ!ކ O/:Dd;Ըqi'lq}xv7ˆ!9&.NC!7C ]U;فNIO uP(t![)F#l%b`<~-moK# +bh `U?n]`oYY ]f0oA)ԟ\*0?Lg<#Q'w՗|F /0%gYOE|Py Ǣ@P5E=爡'U&2CC?#@tdzl2Gt,Viٽm=p CR"h2G?? GC~ҿ=OK|xo E{"RLE߀P?rm>xo}ͧ&>?;KÒ߽cv0OC?{?2\K%O=k">ewGuU#ާ<G_GP4(֥ⱓf_0ԛF9|(?e׳/ Osf4`]!oE7\]rO{#V4 #.λfA]ܼ9$ތ'M6|lǽRcAl@,/F~L(cqy{>Q |"+r[^Znһ-C&O}:,-sVlCi©Z|(ɮ4Y\!!7Ӡ7XW?w.Q A>T`US(%}."S~é8\ :tvyP1Ѻ"w ]zvN9ɂ Lw橞ޮ2vq.e,0fy}=?bd6F ,E(eJb;wf9p8UΙ3'<ҲflmHy>EZ1|3zrqH>yPeD]2 1}n1&)ò~{跷ōg zX=gs=p \r]h67E5$u<Zq9#nD;ݱEhئcl~^=ǙCW,\%ßڠe tA1wU78d þ E}^8g}r%%)]Ȥ )8 d`}k+,g֙kw+ۥj j>n\`rwSXyPz!18Eg5(n`Cȁ;y!kl"Sxή B_#j&Y$ߡ5vz5,|c`5p{\:!zOiJmڡn/#L8}鵇’AH<{a65plRXR1BXqNԛwxpj_V흮V.g=mybrZ'UJAӉz-X$ t(7"|h:QWC:mȨdiy5F_ 9Cէ,9~[I@e8gFqV?)!k . \aKר;y"θq?~gC-mģn.Րa&'Z6ڑ?JEZ>o*hP?<aCeDUa;ep߇cwuxz8W=ɗL9+_qھkpp37@R04&bdO~\`f_07rН.ysgPa)PXס-SVu.X+L+-1#w% ؂ďHq`{sn5C;B^uOw7Ћw>ާ>GOS 5!mGnlq\+KG<{igk.D> j/h5Lda r_:QH|Z};֧ޑx! 4 EeM[. l^y{-T9zw@aPRa.t("3\e'C%j4Kޚ6ml3n>c!L9f3?b Tgz_ tAEARĐC;J0*[FmR5*7;2O <}.[OHC&֦ ngdlǀ`c5orlB\/gvB;`mof&ǓIOnTM+2hS? `d.kѻ'ݮ0n i+ڜ3=m !v#W2 peK5H).EnӖr[}khbR(G:70c/"I^US837Gwd!S[Y!j$M wr6SY#~b_>ZNzNLJi)Q~xnG`3K}ςIyw/' i)OdW!D| Y0Lp>V-iT>Z9n-rF$8?iDbBZZn3%~n!H,-W-dQ :iU YCd%.N,r<)lEMd9{~Ήg|( B۟|ރZ qWt,jճttҿ~ɸ.|xuCϣXǖ:`V-C)BhRRL3^Y<λȹ`6p8# գAAQ$A)[ 1pQ]ᥡƖS~CtQ]aݏ҄ƵJMg 1CV@1X,p_ Ab^;9/[j[?+OfJ|u+l c{FgP&v֬Shݩ2ă|[pFT /obJ+;o'Mmpu28A샩vzU2`gչss~e!a N %jK.o{R,Tv$ jZ>i;!(acGRf?ul!WwI/&/~C]1WP Am( .}0Tږ 1Crڎ cF^:D_cDYGx0A]0.CV]rpuYQ,V(H_87㦒u:A:Z-Y"g <bՋq&U,.,/þZ⺆՗u6~H #„Sjmŀ{PY){??.=rL -֨T]=FJMqݰm|e^꟎ Do(ЮtbU:nF8 Mg0*;k#s] uV6^̎c޸[SŘ8c~ܘW%TshhbSQqq,pZqaePŞ:zᛓEJdO`nI<3aF)|u^g*t*#ewT֓Ks/r8=ejG,QKW՝ b{eMezUdڌRRVNG_/9cv~[ugxacm ]zfE Bh Ɣ| M)ց,̌y;z턌3 pBJ} E"^ ,V`35GH̢5Ð~|tx:N>Of++izeG)S?AduϟE?Þdok؍}e2/BWKΧRxϦ9 SI؇kCt0)=qs;VT6_GT9[9﹫ڧTc%U3;]m 7) 3MZ]|V5mFm0XgYCZMz(!&({LjZMS|tHJYF]AB;nbs2E)M@>Vƃuz[q}$M 6(nˉI@Z׭MKۥisul%3I`UҶviscZaxLthRik<]wu]G5e-l:4mJ1%y(GH BC]^7NCaA+Pu |)Zi-a{p4 %iAf+.Mԧ<}jʫ`m+𗸌K=#8V- UDE/Sm q4NW,/@ѧɛI?HWYMHB cG7NNoH܍ /տ\ s^OL"]fj/FwC)>cz?4H^嬵>!:8G/K>KVBga",Ta3tBH;4 G^vI`˪ 9yBLJwl+BpV:%'* c}IH"=I3s_tѻձ*NsQu*B0ԗ蕻xhq'0jǓBp=$ ,}7߹tc)Џ]NIMHU,-f~,]_zpɌ1}tjwi>tx2 a &=>xЙM$C?芀޼ˁ@#pkpqtnQg}ᥛ.7BMHly:m`Eڢ( yUx|N<⦐/O/Ya;?~Pu퍖ctar9$^<@`r2pثv8>huz]l3?;~(?]&o@q8Ul̠!DIxmjMV#Lg&-XuֻS3blTÕykƄMCRgv ?uǵŶX쪰u^.t[*B{A]\F:.obۅ/TX*BדژF'1RroՏcVc ر ΦK6&?hhM=dYI+HS'h tMaWOe]$01Ck}?߮;9R UtRMxSe[煅9$B)8C٪YwLH!I) Dz.hUD]y0矍-<]*kȠO7n39Woa|Mݲ ZΘ 4l=SեP>Q]:~_TCy66X[1ߠEWUԎ#XJ: JLI[&u;WKu0^z@ )vn,"\Q9YfRhX;dd><׍ q/m[kDK::| K]<7*0Imxv]Ƞy`wvb~թuVfLhwtv!N[!6QW.\ߦ+Us;V.]0*DѰjbV7:n !oH5͋^1>C!uj.TF' VySyAx7qQ{Rź ]j.\PKB>W$ǙؼH9O}O*A|yq顇>O5YOU\ivf5b ަIaGfM{c%$JY|k[*N&S$ENO\6Oe KZRen9ϻMR=4s<*:DQԜ+_l C.0'1Ib/Ȓ |ڔ<Xjl_Ma3]d ;xaQKorM-B_42UP;\UȦ9W,@fi? 3[*j:yQTnWj{P"$PjvUrQ!rx 9uUY>X H-ɧyzdP<$vԏ1;mth͟6t P <2b:4m co+ΗGIW4D;_̼v$ͳ.i[dP%'a(beD+.ly3f~/W~)u=&تt`OEoIPO=`pMsCF1} zUy̢񅕵Qq k iF6 녰d37.hj.}6+:?8\]gܹ0UtYk:͙}QbƣYnj> 1 >{?O|}:q ~M&dشFI_f!ap7}UM*,SqByՠήnZ0|2 N)B3?ft3'&+#ohu(,=lQ2F&mGoh: p,3$ݖ1a(CNLZPK!=0ELj)c t]er|yBoi|7Iq{QM4ˋ7O"s[Q:x[⷇sǺZj\y} /6^]*g: G#Xt{Az= :5#lhQr1Ia^zOvq bd?P';/E%gc];>˝Ȓ]qҚZěeCmQuUp@pDͷb]9IfWkw2?k,F 7W.3d(V lw$i1<9pogka݌ˑ*>TTQgzjUt}W(Aj3IOyN6r) bƈ]m;1]#nC$#ds~ /34=ZRI5)ҌN Fm$.* YVY]OLr1_ڢXUtJaq z}|>;UE],Hmg͢)OҤ>?ټz kweF4 C"tVHXjulMOn\[ffilv;U6QMs0xr4Yx5enY.Qt7/eSJ[N'u#1`Vw^aQI\b|N߿O)t֛ RO?S7_$w" 'AW_`0_?GѬ7*] f#}xc 5;']&AKO䃻A 1y>қ45oޛͤ~>PMY ik|`C^ a15??Ӧ"m27Z7 Mfp_aȁI_*G ?li tZmx0_3\[Y{5?#2fCX3P^ ͺc.B `s͑Ica\d4B˙0Q6)غ<ք]zܹvԶdLHWcFn+$"0%:(NBT ħoS E!n 1Ms'/YLgLr'h13*e?Ϸ5Z ~DP(o)sEdnKٮ/'E"FLy'2}R !#:36!.eNY* zDIm0Mg6Y  ZU7X-ŗdPY=;pAFI| jDzUqy`zQ./Trz2x_>u}RF]s]Bv#gq2t'q.Q+ȷIt[N2`fKЄF.CT(fKBWb$P݈OŞR0W1_AHx:o?Qi(!Q#>&W-tw#j rH[u3K㏗mm#v-xf ـFhUHNjR!EQ MZmʞݍ@djzH:"Hts%ժT!D9UhvR 0PYa5ׁ_^[Y&F>é{U5PgLHpx4͍R&kZ鱙.3u![*K5RJ~q%uiyW?@^$ϳ0DM7r}S|EeιQi#hbk|Z]]zG!ŵ $:mp4Ȭv=Bg$.]FTahӇW3iU:߆V/7\EԉLT,߬&U8]F7{|yt:Gfj@8!B\ #_‡)q; "8R.)BT\؟ yƫY((b+ tQtam/LW3M4o/ Իz7ɑsXΜ+ e$8-$NRG5F% 89z\I5c\DE f%3V=(n_<^|bw?e" f-l1պS% })z6"+#FA{蔹U/&2}*`4LNgl+S}G wTN"ɘý][t̆:a<؈[3w̒rg| =SSJe%:" N i/dR\Xy2~8lB}QX0(dk4An cjc3yk9ڬD g,φ1rin7p7۞I@F䷄o8'5#]2Ly]Hn|*34)JtDvJt.pScQy3,b(5xf]'@Ϫ| &diae5͉LYd,>ku*s%  ^:9ZxёeSB<@גL%)@HJo1¾̋o壧2UyRl"Wnw:!S-F"aw⒂vK;6%%r¦Q)yi,iaG H|}\uٻu#HpԦbR sC˭HHzI/ (3#;Cɢp22rNO !T,@>_F&lws;3L A'9~<YhK| Ep'@Wm[yotM .zk.yaUzLjSa' cׁQLȖO552ߎ)j%׆HON:dYmco?ɏ >'[o8N`~:ٵ :[b9yc^e0'U,~g|.8Bn;R ;ܸk|5ܪdccp*J+ü-HFPu 1@v\K;ج1typ5\FjYk5c3^Ϯ3)<! }JANQ s_j Ýt! :em R, 3=- 6P" i jv稾x9ᖌrK s T_J|kDopwRE1r^IγN=qKS/ ^1qc4MIǝ 13h% Cun-*p~6-Ea> tOI$?GW?cKlŔpcԳ/l6t98oav,y2qZa{wwQXLX||b#KhI37LzO8_Op>6 ­{1_?J]"BnAx5臽gz ^EkY։c\!TSPʻϭf//5\FA)k X,C`nny }W"Y^0wie| U.{l'S=է^,@23_!]) In7>1Zx;mJyD#w&@RI eT^u ϝ46f5`(rO@-!N၌7y~6Ϡ^ipOx8n2,]ޙ+Jraw&wWO,7B{"+ZAA}Z#F=!ڞ{#%V׽*BUIJeFΫ2cSWVBiL躁Fer>d{:㘷$RF٘!Uˀ ?0'qtPMmvCd%4^}:iY— 6&zE^2F*8I2 ~n҂+ (7hB6\S |(lT\(˘G w{}.T`.%<y/\qr)}vɉ)D_U&T«wp?7`}O*{WoB|knrQu6$dYty5dO"{͔5{F-CrRszZV "nYZ0N ],]pU,.@^WMpl¿cɦG;(azu'Htntr<#LޏnlXF:/S88-arg iq(u{P`UL|yv5و}'x㧽-+%VBX] o\a$)*k3m%ٰºCt1Rx)V"wT@/jb0rx,%,abȾwBs'h{pq|7'/iJ pn <#Õ_# ԯ~ࡉc7Lk!KeApOf^|3g"og{KLIB;䨸!NGϥ[iV2rcȟΞܽX2 H_0qft6Ztal5"N%pn `!pu\8v6n;q3ݵclS~,zKmò=y 0!>A_p"/KyImd@i-ʲm7Y]3؅1PQI \)y㼮RڿvbtA AqG >6ٙt6ZOd@ITS}f[l˴Uu+H.ΘNﲗt\ZiLikt|82j^R7{s\<<4l>2z QSp=4/Kz1#hiچ3+u~1+;bIa?d|d7 Biht k3޲qcƋ^) + gP"V^zག[ Ͱi0ˊIiZH6/:b;o)FDGMK%Јp=!cz7Μw"Ҵ{uɦWnzDYsVޤq-6(S5I9Jp73Ylv=o|~Ջss6~Ǩkp*R-*9jv; t#*_Q_.R}{ծWnr}Kh<(eI\:H2QՇ*v75K,l**>Uyy S=C/~ջfNG\tyE|~k+V)[]/K^ J-~՗{SyyEw]=m+mxvIv?5^l΁_I#=zhm9_&Dy_*'Ouv>F*d~'m/x&Wd]y ǑKX"\хgR2Lm&Mʮ֙&yktm_~n0_r7:pY"'oMKZDof֤M:ڜ)&)kZ^.0ŷ&^ϴLtΥi̖NC QQ4SLs`dJ0A܉}7iW+Э,#dtl9,rEmFtAь)[Kd^hk[KC:s\OzgePj\N3 [,S\!C%{V׋kOi)I؍~8Ëo8pXzUnTto,\1Ir(;c/DjϷ@ՂGbiT?**QxyjFCJ ^apv=kK[GT8L%};6X;f&@KmJG+XɃliKP1*YhϦ&^ǚMBnunUMW&Y.ԤJ&v Iށ[/رUK5#MɯF"]o:_c0ïsőQa7䨮f@u1oS8՘{~ODi001|47[<nyN'͸qsu^acJ 4E휑B / c2^AR4qiѢ-D%?6uV祹ln3MU<5jVp3#UeRTHx$|-ui_Fi.J]w?qeL[ pIǥ(L I&$ JT׋fdžb}g=xP<`Vewܢ(&9kһ8"p)7Φ"|Q$\aTN W/.;hOдosҍ}͝5rMђ`FDXͧ\m[bsh1$3 [e$ z$=|&>`gԇ&@}gYajD>#q7vƓp X8G,ׇ,mKa5'^SXޙ#|CeOl-Q}V@޿" kDtNj4#oo jsT='^s']d;|u{4EՉSX#`8w= DD^|,BZSr3l'kl@Z˜CFKbs+RJjT}K:?,2 )us4FLO±mؤ :aK{ßoCd,kB.0 ( Zlݳ6N[{;5jpZ~Uؾ| MSnEF^!`!.sM~0B8OZoU|arm6fMNytAmΈ{~'Xvl P쪲E9h~U*XK BdUِq/JNq&"TBmdA  b/Λ3 9 &Sڜ2n$ $>r;9B  W7FMC,95[a-7YND7X-Z`UAhw;<×Y-ut_=t!(ZTF渁!_XԔgLc 0 IPv3ұ(<+4P2\)U 5{,1J=F 4sA=\5-|E#}V{"8<뷤5}96Aq n5߭=NU(6e`k{y'/,+ӂG>URcᲦ)Ou8 cFzs3p֍K`U*4A<B)9& PRgNyܻ;/tf%$HEq rqFdzļU]M\ R ؋J72BK>Շ.dtsj]S<*q O6SP`PbH462":ٖcY(?y(Ŷ(qA$;4z=;4!n=cʒ"·2 !z!9 |xH#EQU2Daݞ5߲wAA0ǚ.ᢄhkz~N2O(䙣 w1D:yu WE Per \d1HyX]% aA,*oD/kQDUckkkTR/-(icۿl#lZ&M.;Y{Y&_9r=a1txlD2qHʼny+*]4ћtG1|zN_#oUym=򞠁g%i ۍk+&\rD'L}m@$-OMlQ&ϼDBp\9IRt%]6x7p"dVpͼڋ7+'wT_5=&5/ _:[b'F;2V#nGWq]{ M[,a& br9.=Ӈ|$vot1$w[x\6Rfޡ/[JEcVɹ WUtDGe " '}*wgd rbl9e1M J۸Fj[.UhwxO?z\ߖ Go(fU?C狨.Q _ XjeMgmj}oKjV4CAd ۦf 1F.3/uU5>R 㳡Dk$w|Ql}bNp0ltyPG.AT[p;'N+!B; &n{ֽl2s|as:"ep;ѡNLxh<9:`F"oA9q8BV<%1$*ib@\H)a{T։*jE)ۆ!00@ ֙c}F]; ϐ!?j19p-enVG雃`g tp tΒr-M3 9!wJu”nuذN9DIå*Ȗ @9D&al!o0_D n Mx 9@Rңqp]QƝ.yʪmktm TiUZXٵjK <+OI;AlGoZAtQC'r"eJ>v2S^+X.; sOsvu83tkc3;W<-[_E/nT>WljE 3"ԂL3){ȒleeppilHK4 ٲ, rc[P Wp`5x@}0|\SσMO/gpS1:2RqIX8[[ .*Hq؄FDjz2p]B޹?9f w =?l 3м)L$UuMu8qY=BZG\ Y-wrzyv"(<.-6j4LkSEqJ,OtTAa(5RDF B.ؤW .`MW t35;gY@(;f? YXځ4TTL+ i\ˊ;]|g37ưǺz\@)"08o{U\LPpksԥ{B8Y.0_-q CDj Du߰˹s@VD,;‡)rZ- ~B rL,w=ާ v;>tܞieG,|2C%|/-TiUDv2ON$ QK ݬ̑`o]'$`;zW <GvLiW S vy{<+ywրP+eqQ_:;:f()̋J"s勳W?`EQ~3&VbJdg.-jdKGCļ-+,лsadD)GPN}V@ȷ$hz؛؋ZCwCDUљ޶4]N"-@nDGmg+]$u㑆џs:qaM .&=υ|.=/񜨶 CޤG_G#Ln&}_CkG*>`lһzx8?˯fp՛E׮Va4s8N`tٸ o[3]Lmd|bPU$VXN\k[d/>sߓ7Ub #{X#UX0GH>?ty-?6渕*門'3{Ai  %òR֝`OܦsmJa:GpxH߯;.jNwW9Jsu? Ԛ .Y~-eUaa j_<9`~]87p{f7x:fO~m]1Bq=BЛ;% vP&HVvwlN8p_v;ɯƷwPߎa rj/3 ӟӫphH=䷟kFv0l9FYp<<A~733u% 2$9NaO\'NrMMxN/ DSG4hTZ>bWNOw NSШ?Mw 4;نnv $p_ڕ>Yf_8xL 5RESwFAk_Mh/>߃pd OCE\DJI槔r o:˸@`x=j}.=s(wҐ2"ˣ>C*o禎!leY#_-u?"U*ŝĶIb,J+RWJuI:Ԛ7ד" J;h ɮT;ь|\/!Ѥ-}>[9`\F )l^o`"Q ƒvx O,Ήtw`yž>BécJT$օz8݂ETd]O-\zVG} hۂ|*7eZ8p"]UWeA(7ŀܫ0r$3Ȑ'5#ě =HQ{# 46ZJeՏ^U'ٙ({4Vb9KUL@WՏKԪneDysG&6S zp-^@X@W;&%Yc 1ǣl}osޜnq$4/B_޿AdL &K;)d:>ȩ/hH Vky+|ϝB Tv;1DU8Ζ;zt"!|\ժcjm#BTG)S"X!UJ{ NKKu4;&MZV&ؚـ>AkյoWoWh<&cs_7R9:J7U]Y\Dk ݰ32i*5qC Be`޴w T<b0׻k(PePX j%VN~'_T+jWއ,Ɏ{Nt:YGXĂTܚcꆺR4.aW{H"߶_zj !|zC[#FW t8;>9 >袄jxg_]~˺.cUn5m+at BsӧqJ 4%N= Tkk.Ŵ#|&,=OާaiE_*$ \/ɍm]E)w9;™H$jPbHxw> xj᢯vhg#1eJ}Ey{2+{$i(Ct'&&idcdmCQPT]`h9r_^tψ\sLB0Efejbu6@۽"e $ͽ9GKB=E-(T; ֢0 J~8bYꢼ n1z0$(ҋt$^)CŗI.U :EEvpI罬z AgTQ伎w^烶4\k\ܴ0ہ~pϿ9J8jzМ ݸX~+j >4R]aD9 (h5D4lQ@A1q4 2' 6W-;V؞r*^/G6EfIA5ߕ[JlkvpC?]T:_fhw2cVCo{?u1g$(*]y-SM.mm! r |߆`9{g[ڲӫ{ 5ܘ8CWo櫳ok>fIĵNc:1TMu/K;=f kv:Kj낯'p]{%pf]bp,+,gT8CO ҭ D`T(;Y` [`t&fVdgV2v%jjC /ki,k;gC6.z;z<Ep!HzNkP UeFNHp/ w:3XZ~`0*[%,*,H]|-s2ǻܑۿXpi&ڵu@cm{Եb}p=Aн;LxBBu|,o d|ʜ 9"#:r_)J 8R~(`V#P'!23155A=ă[hRg5acW #%"ݳKH'hs2<ʹB88Lj %S<]̞@ aIVpf -pg{$QئGc `!rjΉB/ٛuq4Y>nO#HZ$@lKd؄Hɿ~ވȭ@#Y/|*'G˰.š`tnc,һMԚhi/ۈbGm_hToG1O?%I\wp ݚe@ٮ?5ѝ)n'm7r4Fewb4; $@X#;{C ӯ|jB7\-+0VM td1aj^&1}i(xprqu"Zi;zbܶ=)UP ]FfT9SR2tVՏ⸢KVmUe}1Hy+Y!I6V(kdq1ڡeݶde= VKӋ9 ESrD&'#*C_@ .>%*z&cQs@ $Xk0J쯲A%׸ɽ}.Xᲆ[QUR M A B&9lEMu +H :XQzΐWX@ט 3'*Y~<-N:Oy %B`r!vmL4>T[y~<AK[.78Ī!UO i'i분2$D>qȌR0YJzO|!kBS88vj-y\h݁}23Q+}R[+!~a5E#SsXK @Ixg 7Z\jXy̵y`lD Rduą @#YIdXңw׎S<;'.-i\fcpJνG>F^/YN드*lQ9`zA;G%x187Wy+=֋[}:CNлUHM -n_I3r[9U==InpΒ+ 9D OM KuՔ%5+MbP2(gsOjNZϞVQ&#q.9id an+q.NA%6%U쨼S~xܕnQJYlr0;&-WOul eASۅ7@5SSyeWW`R+U . kxuƣr.P[n"K ;(=**1"S?:AfsCy4>ich=oHѪ?4L'N&RΑ;Mm693SɈF1zK8FF ;YFq0mSsU% ގriWЍP 9sbhH-Ɵ]`YoGfs\+ H ic$t79Wor>,ۆdp̆b6 !~16캇\pj]ìc:TpjJè{ )' bFcY_Y%]q)< "Hc!(L[Z[!ubP]$ur!jԋX=k~9xs_C=>+z%/r9s~)DQ-Qʽ끚6>XHijɆ R̄{" hr9FZ EOq.xr[3ob=\oZ.|75.TgcCZn B_@]f92)^rޞW֖;5xRÅ4*Qua خmZ$sΞY^{Uyn0bd =#W L|4YqLM-0&j(T{PT+abдl8+lMb/ɨ)iI\_If/őH367o}b6sR ̕d`JLvhD2PmƐ_i@uk~S."ӀK ]̵a9Z1Q!1K|OEasrupp6Y<)sYɾwݤ~a[4HǾk-+/)s/RLJJ^]/;X@u2Z$`(͏/kJB"v,=oF{, PMT}Ϩy㝞i*"*N*|F|Ub@\ͦSB]W`LJX|Q~Gg:.|$pWI?jL-j3j/U)!$-4VLB?H kZ 8հ05r{8du,c2K$Dlt׏Xg-H}vߟ,|3\u3"pB5k5bzZn]v3c ?~!Xx4Fh!k$7;Ө$w(z5z˨\x(N0S2iG?'^ha)l3cL k-KO{M'++%BT'q׫:B8WB`wyNyY<:/!yȍdrGwê oϸRﲳpR/Kld ʎVj2`-.d;*H5l.V nQ~FvV;ܧ4WADBe[!#Ӛ@0UPǷYc@:B8J{ϧmcL,f")F1zۺb*0+URƨRNqo:`>~+F8^P4 c R{v/ Ң!I 4c0%+t2"euX%Z2 }A^lOCVTS8 naF32SqVeKiH{ B$R_:@\t_4{eP$ nʇ)ǃL0{/j1dML񗱖9v [1r5˴!v0a=E*`8ХuNAnJ򺟀5nU>QBړQS\GtdeGɝ0tvCK}۔9ϓBϘҗGM,}ϻZ8OjGfYmSX!vG3΢ϽPCЄw4TҋebitxMh0r&̦ i[=!Hs87[6'xZP1 (mqwT ylN\Nݎ[zgǾbXm܆^C&K^:cdמю- 1$}酚xJ:tKNÇg'i!Xm%s\cU6;q D&*ܡRó)H%[J@Neχjn\6ҽ<ɢ5H&A=[E>w+FIWoغ{:,~ޤJrHpHVtZyZU5q~vt#b3M55ҥhv=CGs?ޙro;!W|`D_ƙv|BI `g^Gs7&@ɰ^ϖ+nPʺ(6_}rZak\[UOї %¬Bp[LL"gm@l^?H;Rik!A[0 yn& B|wǡ|7 80q-Y¾z:lDBӐ<ܦ w,bò*t.&N?\\ d,W#4r%Kb WKøn@} D-+%ED ɽ0{rfu{ 2 SJNP)(ڞx<(=|]TZD x_(ɹ˃&s-)[ S8*"L%ByM2JKP(hYͩNLW:5މiIX VQ}&uIijR#GgA[ei(}AI*_B$0ǷXo3T>\[`v5^d4NO3W#FrIu 7[noa-ī;ڔO1jvJQ/mQ 3 5G<ߊ>'C5T. cu+ӢetN7T4[I pHfroMvVwж@k%yQsQk[0?Rj\ZG-Ko"X[vt-z%ئλz^_ֽonVݵLGcIoiQϝ.8zu("1p)M>RdA ZۥHg#n]VQ^k*X$^RWhdyd([^k^j.kbP!CHYd"b-&"g}bbB"TY]3%`1MݔWwԯ@_F"d/鿩8ʏ426A-V`\Ͷ Jɪ͔D Z\JHKBK1}Pʻ~$PxCҵ5a Db4< -]úM\p%c:ULpť6r帴\ 5 ċF94i]6@]-Z2hʔFy2|O*1L7S.?*¤0q%kv`q4ɤzB^:UHca-){-e5Eb0%0͑)B+ euθ˸Dsi W@5;?aHZ{~WI'-7c 8h܊+I1|>Zs\( NI0!2QЉ-4"]u%Z Ѥ,(ctG045" H1pkLZwc->p# izoPB̡Io`HNKmMiǴNLoY@1kY1s'W<uq&m܎*  -=އS[A xJ۪nU`L!Ra띾O`VBI !A7lɌb}*v@z>.E8uBogծ̜}lVq':; @v3lVҵe|$0LΥ]" uXi}_{l fFS7@lrmV"|Znũ1"Ti.Iб ɀHNE8%XZHR*`IBV:Gftr7^RKm3VM/.",a2Rƻ*^U\오xm e}el5Q6B1s:td2뗶:-+i&+XT!|=VW yg& (0VϠE_J{O-Jrʡd3kx{|x0[ DS0 U1r7٤Zsh0O8$j ńS;_>!tidcM^rNӬeǐbҳ#*" qiʌ?աI41ꕙ1ԓl@*@fA aqbȏ?HHD;,MZI4i$=IisE5e2wĜ{3:t#Ęp mDް=N->/1~4vbD=a;@fj)WNR[; fmyVBsRs"(s V7 ߌG <&gܿHaRRI͗~9sdp4Fgz.C_vZ;Z^zU'1 ux^۟X|6PƍX ;A4CG#Ux)QD:,w6G]":Ȭ$?܀b-G1;?4 ف/IhxuJj裳* M.pG>[t(mEΉрMv*-o6x=qgYo="vI:"QK]":Wh}s_0ktDy8­USw`n({ݨY"Sd5kh Z9<*97EqY$sOͼqy)ea+T5XivٓՆ*5KĢo2ٶܞb haq:8ӼӳV}PMÂr8]YW!whH%IV*,C-=; UOJGObjXICZnDn :HTXEАp%T7c`~mq8X5 x.$b4RQ3-{?$4Pp1}U~#a0\JW㍚zg,zjAYOkw+ʮ6"%A1W7І{\JKR5 t/Hi]Ԛ(oB1D{Ӵw']uE8r+ZPb΢wLJ׿LJ8?L`7}XMaP£Tt7ԏ.GćЇyMO!~O>d⿦څI  ooʫ9e_ïr?b ph"sw7 yE zm- OyO2ͫ9\lSh lP $ FE=4.xtBx ;j0Zj ن?l}V$.kr|ݍMtxs,7'QDw7Av߿II1džn%s/2&^_vH_^^͊9A[aU!-TвN ljg/EuRr](K9n|$E[bE;I .ٻ c!CCzO8b[+ ՗*X=f/-~Pl<OUy 3D+ԀaU{D^ G;u 9ywby5~މfjAxS [ۼWR"B.xZȦSa81V(¡ʁEIm{#X5LlZT@8(&:fKĉ~}DdJwIC S7y_oA).^Ő1 ˾7ܸ^g4[԰.@5:H=h2"G,HpeX8J"%snڱ7e]WG8TJ?9N24UB*Ǹ$G{] QgEԘ%Lu?㰿CXgHv!hffTI{HNݺzփ;$9z'1b3W]2J_Uu\ K!+qp+ Z<#+VVK1jrAoT.ԏmaʔgZv= &QMlq"{^#>\5I㧽IEM!6^Ly-/W"]CQTSN5߸p,)%>s*(@#yzQ5LAhosW%o1\6i[%1@_G Yߪsy$kR%bj\-2!h,ݴ2pڤjmAAwmPJgn8DwC[9DWY;J<ۄ=Jz$ pkok檛 }-Crt#vj'u{.?JBNF/(t|t|9uhHSjSG&0 SD]mݴbx3}P.*j\cݝk Xa5a >'i >mq6 ڤ3~hBZ2m3=9ȟ %~/.r:]QwO]ݳ41kg8s"wfx8)%M*f2M4{GFh4Ǽ(n2)[cwn#D|.bJrW#w[(mVvٹhonpt3[X'Dy;p*9lVS/X/}y!pۻ~?~*ĈTp)?Dj_kU`֐nY񍠐6&1b K~*JҰt=UV!g;}*Yޕ4.SIMZ*:ڿv6m Qvs+C{L%O$nzaʑ p}Kp,3kZOƜo]:דXY۲׶D,g l)F Ip(QCq#T 1+5jݺJ BQ<BgGyu"MHp.DczA)0U2MݾdhG`&$ ‹ieܟ\rOO(<-6ϡڍ'J+ǒ1g4GR[^ek.@"XM31[և﷕<3 {Ht%)1?j9D"UZ<T 6yp Nb.Z۴ > *-`mZY@X`8/MZ<2Ptk aTZPi]4vL : ieMiwzvAVcLD HZ )F,DQgɰ]b&FX<`>̺Gh ٯŇj#g TD 54cPN b/8$1ZV J n`$7^ŭc2䢕4PQb|Z&LKi4%9 'y"iJI(zOdGb&E y\Ӂ _BeلkpTI&Pry.wGwgw=|]<7=XQg9y38yD.Vn]W? s3 z"@n+-Ϻ1N;}7q[a,Dn=q zX-.8?U +{gȋӘ9P^ >',V <{6`-@ Agxtz|śO5 i'3~y^h$k8;]t4^T?8rts0n4XzH~ LH\u8FIptM}rQE<╼F%(-Ȣ,k\aOءad_dE!f?І[Ѵب)-- Vr<˞$\mWnT^r@^l Oq ҙFĠ7c$yXk0^[{6͑8lXh Vʣ8$Ӈ+x/sWqP8JB-+jbďf z/sx>Ӟ)2œ!^E_R}6 &h׍<(Krz)“ :n2#|j$!IO-Ww|;1(Xj#דnJ*w~/qU2oF""AP]AN}<3}EhS }9P2;4D\o8gx-&=3%w%%ѴZO>_%BbBbIMdA|W-b:Ҍ[Pt&##ݤOt$m^ڇ}YŴ|HLKۻ`OĴuKڌ%DߖV1-i"ܖt%-CdoIa[f-3e̓vT\\ %0Nc<%@pj;?N\;ߞ^u+-s4`[Ba%mu q`R%x~ѮvaϺHhDpqm=a*xŸ@+{n3x {4~}sQJKj|R\\%_ %#/S%#ܯ 3f8ݚN:Y.[a.}.;*EC ՜uMSw΍M7{c !@3ԫA. Z׵ w3|Obr)! S#nmqj4ZSopjr>I0@?pH "4t ~ojDa2|6\P?d_J?&N`8z{w]?6wtǕ?-C( oPL]* x#uqL5ؗ{ߕOIN N7_zCc52Ml'NO•xD=NwϢ 7l{dJjk )6.ZY "\T73i8J63 v15Zf&V3P*R&2&mFb.Rdo/ jg$M!t_2--9x+Iܢu3"nnwjA\IbNzD +j&u.ʁZD3gXN#9FtLX6k W9XP澺BSgZTh`gDesdvڴ &*kD$.Gdڋb# " l4X?>HAC[*VKcFȨ:d8(N9T{2h,y0=NlEkB 1 -_0ss%JPdF܍pz-0+t9 [cJov玆0vN0*{h8ӗ4?va C)cԚif3;*ڇFh3*V6XyX5 Des|l7q4"d%n;VO?")3|H` Ē~}qIb ,b%\fOa%i`A&_s/ ˿{oiDaqoVPwz(naP,-\MW]$&Nx RҤx>;E#HdK $"N'1*1Օ :ID ODt|qo-\gD@ѾN ya>j9n9XJ HS'懪.aY1V^̻'<{ |[%pI8J{} !M|:FF|cy`!°GiD_K|&QW!^TCG›n0 _ Cʸm$+AFûEavsnĮG_%84b-nNߞp [/7V/('ZWD;bM^Dif ysl DlUh< tRx"\4-N5LAW#AJ+F^H̦Bϻ[~N)Tӏx5H{xVTs''?ŻQ[-/;,R9)8j-j`K)T;ۜ'(!b ;Eу V?I?dϐoESmT}T-ԡ A,cOJ Ԋ2W{X1ۊ-Ѵw' m¾1zRS",*;jC躼}Ԅ$T) W oI CapAQ10&$.gpP5_7tF&84I)TLw8l&=1hC=ԣQ+ yu4IpxqQsϓWu!k7 =>gt9Pe]gDaPC 60ûf/+"xx9(`4_U3rFl+~qc2+0~ɣ/,ݍ]Y LĖMyWD6귃Yv }N^0-EW΍pa?6x2_fãzwPy3%ש,Fg\j[ŝB{_4ϡ8ҜI]FV aJ&a&o;XR$7|X>תoz[>mq="&.6(*' Z&4cM q."bE~zwoㆷ[WO2-Eqz4|q*> zܿ-rt{;qK`:|B,SêҪ1Y|dY*a`=o;d88 10s&6`-d3V񋨻pY8EQ콵5XTFz1.)#qoK!sZG0e{-Z,/ {5Z:4I4&8<%2n ߔtԒMұR!ںZ%Yy\U<OhM I,ָ8qo1J< wADRbz&0BifjB3 RLP2lwg3 44w֨G֎dzD"t\`h8M^SـGx`[V5=\˂ %Py SmۜV#w0D؅ۈ(d3Wр]i#X 8dKN%'R8ko ]`xO\Lvbq5P2fn|=#YΧHUCxO&,3bYFvWռQNwyeΏQg /r\o@2%1 RGRVA9amM\==1 TQ)?*F=>ÖǮIq_\Q]a9MѾ^Y(0'J,aeM(auT[ GK)у`~kV?EÔO^¹qa)`r@3eE.zpy$H.EΒ0+vĊ5%T t+6#MjxOV:E=V~~v0|Hugn^(\\̝+>Jǽzމo{c`[r>Z,ݽҙn^5o7 &,QK+F^%hc 6Aǩ`F\~-tv]?!g.{ 1#`6W`UfZK;hKk8䐪q0*9T]Q,[A."i`"a긻阃-1Vkc U5B*3964.ڄV5osְɺhƅiŇ bK!civČytbvʲ71T9<:Q'!gEjCbwV[ :.hiRg;5@QT\[`IdK/Q#o"b0w|(j/LKc}is! fv_Ҭe[ EדV\i4+iTOԝY5|b(qԥ嘙-ݥY _qSEfbű:讵ˤ3Q< o8EijBl37EA2Vn:}Mph*ދ/+ԔG7w}"+ViF/qS D'Ir9 :002b ʳ21Y- `FZ ϭB0$5<J΁oqyڜ=LK) K( Do3#r??8:mt;9;,?fjzE%҅n0/k>7*)tZD;\QуhW=aMx:~K \A\i6ZŌ$.T'ZwhxI^8OPW0' q؉!OOї?Е= RgG,dGwޤF&i)(6 AZ7kGO?=|U'rj}т_V4'(((_%DUrNˢ㸐[ ͒PFCۼtwN+^gTD;/$IR݅ ;sT 5Y8`)+ARy\kv?w|r{MCRDN{_ #/慙lbr-U[UsWlc$,վ|H^tTϴel@wRË`"G&A]TBFj䵇[Rjm'3@8 lezGcyy=e'XIÕq8=<{얗#[!|=/3K@Ȯz?@X{? emCȩr 71CH. `b&9%/]sy &rc(p~=ŝzm3-Z|d\^ʶ@Tx۝?9!]G'fszY(S.BBI$лo"*F`VC?iOkC[(Z7/t n8x0A_ n#_[h\I h2@RbFb&W~&oy0wcqGw)y c _1xo^&LI.QT/"4[~'Y<(gL7Nt!\_2W3^k6p3Q:6pR`>4h~L7qQ!);agTm6lfKiZy!ӌ/hB6m._Wn6Xt]Z(@& Ж6JGI ZXyoi-*DhEB bkĪ-tnwg^EPZll$u_btܙɁ,ݠ9^bthsC5-Rew ,IWQGpSyq$ˍ0  l&(S4ϧ FOOA[bѡ|T6-fW_~  *;E鿺\֝X# /[~R SVxN#|I\[Fq:mVx6QSoRC(R"@z" hgک.}촪U7%"\$2LjX%L/\ %kÁ6_DL0aoiNIz%( )*Q 3&"Ӷ]Cj?"ٓ|s=(Tف&ecj~?ݽDjؤ[/s# "0s'u(8s{f<ު]>e%8pz; ͷ0RTjm+EC[="ͣq+r;-+fOr<,oA:m G6vՖ_j6MD  Q R`;jJy].*# J۹ABv))<^ C$j?YhT~lyTj!w?'_O}]W҃=UzSetM8jketY &cnaeFr 1ݰPJ:)KّSdLkiU ;JDho,$vqE0t' ~D(I-/qٯ6@NhZ|: A;!*O AѸ_#̧FfvՃrރWlom#y%|FX ޝ =v^kHѲ8ܑ=֙A>FWz(qBHvqXN ƅK;ymL<$ 9AZ\}d@imo%:B+bFrmmcWW,D7ҶaЕ:w.Pl/veB&Hr_TC{.5Z ֍_rs $ L,G_"[)-+;VSӱ 1tqYPj^1T~1{tcz3U4$ =hqMoPIDW*&׽aw'1<3#agB(xnazj|,N Fw| 9 q$1;3.׬Ml-xvZQO`K0)?C=Hmnk5(bÕ1XݸkLveμK׭v0]s7qEPѡ\[dki1~R3&/RȾ*8)C{hܔzwS!CIV|9`]KJfBХvwj`\`dXF֖?풿K9&/"c4,"xu!HŗjٻEc#eZm<^cQ4q3TV k{.o7T*DqԨF` ˀaƇ p kK:(&,O#%}-ԩl^а7*jVQS yG ^ҷ#q&}/Sl| 5wԋQTUDJG74*ֿW 4hOu[fu]p>dxQ K7]AsaͿ,Ih:Iɷuu3P"}&Y۰ٟ|>R􂵸E'hbjX@e .AۭSsºCֹxH^޺ZS)0X)W,3>oxpfJeQR=^ybx"{?$6n]-Y1.D #AVB,!m+˶m,ijU-"3@[my3~B|Q-BW@  :j(]+UnJw[.16 qj~"W>((cyڜwz3tzPw.}u86OCLPސ;Zm)ށYq7 eh%(r>lȐe]}hť\=KS[#{IP( >YJG*S.&B_mٯ9z(x ^u y@ͅ2Yof]ȫ><gKh[ЊZKne(/1ߧ[qӂo(NTARlv'٬\c+Z?Udw>]y:kHIRsT7VVEʖ-R+3Mĺ.f\[uV):h@wӆ`(s1#;Cu5_iZJf>wh'Uk϶RS׶nͧӻ{[w,OC|#6QQ'uhUZ}I;΋oޤZ+kdJuf7gpt/ jEZ+`z<@\ Mr.=E ]@%+$ha6GEz¢{9`E+,;78"BK<AE^ؤYɊlt5_vX3{#ڍW+Ss%@abKa![0|KB 1 92Zn'knX1Ue&htBmSOl:>|<ϻ(Sz~޼pXeS${kWmѴ ٝTdA"Hڕ.QG+cJщȖT+ã @(Y$ )1a/.,z&8N J+mH*$.cF _/ڴ 1Kn`0k{B"/TZ{! `;WK FK.݀/ j^H/y_Bo^ }[/ܿf.fz|ҏIGkwQRv-"lYGvZ{XT`K_vx# bܗ d8u}Eg*F RPe0ZL|i+p-Q5J"5b#1g` h)uDI%)C]:I^z`*r̠"Ԁdb.ҙ̃!@0 CbeVhN4:'Q%o0D&I pe|hA^>'^PC\I8݊>CSF Mc^7/uŪQ8< /;Sp!6[xF,wBu#"ueih|˽h/bhJ tn)oHmdiO9\)D'RZg^b0@ gr9mGqȚ nz-elI>Wwo_4AND~ϛG81`"X(I5Op8)#D랸l} _mKEpe}α[nWΒGM=SlD`(Gk!2-i3W0-s /gޫ<~406 ]q\N~ߢ\{{8ѪMx>۪HËmSP j(Lɧ{`i0Ƅ6S<4(v@@D^GDl+˛g6Yv*]),mEp 9:_s]h~|=F֩]V0M_i޶o;wPҽ aS ,a\+#OAB#9TbEK \GTRϠF5_7!~  pM. ;BsW]Dy\BR(bo@ ]gSg ƻ<۔gFɛs ͷ6JzgLa":*zE(n%]Q*zz:"7{w'. xÁ[HX[e0Q\ -P^*Ǻ"<ĞgpcOX`\`] ]Z.^]T6ne,gZq"ccZKuTW\YwAf>b+m'1#Flz9_ :7jY<| ħܴ% w qFWJ=Zl›f?lR5;Ӽ$ nC=MJORxLf9>D'0,^Os˶*wYx7akH !(DTE!Ɉv a*sЪ;DVl8 =gz -i-SZUE`nXl nm1U݆> jܖx? Q2kjycB٬(/<~j(>ʬҜn`p1<~)+y 3"^"[+ WՕ}q?Cej%jQf\*5) G;JMjT8󛏪=q7k>%;7G@Xnu֜5t(=!a97tuZmN\|.pmzkOh+\b3-#gV7&GZpaévo5֦3=E [kǽ&Dx~_{uur5}firz@Þel^ 뭀o;mWIT〸iQmOˎrz3pZ+5Sy.P2A{jP 7}7 mDF 1_ ډD8]&OAU68rvQ>tļU{ĵNbsųe۞LKy lT|(vR9'L=8-[19B8v{ЌꁓR_D A;>tTkqv Ƭ]Z#ߜqs HsDr2ʳg'V>O8v癊ʩE)EC#MBo$6;x+nyv29'ȠtmRhhӡ3|52-/KEDj`DZ#NH5 2d쌩e Mofr"[hhR~ј`كicm+0gnSqW>| +)ʊ^6{8^=UYe *w5* aU;ƞ  ~SCgxwZHP"ؖQCo_pUhQNp^ bb@f2 8L'oIp}a͍y ̻eHkсйٻgGзM0e(lm5b=Vc]ㄹg*& a3)FGi7!һ"7;x,ZU/` U lX~\^ D^p,6j5keW-O7adEPvG2vY<;Jx .%aX'oUI*Mc(MJNQe,kۡ-o{hQf'$VnϧZsd=#CSeV1 +秓g'5"o#7Nj#>Q%#kX3Ӛ63~/C qYuB/;Py ūl2Ax k ]J5 2~ǗU9sγ }rOx^clXs7ty~_ryRϣmu8 U2͍ojux<聴b ҳSy]Cozqj.~(:l_a+h9x~kW}C)`Ix>%Γ8S(yKkvӯwߞk9G8Ǡ(/۪>lf h 03Z(78U|jnQx|'~o (C3p]V0~GmEYaA9M$pDAA\ KXdoR-zfrCn0ԋ+]?GldcZ-՚ke!miF<6 ms)Q\\Cy {8W_zI12@7\XތbsI&Jjǀ1D=trx[x΅y! | uܯ<P{Y꫁U gt?hPL LeA@ JP[+aKK=ޖ^}T (N7Z%MtD H`LT7e6Pd&Ћ>eP'? B)3Z LE޳%v۬ y]mvKd҈3\T,o?gsYi2hF]phn{֦jfg>׬u|(ӘE ӯ2e[4nMvu.M6(,1*Yu+茔@8j \āꇫ P?&;et08f|T12lMg5 t$?U# B5#D*ceRft(5v)ծt;cEGH Gײf090Q%v3_2Wa;كZtWfN {{#/HgׁiZ?)4%Fp@-;8 @8C!e Q&'Zp- Ij!TDңULf`!_Uj7ycrPj(% #sOSnIN \v ׌>_zgp#9la0PF #ȿE`eP@: %hzD3JvlQ*%%D\ jmF5Gyeݒi>.Xݣ uINk2VPD n1{A]M٪6q7`K<'l؁U}ЍKKp`GLLj*nJy- dT /'Jk.Ij~*Dt/|!_0(jT:EK(%Ek+ԆJU[(}u4xKDA% y1-GN9+I |JQ04N>?DP~( UynqWY$+¶`_UQɭmk>RiFO^UOL$+OB#^{ ỽQl@=OMbȸXgH~ځq9Zw ңm^S)(ӯ-/<.IK~3[?c!4[k(cIrp粦TiRm8* !(K؊f vyFQVP $ՏF̄>UV@b!V=.mVkXB;b1;RS@w'| /5֚URzfg[wgR *r3$\BaP"CqcɃha[wF5 &NV9XO:k i e Gw y.=HZ)'x^N1ž^3*k(<~whyӧ4HC k}M)n 5ɥ~/vюi:JTw.8Fj !ĨFd^[ @CUXCe:+\<;7C4q[qM-W?Q`"f~CT>2'3K.# u3 o/RWd錷TU]%AI-@q[hcN(qq2JI afo#I)2H=)E?߅Bn^\8 1*"9ET|TP,o'@OJ7j6H77-96rhr_j|&Ë۵݂ub~q؝&">bnH⎌{Do4Oy+w[&UX@`_e|\B0BR)2?% z>y/1OKB^2 ۢ o4T4P- A6V:6N#Rlj'tCfq 43~yV;rgDn`͹( 'swɔ[BFM@g*cq_Bn7,t/KX>zzVǢ|F~ۯMDdv,/bBnj>SY@^0]x HmуdZ>___J 5۱ݺS+\2<$'J7>sW^Rz@FdA} m q4#lm}IX<%HJdBK_,d DwI݇Q @UmV L(}By-V8\*>UQl)!”YJyn4v]p*f Do}6f6^+FZϠ  W* Uw @К:*6|90VmU׸}"\gAlbQ5s/VK5SB0oڅbl(Og5w8f1ɉZ V.Wr$rRZ效5QO>\۞x逧r# <_`HHY`FF݂UBеk)`¸H)l@u?׭撦n:0č@F7W"Y]%FGv?  ԡkt,l'5C7 ]e%JLЊ1 }bM҃AW<sݗ3ɤa9w{_`4[?P*ۃAQ#_HT.M61fWP<|_Z uj~ 58=$ AzmE,ڞTX i^<3(j:kG!ScJRvy7TE ';1#hyuOh'꼫y¡%#9(I7 p|-9jo 8Glyl-ڕڼ=-HUN-"J\iˇJe)ȞGmqٝ"2pd@3JpTSV˽ߗ;裳g 6[.%5" }-1dm6bfIz;ǚ;P]0NxtsvEb1COѿs@/uurrsf8 X(Aͤ~_28:UM( kvWg39Qם1\Ak{f2[ +f5eZΩlF鴥G%%y^PmI"Q|Ltׯ!iOޕMuA):3;E/LF E,QdkTD6 .{wWUYy|A8LE8q@WXR:Mjx?zNC#)kElEOoB[wxL@ e!:6=}Bi#l 7; =P:(NܝҰW1߷NGӂx6Ծ0̐ihvE؃k< v]TYvDs~%7 =ɂEPP  +o_ZfPq"[ <ܾ$_\ 6WYwpL(*Yl` =t &9Lvfg)*ã5c6XyqB#SW "q]6C,ҍJ>b @ tM,F)DG#1M-nMsA[D>}fbIS8bo!Ľ,JP4Pi2osʩ)c#L:jyxL .b tgFSn)+>].(W;{@[kf L=x5F$O|X2xӳ[}Fv BKxh V^J T= ¿2*+lh6T~b;W&VGwgXG֩+'څϸsL['cG~VvҶLNڶM2~ėgCWwL_^,Y`+bW.g|ii[ӓhϤXf?OP}VV|8!. S6D:;ۓvVd&FE˨h#;lQe acδQ$88ڈWLZ ٘㗐%]^s*ji"zHTZ f ",[ج^r__iݾ<q;4S;}e"jhZ} !rxeF(_g24M;e:/t9>IY‹0m.n9CwmJ-XoY<_J~5M"ܞ0Y2ҍ=ͯ\h /utх^zm) RNE-!k?]-?-z]Rx*YHP_3ȗ:;F E $wva]VIT,Ka8D ڢ9/DD{{]‹yR]SimdW İȶ]-o'p҇Yq~G`ip 7/ޝBfD!Bk<-wMuzTJs; Qm^L> wZԁG/Ε#{9J轂wV5PP\%#|q®k SD}߸ z܇l%8+>Y=(";THIf]|LP:zR`XgB!nSR#J.jC l*/2k8|,U*ۗUH[n-h̐}|=.pGZ*@[Y e.r),GQ|V5A~>ѹzOU\ tQLӭ id%pF7՗$` -Wb{ޜrB|O/g 4}Cx-d(MxսIoL%C>f*[z r+y&!G]Nr}rYfs8^lfՖkCv0|w'o{=]RunƠf<4,ᆬtgn% j\f J8^z`mi@󻡔I\G+-W7/R=-IDw铛#ZgaT(?զ]+Cz{xK{mȑM*P^gՆb5U03ZjN.35ȒD ptӀYq0}D\#%ɕb_+Rsǡl0_7~Ri olk Cldɤp@[ӱ>'^EmxizHvѴhq@R8M~8\hmj#~X*2Gl Q3qf^Q@^QftzMռŕCYR4Z$,(y_kfpmզV6DL9fLIHI8ݫj>M*~/Hg!- %#dt E[WTFˍh͏ b. R .oͿ.+ЁY¤NvN/L DRM-9(VZbҁt]!5 '~FiGwqv|U{33G GA" } 6&X|x9T (m-4}JxrqpB- &Q3 cbFm/&ɔÖ1duraic&R`qf1INN`t}Eo!*WOIFY_<'o3J"ŁFnv͈CxFN<<+Iv7G6 ]iwBeWn!nWV՚YjxɁg91FP[ũ#ِRrBa/" F&a_`iL弊kܰ' {8#3jh3b{B:To[Tf\Fjog.B3UZ!['HB*`g"^9ܜ8Fj:ޙN(Kv Y:(Gސ[lNz Hu$gD|A-(Kty6Ié _ZѠzu] mvau+0Ӣ2-iAт`_3~nKkۂz뻮 ǾjA.*!jr(pQIm RJjv&[,VSzxO͖U d8Z(ݼ0 M8*XRu+Bf&?}w+ p0*oƽ0 Sȧ&prWq9mF)G mq=GR&<FDZx!$+_)Gq8ċn\<{W-8+w11b[/DP V`1 "&Q'q/<㸠_5[xk3w ?w3Xg#e/<^~9w6%;}uAYI Î`HmY|?Y(=w0/ۭ ea)mKsL^_ g$Һ]Ha`t< OhQSÁ'Ũ\7|y,/(}C `\bM*x|=޹)&7,;)սu ;g.0pgo҇m=P윜c sRz:1 mk:01hǝj{.p,F`6'Q5[fkg>^}+dIƔuYw]j<+"kBGW ]CMHAK/$_:K-Yq[}?f"ia WմdrE C'D#vjπzMASj,+59!bʀT6@kIEr8™DdvP-=fd'"4\,d@{-W4qZgRWn'!%([N}EKhʼeڄ A3}yr4`{U +Zo;}/ 0 %\5: yW.t0|㏻lO"pwo0KfWw!f1&;˴}_.w1fL&1f4@BD2q^RKQ[>/jߚ}y_eQ?-}/IG{_C-hfm{ '} lv?2E Id,9pMq0_uH!hsww0w/OA[ZUqBLv5M[eE^*WoVU|PFJUjɹƊl`wg4jۂԭxnx{∵JНT+@{j%3|WWw!||H`c2"t$k3KpTu֕wwljo3;a}bkWކu'sNdk~[cT $ia~]~ vCG{,b_6޾3h6\ȁuS̄fs/fW)plV+P7wTw몈 *a@26h>8G6MGQ:R݇PFR(0a[BPJ}g Ч dfI E6ڧY:x8F $CRR7D{ wɛ+Qk$ U2kPR7#ASi QڻW[~0b^0%ȃAK@.b<洪OqơTĊwXz1eU+L&bȼdH`5ub,hŸn#_@foP Y61@}ڝTqa}Peʽ%s.ފok綔THƵ̤.eߘI;I Xy6/h#p4^j!q^ %CU?>1ڬ<p}4&y(_) Et%_خmrPSpTd^-;^H~!~gm䇚Ht=әl#⛐:.a+`q@{DZ*d 2S*.hzbF"+ʭ|QCCHlhƇ\I%t-akqOS8 \0։Xy!@VS*w)JP8+$\\岨_q_ia;]QQd,.$V 2,j,gܷ IGv)a*R j$ 'E ciZÒ9P2( jԐ܆ғ+o cب̤ %"Ŭ|u:o G`dTJJNQ-U9@+w)0[qLJ,-""oZΛSVFS;"L(T5['`xýԜԜw$>?C4қXǒf.:j#@XP{gںY:> II*!7ݘ: vf5۪ ;72uâd`86S+. :7V]B 5 Q8Ir_72~(b$ *tTcSS8.n- ! ;,O1EѬη A0 BnId{X>Fqӭf)~ C(gxnv8l~^S}mj!%")b,!9λᙳJT5hXw8^N+>.:]eօւ4muiRe $[m7];N 0J/_ = CC 8LE )Kq6W@ mVh'yTj_ytXIT<O$0o%j#u{j"`0yX"/VTقB݉mo-q<ppBXqZ O(.5%0ڥP {oKm yXQz+ujG}%dA:p|֩EN8jcGQ,io| !ꃟb#oPv0:U1٧hZo"eleo|u oX98]xh~0WMz} `8[ wީo樼_x&wmq4-'}ۼ1~(N'Mpw~/,nrgF߷z{;O]|wCy U)FW/ޫ]%(k);ng,PH#9jFhCdl]͒l[n% Jb~JndeSsnQCO1]SOa D q8i5U!Eq;1ƒKX7y}-8[ul(/@`ܒbnFQ0xmb1ɔWxuqGo+Ԍz_U&z+UCK]4 /3ۮhJǯ ~_ VB@e"(94"@l$Z*_)^X_?Zdӻk*ʸn稍!::~?Yt9Y>\!kQwxvl}f@~01"-y޹;m7˒3.@ǥh#KޯO>.w5G4:*,5 d>(AI1}CEdœwZwjYKuebڒ֎u3m{59KӺГAU啚Ĵ6>  ʲI9_PחE[g(~?CQy Bg(G'Ff`Q{j' B8 홖<V zƑC)PO5Rʿ_1u2)71@7/.@wdJ׽I&wǒ縸jd 4s&"Bjw? sEN`HQUsZ$0,=wOwbߟ\<<=\N B"N3>p3wf@^\y18A^s`lԆnDawWw T }iwHI&KTVr3&8ȇjfM6&,quwO>` XJUPLWޤX6](u ~U0LZ߱kX&ZϘy@"/MM{1'R{eIjL+nj[_@,aʘ TBQو鰷7ȝO4/omUel/f*hhocIJ/Qg5h7e.~,ȼgDўAbƌDreu5įB<%KMV}ruOBՐ%a < *™Hⶈ,9HePR{#l`Y\2}|Q\}С *y'fϫ\]f>qfw$܋?vLw"X/JZ e'p 3˨r[ۓH4dJ`Aܻ.~3bC e tt??0M(>;.[cGx)KRvƋ6*pW7Mx֧(qBr:31pNb ^ &@)R^c3~K2h$n+ͣxrdBog 39"XE`4מq)n N] ņ|aK̀fLs .?!7.|88b¯Nx;OBZ((A16LuPeZm|_^ F I ɐ6G/i=/_zXY&2h(pu$F;WR{nI 8/$~W#\՝'.: t; +aӡC?ѸXUL>nQČB0Б=ht;h _C<6bT~۟a_Յ`؞I@u\73:FDϝs؈"I^O=ϖmksXER2?(w!h\>ڲ:uBZ%EJAQՑ"hF枏^|YDz|E,TN\a..ߤ.@h2Tin< \#O :br92ܪ<'{ȷ9R<ǝڥd_[4wel!Tgxr~?+HnJCA1ٸn%4#](?[Ɏ3pvxj\ fShPLD,:"*. -2|K @>&7^E 7$e87$;Y03/G/k_s=:c:Ѭjb|_B\Ã!;<#gڷj{saޔB!Ʃ75Mm.s3 M.zM_ ܷx~+㱢 Ww!UL7-:F/]xl9ԨoxW,׭A/tg\7a>|Sou"!b[4MWVpZwo7J9ӕ췤"4![yY+Nmwlq K, U65Fi psHeT"0~ @A/oL]`c `}mgM]W(dZQkLC5#Vl*-.f3F-8i Nx4D(I c¦RI[TͳZHɧOLs9:݀XԌr0lS[5V+o\``z0SXGL}IdU3 OmMZ}OT_h=IX$V5Y@"9<fu!ЛLuo(qsw2ee9Rh~]A9Ppwm<`O:Il]GM4@6n9PiF́@]WOQ-uoO\+7@꡴ * DTW.8d03j1mN9jR6-R@S_bFV1HHIG_2rWfBK݅9Mj,\, ;l9euDܪ,c9ʭlf }:Ѹ vyhű|$KƑB>pE1E&o 6 :?)#1G;pUF75Ik7mr<s.x%0\,zْKÂjk4x9=^]/$2ـa@ E9Yu \v!}ji[v]_a=?/?Ud}`iEνF\YҴ]m粛t#uuu4W\VgO[^!GW7xUq\ʨkGK5sv Sk;7ƹx0Zop B'́O~H[؉"(7; %ܪSLAuh Crɭ\,'1^owQtSrpE>nu?m' OXvP9:ԳjTZV-/Q>4~޽ԾpP@g0 I#>ƱCcG"|/x|7U4<]D|DuUtvPGל:"CrbqAUaPMJZS-7:w_֑[6~|6BiG@<>فJk|\f^q .?"'c1ۺ gUקMn0|"*W X *" ISQ+rHsT֩`SXY)&u 3\،^BрQ{{+@9p!FrXSCq\-zl>m8IQ ‰sW b:nI*I42d*dE$gՔQ +5/8& >ӑS=whEW}z]v1H=McST/(1Sџ*[+DI oJ|B8ܹtm1ű/ֱ'L huP-=ax+eN.Pӯy56ڐ;^?=N|[cz` ijfʾΫ2(W ! {@+UoIG,]khI+3'Tĭ ~G@1LTML?}PZa-x(ĹѩYn'?jPN§ļ| vc|Əq+wb;wtJ_衚@̂?ITBfPSPwmc6)ja$:Gu!BR&%_d 51.$[M[aLtEb g<)LvqYOh4annU1xh)*Qlj@>)y%dAG96DHzě2`:x+SP)nPT_A0lݬyⶍ +ߖéIn<jW4]7Nu[3-cu؈uuhWx~@PBuf/#YȐ t{ѓm!gq9a6>}* {P3Q0+#!'oD`d:c_̭zzܓ!3TVoADCqҸjbdлLa|CXTbE{{4wAПMY\orwo"Y]4qF >8ݙe}% Fe88KDfӳ=-I\R͇ )pm7{ʎTkW4;|E_c"&wr?֝\ETow@s1>>y$6Kb".|r7We.RXw"!ژf ?U 'ZܱL7k[^dD9UjH_ g(nEU"kDy)Q 7\6.MD/ЁBXd."4Y˟Cq”h쪃>BlW1]ܐ& ?4]M?H~3pE!,R&>9spQ A"3sed-~mߝXO&4&<] (L/3F.ZZ)ZBkxW\( *d򚧟V324ַl3箴c="uUD"8l&nc/.L!o(|:}-ZY3~f?q ӛ8Pk0-R bɳ!Lc*0£련<|",<}7RğU쫾!g98"amM*ա6`2ꫢml5ڂUͪMu- b0BuM~%yc̲~BJ_">%򯾨ڣo- eg+ ϴ>c;Y/!40h^R#.D4$Ih0KbaR(M]^<01mJg<{>0z=xӿm%qQ+_st=mFI1^ڛzS/ VSaƝub a8Ƅ+d$LURc 5OSlAT!˨35F/D(-+0WHCxphak# _/_",f6fr'ZEn"&=GLp J'bES"ol~9JLPf5Bս!,2y,:w,d۷<_Dc G%lN1] Ηv.CyGWȺ.Ic Рo`i%>aq-۰Uתc~oyf*A8D$xQ;y; wQGla bh5!EO[+&f@&0O X&乯F.Q ɢ}?؟90e 39ޘXjl`QzӨ co C ZL?Dž-v8՟t\#À_0OYw?)LA]+ezSXVáICD Q:<"rѻ2#y`ݜhsPm1B4|ޅ2W"xm *OP>PiS1r!2FTHa9<wތqag̑Rsf+E+6Ifp=H.glӧzP:;]_mB\[N+:H9' 4qbvOG{! ^#rgPhhzH #\uLH+Wfc22[-kt&yr=8ټMYj ng녳Dn_Lz.rgpž0ݤtH>boFz;nBr_nwS[;1֫;_:4cIy&}?_2޽gN/ K7woK&u-I}~~:oݗM&~m7NAu~y o_=\WBǾcD:ww?Ѹ_Nqv_(t^[;zu֛}KW&Ż~zzR:y߽ ^[:o7 -| Կei]jK,g!}*g {FҸg+yF_.aÅ A_wvr7э{|7IW.7fFFŻf[;:mgzݍƎ݊m/Mtcn9"w օ;Y+Ÿ9EZoM]MK 4bÛRB׶uxaԺdW ܍;p-lqnO-I߽:$izK]o R\ Bo3= *ƃ Uk;f (uP2(sS^%;iMyZNtAh,Ć3a'j|vCqixb:E;@Jz{f^c<$C<' C\`HZA6YVE!Cs$ ~4aY+Rn8Q΅&l+2qm:84M3}~$N_c;v9N\$ژAGAڭӚV-,u(͌F zb~bF߬kjRXKmVswvhRYO^J2-m0Wa-òMʕޣs[L7 axG37}R'8];lm&Gt]>GT#+y&;<Smh4s ˿9Ś,K re3g.78] ")P;Mن7׾ܤrѣOfഊ5֬MK;3i'w(%ez90Hlreo9߹t#"x6e RbftmŞORHoϞ6[5)y 8,͎x/J?wdkCC_#)9+6RkGG_"6oo-6^a$w,Fhg0Tt.Fb*zj ږ{r0h ةܮ=٪6Zek{bgm M[K%RE83E~%nd:STDNr{X46=k>$4wp^>sr,SFƭP;F|ԆzA礢QNl;E7rg1$ׯ ]#I寧g{KӖܹ &^{H )>p9rYBDOZWu{JPL8Vr/;y^NRML3gtaShSi547H2=!3iThp79!jgk¬V\5eS I?kY"GFi]Ž HGxáVi\1TZ䰧9TZe 4, Űq- sqZ=e~vxu)x5s0\{7몃q3[;6Jn\MGX3v-A&5j;ca[\$_2I2M7Mi_TNRzNz,IiS^zN6x`TyTL Fz $D kx#_V-h|!|.ܪ9b,|ttH.GZj[ʗ+aL9+Fje,kE*m|c&,Ԃ\֎r%eElOPxf4]" I 9y C!7Gp[w5!c Vp Mͥ䅕`X1~So$܀{wPi5`p␂lT#2`Î/"̅[^%(#JDd~[8ß$8!bTt^2A _༓x tλXN9qR _Y}=˜];[.ipjc-2uR7VΣUXzcvAHL166'iSX.2 \uZ'Rt5)* Ld@]lfJq9h#۰t9ؽr";Ȏ12;UdYQQw6MЖM`^j @#vh;*^yt[`?,wmrrRH=YlL9nЩ7~X|WڮZiEAcSqg!N y?ZK7)>Gdž&U=Bh] #灘M܀lkkN #hnt 7DNhL^KW^fBpCVL%mZP;L% ՞K7 صQjK#}u&o!p5%-_|BX:v2JZ0\@S ޘ?4(Ɨj]o:UK`}ufdue?9-wGq zJwMSJGpL-v;z\+l2ͷH|o 6'Qlk"?-2ؼ{Ykۭޘ3b7͹tsb#ZϥLJO5T=OvKk4W1jD:xTG~oOϊZrcқTfl|;s};f޼Iu|+=t;x5S)ndHG%l!;ĺͣ0$˲l`TN" ?&|F꼨k^`ST65}I@=,Њ`L@[v:v,tjsʘ }3C>o_ |Mٲ8N$cTcM1ǹ_ a*.<<;2@eUZzR29laO4RX GoD`)dSoYy#QP>"Q.I1)P󠠄 0-\۝ m`4k Aah$%ʹTۑ%R;iԣ+g{R\g*h`]cO,t)D$#JFXAV]ɿ."HO͹EP!{?N!#m*;J;Sx*.!Bb.b]>v:]>:SV[\b#a̍p(5oxiaxY-Z38#˷\2I[׬^^ oxURL^+Ք{:Q}m?m%1w-U99gN!\@1 s7=:K|\nՓ F%Ϋ 9Ȫ[Y$t2fb_|QԧG pg@ge=i^ܲqE:Z`u9F qƢR]L|Ҕtl'G7I(TcQ@9lc Ⲹy=Y/rWUE~'8r.L ՄHr2 Ap/ ^xr>t.~ ql!VqPǜxB9'%(MɭD ƓۿuM&wv9 6\~.k)r(KIb/څ? ]C!9g)vFnݹX?w~kV%1wf[!rRBu*pr4qjT:d8u fxY3?LX 77\KӸ\_펪H!tf;[v+릪l*OR%q}Kx ٕծ]w_Թ|دwP#+x ]] ܅Зe.L)G&8&^~ԉÓMCLlT ;<ΏYUÉC^g޸&r*᷸< uR:g \(rHnGd&j4!D"%PT4*ÇΎ]cǽ.+˪U[ {-v n7_*V\ZwpY>Ic.q7˥CϪ=qQmg̽< hp t&W&6\Џ":kqˆ !J_K8WtAux[VBm#d 2osE-Mۊ_2 ɘ{:BZgQML Ua!Z]ZH6dݖ9HUT\]ؘr)UAOPKf9}YK1J8Ȃ#=yV8[U/1@5#I%u/~8򝛙Gs*wGO ,JuH~S,+K㋀kJ^VJzZd]PA 5̾n-+jk>nI=U'xBqNI0fռB/*9K, {D,p Vs ?SCL/ToRXJagSse ЁTPˍlXK(9Uv=6u+CvwO[a)t>H*7*Ot8Ps‡ʭZTjW`~Te1qP?heFr{r(G`%(ږȮO۴>0K#Tf=rQu>HzkzZ{w}E `! vZ&21]S:Oi}58]9A챡5l[(ej!>5uO ,UzZ!`.+3Y>cFXuuoN_ُƽ\.q᝷'+?)Bq?jziV*$akR=C +D:C}y)$99|^BIT+JMA(^/0q|G;Fu-MtKb6oY=bQCJ𵜂|gg`gشJO^=S 5/wK[κ[#X1ǰ̀ު۹ۮyhyP-O$]WUmyfW̵̱2p.?ByŊ(`ۗqRN% 4:$wJrs`xem@օ84HZ䗌nZ:,\h/ʯȴ5|~XgeJ%n;*P j;ϭٮT?,:h? E0NP >p{> tq~3,| Hߕ|*!5,$8Ҁhâ+P[AE>?^\ ^aU%;,2HL< `< fvu:Ѣr+ͯ*z5g!W]QMs{u:pzǼ ߎxҹ2rәt5cnR7wi:)Ri *".2&]v5e<^:w-)a k {ܙy:?!?nIu|jFF['D]`xe9yo»{7ףV1s=xOhR+wﻓӽ7mp PS)𦲦_˯TQ (9(bcU{͋n,G4t;v*6-p(\bΰ^Ql ̠AK9@[V\r ؎GN[% j@/L5?U'^rAw ar.Q[Dah > H{qB=0ʣ0ƢW*l1I@Y+ݝtP+! Knj8<0iݸQϑ_V3՟2XAVZ6\} WS=ɯj B!űfhRkURZQ5i@>dlh&.ٰ$Z 4P{Ԍ{^-MS},eZFBݘt*Qz>؆bf vOY {=V!GSqJe2ő=U(-oy6 F}+.RM);Xo9Б`m7 «G^Y \e62f[wEI d |$FGG72Ly-|¯M%"wB\:,7)n >+" ҿ{AA@",S=" 儈 65(KNN<\RM  mU&Ij/Vw7"ˏ3ll].pR9o]>? p ,ntpw"1> d٢܇#``HbXICA/Kk/6pQϺ#>rznWG(;T=(օgva=ܽ&" w3zT~GzuW.ZRlԽl>.RSkWg|5kG[IQe6Hk4G\?墓73v֍hߖVYJX!j{Qt/Tј6F )=,XsIw *;df_Q ^HǀtFq d\~/<7'R'{0)s_ ,aE9-|1Y`gvJzBг]{'DނHaӍ~{D!s 3TN ^ z K_/s{~8(帡t2//ʔ!e\L ]p1\ PKڱFg3e<3O(wLʃgY򹣞iځrtFNX H-ʍq"ȈbI+{kY*T^zl"grZR[9B'Œ_\8"t~Q!ө@n^N8u)=c4x7[Ej^rqT~5LU>L@Ƚ$" IK>e`GլaȳS\Rg?ETn(zs3ua! ~WhoZgL5%1O/Wcn5>c6_ʽKb ƋN _#Ðawݞgʊ5J+DPxl b_ ɱ{0>X#L#2$S(AnbKMQGwJRLd]N%Z%ǚ4Q,nYq@ˁBApWZ>D@}O e\!:ED;E Q #${}0 [3 '_/q^w > z"n7w+ۊClyh`U_O>#hU_PxWyeC4^y0(]肁RjQFI1OOP1D|2dnq[Tz_pKE~wk/j3 v`|u݆d I-0ͼ.:*aWUFgLT,{ূL Ң$Ԥ"xB"\b*9k4e 70"oN*&.jPUڅ`T YRhlݺ-oP5(fP2AJȘ$`|Vs,ݪPni^juJ6(P2#\GGy{mma* BWU,Tgz&%y,&)qAW$7 P;ikfTXGY5Y'^Jn\'Z{" /E]gBjCet1Tn؆n "hE[fS p dO&S8M0mgѾ^⎚և͟Q1AO!$+/ʤBdUZ[#4< w.w+~ӋTj*2jN]9p7]U{ʛ^c*nMȬ1>ͦZ.T&1I1xodrfy%?+d9]ofJG nv}9nYygTZoԥL{]|7s3|W+6jރl?՘anf9C٥4mw y ,EKԥ{trkkΤ*BZ=-"BΣ[SSU[)U e/92 i76pĥE6#LV`tX8q{k3P Bt)Q :rД ^g&D2-ҏI<\L5ՠ{$-pe`ڵAgt a3W+(w q Et*~E2]˴;6o3TSmfz4q;wc?a]Of[tGœvGWO.ILWZSu̙5ɷ: 8s0HUat*>.ͤ{QeqX{\\_;j&6.U$0 ۶G ۵G! _nFyw467( e$uN=ߌ"rz;z~;an7'\8%e[Dt;7Ixnߘ(P 'J$ juw}7؅vKѦ -r8M]5qTnUrd8 c>l;HٛVڔiRS((O1sPSjm# [* =y YL{ z'SBl_.cM%zV2#bABt9IHe^RJ8y4ENOBc d{V1ƣM~ZEM0 1ލB+M5ip*.>gµuwziź`9g]t 2 n%w:M\6|,.瓣>an4=Y“7zU+'yY`e(~w%޻%T1w,p\+G-q=Wt?v#;wBm~^Z,3J2O[!R YiwtR[Ncq1FH)"@ zXޠ>SC~۰hp&B?b7("Irw|1-ck_G 6%|Owk?yA^܆h?va[ `ėp#.K4Mo عNxB3CÝ Zj{2D+gj}k,HoCTbH7t&8W,r{.2O%4-ul3?\毇!8?#}߈]unt7;79 bjR=ft4 {53e87aQbGkK w3֊6w}ӮG=5_O =5SB&fiṴ֌1=֞13I٢ Lҙl~:Ԛ-%ϥfll"Ov7+24%ΥfX3IZٿ_" }el֬z;xgk Xg k~rkDڽK|p{wo>N,M~(OU?Yh:RE1:M oXM_3qR0o&n Oe&*q)}1,OHY owzo?Tv>VܧϏax1O:͛gϽw(ZR/CW11S 1WQ?ʰ3~W\(tOF4U߃XFTs}Zkډ ' \;sx isYPȋu-~UP**@xp֩ ] fTbE٩+oKe~k]~C2 đqD3ԂP]$Jr"v=w7S93Ic#E 7O$R$Kh fDXt,`Y&ao$.E*9U߀]83jP)6Bo^I2\> Tb:k evBo|/Y! +:vԅƒjV\ wzz}ۻXXZƅr\h1Z΅ta%]hQԴTͫ椦 <3ÀN{G{3 |R@-M_I7_:`w]ۆ{>j|QvG힏=/@n6>}L'GcqC(cmZE[:xeo?CmR?z\f TeBut.c%K75Q9uN]Re0ukL]&*Se4u٢$>굤.5DO겡 +i\.[\9jEX2U@]JPeC M겮uԄB]&PmPhCNԡ"pt٢凢Tݙ*E]ִ\SZe^ebe]3E5*źQk.Q.WrTzCR˺eCKUM겡'գ77EFsL'7=ى,%EKzp?n&~D >W_j˿un<ǤWE_nd'wI4 ~w7+\n B?/]Xa _V]+qW+ B]tި>phʠ&W)tL:u':#DK/ag4wTu4~Xo p{{7~k9h 25rwdꍣp)of\*TRyKeJ/)TҘKcJ/Qsd'+*%ٯҫø`^d?'"{#Fu>E!5pքSe)y h/ xv wE o,m5ŞMz 6zo_\7io1Ҟrn]C )^(5{h q&mSL5 tKimaX-lDŽa55䲥yR׬r/χ%]L*i7Xc;OEIc[cnvhLc=`-5ZT,SxnK5O+)Фq.2!'=kyg(׿Q1e [z,֧sA ўgг~imVJРfYkw[t3n o.#,XP6$Ilӆʦ̝# @"Cd>BY}'9ej<r>>fu}r6q-ثeZk2H=Pvewԙyiś+a͓ ;ޤLe%[*۬P03RE+(^\l4Q`oM;[o ׹/]WQt_2TPGo,7ީ0/p:!k^3HL_K\ylWIDNviNrWx|:탻1T=;h$5`v2 8y dpE^>GkrHAq Ge!2IxUN%ʗh7̝개;|B?LyY=&Y=4 N'%Io 40Vc9=-1 g0B <od[J"' p2{/҈RI!ƍhڡLKނuLG9.;نq/Ot@0O_l=ʅMc^:Sj Bn~pأ~gte p̽=l*SKrԽ3H 촍y4B`'oF.͓XXn*1xB5`䪺 }םI'^Cjf݁l:O9#-=^Kp]~g!3^܄|d%DT.nԴ.J(lmNllV@jlO)Λ-30KgKp̻c-;eZw͠4Ա#sj%+QF? Cp[]wD9ʅr y$H"/%@b'FB6aY#$[3f{SV6ԈY<-)|3luvu7gfR]T> kl>N`[nd.c%IGsC J+懾mF9w_; ݆BL$ѨEwv//cx3̞'CaC{zb%&,uA/@&K[G %VOajYoEBilNd??IM9jDa&Pf=͆Fz0X־W-I֮^+gZ\U3$8inK+e#DiCR7 /pF:<8,J.4,꛶>==KU R:}tpvui St1_VZu @v}ո^˗ .NuUe9׹iN8M 'bP58^ G^gsgt;Zxҽ9zFoF!~} QЃaUBխA M: u;,~ܕT6\u仃C?` 2FN Dc*X@v)Ź!QL\W/NpEiQѡ%Ji8<öSGϼ[e`pIZ|rQyk$R6sG~}yࣖgu/{d&qxj>3@ \|;<^&q/>vr-ʹXw3xʭD,w`ŠiK/6:2uA>)|HAAhR%ʮ'A ͷ 1Þ*| J/{2Uޖ] #Jښ^%3j\H_J0P'q"BC֙oܚyN Abjq|tLAO6յP5qzY߫gNz).~O4Ifq_dRAGMWmd*%-ͮ/6PdO~Ѱћ6Rа_z$ D65qցRj;]P_.jfy 57&Ѫ~)Q3ytw(ͯ*zZu{4F%R-IfFm@Sz'cݪ7 U BA6sm5r3\w.(dIVy[=ږbVO"-_:?5d$yL0nD_5?5z7nQ1:-Q.h4V5ZبdG5MxKܣh[rz 56;q^H}d:)- N[bX$'rؖXvۖ=̃:ۏᭆ{5kɪ nVftcjf{uajKrf3'w#liT*&RL2չ:GZ,vzڒ}9GjAM4lZFxK5-ւp+6[a zCVS|x7iDȱ4Riej_.4"S]G#%͸F68|6$smk[lMC[6kf sfHlMKAeP \Y= Oh\5=4]Ĺ dZ$٣z v%swYC .sKy]1w&ףmP~}ŒBfjqlr=:ںX^h|-tVUPI~l~f}vZ#%v-6п﮿֍F3D4 |LhTũ@hpZV+OP@ 5 jK;Vۺ̪  O;3dM A=&H|I# W[ztGK[? _4h6󧟙ag<{}:SES+7y34+U\d~f>p ͋3cs3UqW(rC_gCz"q?VmuM8iVǴ.*vEXT:ya$U0=0}\orFg~"Xte Fc7xeNi?rCVe$A~VF.*wFZbZ~꤅hs&Y[pI rʧ1,_)1ba;)~ki[/JCA*1D~^~n-bKgzLL o 1Xha! VO .Şt1ZS*ިX%1],/RŲu:\т\zZȈroqB@+o1]諉 w~3hbjW ̈}})/?Hq{Y&vki+wX3j7V'700++{lBu$ .,gNX<Ơ@asd/0z&]%G8F}? qU"3 PDl|~š^O:- X4-:}˧Q@eK\teg.[沵o.syw.uN` k)ƘPW1]hᷔpp7gpReBQy=eA/MUM\ce*nE-I;ՆO`MwcUիW(U K?%^.ӑ=p9wegmا`H~eSGVO~%G+yń8y]fQCꍼ1e&WN ?EE;ZTj_͸r }II~B0? 1/'S4[l7ݿ&wC J m\A 3Hw/$ `H6 7gF%Cr ACw<9ڈj6RH8pQ1zрR sohy囍-гܑ:^QLT`iG 'A?8őy5#;!s.>JGK@CG;ֵx%oAՉNJ◓RDZc)ַ|^% ѐtqR8?\,PA?mX^]ZloE喙*WE~<w PrP&qwԘC;/ĉ6R*sӺ/k3^5|;VspR+|=2戹_L}a!w|rK  q"j PS|1޽f`2z_Ծ-Рˢ]'VpsC {"2,"4CWFBHhA,dVލOk%>$+,,(e\=Fq88 Q/v~o妁]`P4R`~-O*'q"xZt ^-]^=T$ iHN R:' ޅBO=4DyKF D| "(g9:vfQVNi?:8 _ R=A7C ~:e J|AgУacQvJI0)P=mǥd ͩ `jV%PGfAL(fA5 BjL~mG(捂Zk Z,gJOM[+Vy^Nen^vGq@U6J]տ톒g3B-qQ<Aj`߂zν-ᴫ$ ]c&tE>-Oad%HHLJiF|N_G#!zf2>z)T 5@itf/ b,ҫO|7USL@TQ+q8S@JR8-<r `\Pm_e=ęY.0eCΗ@ ˢ@R2D rhKO׍§05_ iUxmy⹾v8lȲS}#ߗDb4`elO^ԷoӲ-7:K!]|E+LoՈř ){BDv#fB~*9waZ3FQA˃!ApZVA EDm#.3"-D`U!T܇6RKyyxv~6??[{FL*eAϯ0)zsT$ɺifs47i=:%d@~֑'UBa%õjppBPh%]܇f`KࣰNn'T`8tbsaKY j\*tbxߜ68VtFt :@LxDǏLmTsO 쇓6$l/^.D+7|kyh[\8愩UB:rD/.v:TX1Y!`?+$lB',n: Ïn ⨵l csKњ}yxlu=Fs1̵hyڜkar1d[[OqxfMxVE3R:CBWΤFf% [Rb0hP6a$\ZV7 ?2V3̟}1Zys]WVv%}I?~B;1oSzҞ?;ѭٸ#Φzn% 8;-L}!V(aAx֐M]3JA޺uٿ;w'K22JЦqGRIq_RӺE(=~Y2l0X;/a:H)خbfB0ˆBeU uȄ):Ut:AdnQդT{qҺI^!vJ4'OlK>;ֿᩆ .u Y1pcK.]jy ݻ @PmgO>HԼ'Q>'KX]JTquJj#S(9&sh8]Ll-bnG¶kq,Bҁ+^9>iY> C"ǰm .루@>+n_qT:P /! zlse EqG:yx<:@m(,S 0O`^U.m08Uj@opY۽7CɃqky o#&FKGR&I˅15_E9f:BY5& I*v0?T[wiv8(},Af懍8v<&o^32 אˉˀM3:0M_=׺ʩ(}q-0Y2;Z׿m2Vv1lFiSloCX8j)"f꼗s!4NbExeShwĐFqjQ)օU@p@ф,$J.r: [-㵑W=d R-mC[(Md|^hR=6~مBY( =cՈjeMS+*}1(GepCUxoQ|`'}d]Tqmh RԮ/}ǎN@N'jC7 V''ඈb>q}2qZprrI9˭gGJ 䊯pQmho`uxH`~e ~ P@9a#$uH@JB|Q=P-d>>$`[c\I)HS B.mQM"kL-h䫼֨Н(KHx@R{77@ꗼzՊβNjIX<5fS}<CB票hfy:W@ziinoM9;^` cR5 ioZ{iAm^GaI"(j~<&Owب`'0;MO AAuu( R/iO8pp6)w҈YT׭6~U)|8TJU\t3Q~'Qa' Uz yy`5|,bCnn-Ơyh阤gj='zX9N(`@Ik 07Ssx'_z3RFEdZpmy/o;~8QK5/Bi@oO5z?l~F9?곱-afNLV v d7 DԤ-%Q"%ԉ\~PyPJ`SpJvi)Cm8w3Twu6^hso ϧYOo~vda};?Smv]V<㛒x6/d}k~띤gkM}u #X|neNr2ˌ)(cgqf(ippݚR8) fJO,uhBkȉ#퍩D$+(QIA%MH3iRn}iRsp͚zՙ$iq''²w[ԭ5'Sjbz\YM4E;5p ewާ r$ MANBkz- *~Na*:,wLAEwz ?k\*oOlb*ռ]h}[Zh}NNR!S76+ z'ZFyh]P5)sQyl˴.]܄չ;(-5 %4bT߭D͈E7a[Є55() z}1\eq J :KC(-+k_d@u2xʾ%}qJ$!-ALQV Vѵ:Xf>ڛq(h _>ڄ0;OmDƦ<x(ex|67 qrd; s9 Gl5jVۻiM|Awts`I8d'l}}IVot%8Wp2.p\̓0d'vJkpI~roɂWn)q̋6ӎgn`gRt1=ФJT4!$n浥m /[s=En-o:^}';̝kN,+Y\B_dTꍙ<#e4nr\*ֻ;њ_HPJFtp4kr?`$4NtRTթ̻B2];xrEOe^LUɟ"|XD9 Ie~{8'OOQ׿dW>yK:Ŗ:Eng|X>ť}JJ).S\Zh #>0g—t'<ӛ&3CL~Otzv̶2O4|’U9(VQ=ɟxpgGjϣ=ـ^| jF۞{Z.#;: 5'Zޕw&f ٜEb~*%u8:;r;r+ULUVC |Ե}Op[o7-w3sHw7v>.e{7ƿw>ޟ~~xE0컿hw Swz9 /# _D5r!h|%=us]$'YXW1Nο99 M{7ۂ[K9^' A 7#^⿈~MnE5Y[׻XQǿx'] nE~K슎'ߝ^)37/m<k537AikNY˲o"޲hwKg5K8= vC3\97Ewȕ˱]`oqY8kKN^RW2fWh p3|N*º&~v#X2',[a*oJKvWnYp߶.}H2}?X ,MVETЄHLkŒu/e/m-[q|1M3'݇;3*qܜ1]ݾ:;R~8e,]LRoDEK`_m}տr ]7 gM⢔nHwZFU-~ywkCBܜ ~JУ{X7IHs5v^fpА|Ënx$bF˒@\HtKG *ѯ)\?р7\1$cR͵zg:A:Žo >R\̃Y~fm|+TwEWp:w53^ \IK_я*K%swg7-I7wG]I}iII[Ӵ2I7d488~GggRfZ&ݜϥq%׿iOqؚhx=]o{_w&uhkppww&zct_ó]ޚd5͵5q8 6A6^6IK~ -i_ZuRژeotb+02꒮ Ϻ%0.;75xy2U'ATS#}>ƀrUItKGgZoɗw";M!g[m$SypO7ü#y߆֏QQ{ں8_^3ݛ_6 EKƔUO07YZ4QL^l&;P$ؒ6 )e |U[=:ߥh R= #5;*4dR0A&(4 Q7Aʒ_],:P}ôe|-q[tpB-uxݳi< QUTxhnY:ߤx:Le!Zuܛb9%Z 2 +t( MWRV #iM0[r$vUt48t{zA%3Nz1m0*[CCWFzs>7=ِ'|[b[}2;%VlGxn#aij6b3-)9_Nof|\d/ԈҒz4U~vzwɏߝK8jM0[WwVݷΏnŸ<< ])Jx”0 OĠߊۿbvy}JVry IBs&VΐzfVy enX$ ^15$ #:xrwyϻwGs,d$K1EB0wGS}B'ʹتw3Ԑ`GZX>2#D<2suMk0nFc+NqLۯVx/6;WVӗEvPw i2v b"%x]sq?;LE3}eC¢naY::0HzV Ow:ޒڄ"xUP"%!04-ouZxjWPt2D);Ar8%ӵT ٢Xvn1;j+/QVS:9);qjнXs#Y涰dڸb],(&[GwAe\ istH8(6+Jzп{|8Atս’ݎ:A/ZؔFdb㮐}Lfx~G8$mNߧ5+_wäؐ$!}Rؙ{S;?ןOi~:ץSS?5{S[?f~:ӳ])32-sWnXqF?=uYes7 @XX`>-V }U3)_[ k5/>&OU? ąZ`'Ɨqe<>6ީ2i(zd$$9BD2 *Ro>W ȽBۣ 6I/Kb[ԲP֣ 93l~ ;EKN-NZXiy_?=LC+f VɎ{ը\9Ze5l/ bbzMX2l̃BJJ7<&f#[z[<r&"S UޗG, ,K?BʄPh8е:Fc{yƑF# noGcɆYCWSzx&VU?wYIETeZfabxh"[;8HM,MV݋rC6z"hez/u73N ,Pwi~E,Xqb}xo.*ۻ 7oƞJβXK~P Z%铧%+FԖ_EorjFSf0Qkz6:Gdqmh>ƀVGqVl0/B!6U&,G#S^a3 ΅3zq9^KZVh~)oРסo-/BPk] =t:'!m<o4*L yC/ /o$u~[z `&bIrGyؘm^eMQ8˽Z& #|uXh7ѻ8%|RjYȬ0| W9Pq!,6D(U(+ DI%mkFn(bfN%n\!y38YwbghM&|ݹ8fob|!童Vp8D833QXq5sQ/OƬxz`+s@ڌ'2{7Mg ^_x'/]WxP:W|y 4`ͻ_ `ﺏrmA\su'na%Z~'1Nq3(i^ M/~|tNoqJo%]kOmp{R.}TsGFIVݡW\G\x%Rdp Ku7_ !?}߹;wퟄF`$\ 2%㤖x3+GZwRg8w.7fwQafb> ;aMAd܏z} #Ok̥M.wq /9Uʍt `Ka nJt^)kIh%7I*uT~0FĒ@tb? wq ʮ$P/p8tX/ìȹo b!'DLN{L?H9s$˴,(tM1KJA\@2_Vp t9=Aq=@!<-]Y?qV jk^sMv۠- GZ,"x: ĉuy,`k 5@o{#1Xp%azs!C((ُ88z#$oхf̄U){8,N\ E7.Zr *Քlt2y+Mo-tAe$OUCT: bQhA uiTos. 95k.Fڻ51 CjN3\@@d0" T'v|ۉs+F$[W4YY9x_OP db"vBrWa_Ɂx_Η,'w: 9?1]\IqTʷB\N9tl/JN39DiP] j7HozS;^nzXĞkDj0Qwŵ'ޯZEe%G)2hM3:6#M4%%RR8l _Gr_L/OM0")0\_Roۧ6F[Xښ%B*rb< {&Ky=vw] ;)I)D884)'γ {ђ8^<~D4SH$Z6MT%ba_uϨ`)+K52*JNGcI Dc@i)[}p/_¬7Bw5*1b,$k5O,ը=o?tղ}]wy/K&Wտi/l-2 #YNQ=Ct;}ZrTV_yIi2Ft)vjQ\kT*VZA`-4ꆯrFkX>im5}зq1Q!v(8H)0C~hN!k8JSušxK~%o'0G1sz #;Vʜ.H²X.焗xo}$5)PScK8N+R;%»7w$S 31X|'j`޶}LtİȲ|k{6Ǽf Tu%$#$[˓jXo?i?crgN9arg*J=lGZ <3-#$;}U7R}pHV +㙘ٙ *ԨnJ c|8H,xGOsf?a=?OT g#^*+A?ڐ:,*bs~DŅzԓ o;jo2H^ m8f{jq;uKoF?8;#8BOI᯹N$͐ěd(CE~#nZ :ez]aUX WK{ǽ+&G)NيCòP!orEjTyKs%q*JTo/ӕ"% WS3, +hjB\H~a"`xQSD0!ҴT/>bwd陠#\` Xc#O^[ vGڍj( ;;r2I#A:^FxbX3s CYWu(N. e+XdݹXoғ⎯ΫLZ Q*YW TAUd3Zj/%ѷo24!qi^ :wWc ➤7j͔溔+ C%ʉ**oРiVpd+/PX?K Ul=z'+ .|mR( 4ī6S}M޴Å 8 F932~^O_* 30r70B057FK|+etxɀ>T,7ɯ;MϤ%uaUtLW3^ˑOJ$3Mj 2dE|A֕}1R6fEv|Y0|U*OfT:=k4܃!qp }g,h mucin.y44uHE-n+-9e TKgQ!oG!^Xm ћ]ܝQA3AA$rt^Hr=KC@iF7m>pe7TH;ްc<@߳^ )2Q}?]xěaՅg4xtRVZšJ{ 0raKеC  A1Ëbŀi57Oy^ 1Ka.?>:hrutkq߀~xG#6l Ʉ~B QAr.ބ\0P?6!")]l99ЗN|#na+O !V-(`'q[ >.xp}X|8TG0Tƀy0nCSa?h#AG>|9UQ5䱕6,͞x%i<)iQ*- hl1XjGU}E,rةgTeVׂnc嬀졏l|z3D}z DZAl#>^хx(g]_`d3Tԧ}Tcت+*P!Ti=PsδK+e =MX"+>WG%e+5bNAhiz8l~zGe oo0|+wpZ1qזrbVI :+U3P2,"U3r~"<`GwñB%Ɋe=w!:PplMkNP{gI2|X-ԳAH\-h0$ɃxY_w d\CqZ1͗טZۢh[8Ŷ)>XǠm{UFNIQ*n018rӧo,K+xPB\sr(45;*ixEGRe^>.V:%i !0w+unXV $js(Zz:d:Tk FA>e pVھq];/l0= )OAlV $m53Q (cwGB3u%pU0i|}_bPW D\ɠ20A9 *s>Wh@g9?ݚcrQ4\L۠  s?)j[4_v"P…:ƌc@|q OdgF36jzm܁yROPJО\9PTGax1:<[M7D6hfs-αcc dIĔcc(R}b>O_iԄD5 JG}t.n@MbTS$[)x,ݘ0zF.$Y8ROPw;Ỳ(%<ސ-I&ff?e|~|s 9ZekIfI}`MtړvVY\m$ǂ(!Fp.9Mx^vk{GC۹PW{OBmLjᷕP =2N1:\V˒[^Sd !7q=j.gx KM}6H 3UX>`1wPSp9YSϞ,wsA$1H vFAFpxG~Q gh4Z2fgE_LXg&I5ή^{&o:@w|&r&V45% p-L~V)Ŵ8n"7a_4j6|2<Ӂ߬BӞ!,0pd&9`D7C8ٍ uϨ'[)[GE#qaFV{=.̓lP%}S k-[oa+R|>Ç1i?ߩP8軦7CE@"_ɸݝdw9)@Uwuð}Zg4|S/?iAqOY_'0R~V| ,ɯ'&(#U9V۾ }>[ݛ1Fi{Z(T6]0 A"[⸖K7#,LcRVĺ3."fnY|р)<ɴ.!J!QY@=A}+V-:kdjfo+2S6ֻM7}<* 8>Qa9v}H}B{ԖǧIUem"6vc|Ǹ @׃w{z ]uCnFJKfNg_T*QV֖זKm%=,$WX- ^<=Te\efH,+<WNd1Z/ Σ.:x-EMb`I 0ȩ`dɰIk N$UAJ$V| $0:(bـ{A-CQEU_>iYLWWgjٳUggEʭ*8q JiA,X2Us(Vg_f1"M~M2ܲCe\,)=.u12=Np%}=VRox:2_F>9iWoj+0@G0:jT:9MPg8jF4vy|/|eWC?gN<П1UtM>6|>û4FkoXv'Mp:WrGr& m䑉vsQQ/r<U?"WҐfR{n^_vk~\u||p[Gij)y5ڊZQG(+4@)[&]y3 +di9is{5}o{(`(f&r-5xv}?졞9cY7[( _dW+ôa@=*#{7BcC' #F!NxxWr`qG(.|Ku;m(N{wXlY$xĿq&᝭FxGZ J<O1Dh^&־f?b\urǔ0[mah;:Uk$Щ}}=ZBmi)vNkoM>SaPm T9ÂXc~dƵZct sȎg9OƄY)qz'g3̀o>}O6[+=m˧G.l7(#9`aL>| $| VݮVj ԋ=ͰmpeNXmҬSD<ā1ȅF0ԏ T߼P1KׇVb[ʺT+=<:!Ce plio0[__?jwi-OHI]kJƤZ JeurI?yOS+ō2$}dI78y<֏]hq|vHYMRS6T)j.cc_T:Sj;\d澅bx/MBE$ uPd};Rيyv2ZXoiP D ~Pcn<.9ԝ0d_eRM'Ww/,Uj뀗y)0Hߜ wuqjYts$#EUBx(3VĖ$xdvu~w6g>= ;fS(͠!n8:V.@mt# O2I'|XKsT|//SVLrnB^H?0Xҩiҥℑq遹?Lw͑v:V; ˴b;Os<nZ }*OâèsЀTu+ʖ ԴWڨM;-0i4:eHA5 j |E7^tT,>Er3@ڒR|AC y -?C &f"Qu'\OfI-?Y,v£c 0ޠ8CY34`0nl AGO-0Z qr>d1_@~~ˇ"w,3 ~jO39p9[wc@4.. hi$|DR>.A8]aT7F2\2' o%t Ŧc2VGZXtDzr]:~ߪ[PWm5MjTIW{ -*)zbqnCSI88};/#ꌒ;&8iUC4ѸE,^Sm'}I'\4+* a)Hr-׿ܯ[ cqh[WYk.^${W] =O7If6c'ҽ'yV}ل U%T(oQ̯ rsSNpRdxU؜?׸>}ue&wjZ/pij`Kt6Rh^cނ%5VVlA,M05gR9C}7Xk⒃iwtOu!4fTކeqOkQKIi-1)O*7%ކD-t=֘>dd[MKʇr'$fmX6RdG]6ncPZ<[O Rá Ӄ)M\˷iMΞ}o>x_OhY*ﭮ5"xg';KF(F&zyzA{ 0Y8I=7I(Fs!: G@2(fl+?-дxx۰&Hk1<}q7ڗS{5|~5e'X 8hXM1 SY͏2p}B<Ϲz]}to;wQ/AKu)^> __qjwIEÇp +`M|V3}X 7`'wvZ\`&$Ki"y\i@-Y4<;*bdZb@򎓷}Mwdq[`ϐHDǟR#P3J@-@\2tzɧv^ {dT{R.7KmtPyD+`ft,tbnH4 W vArxӞ|} &. ӎl͆GK՛ VEsm ־}[s9vik~W~+tjpwwG:|cM?\ Kzu7PCn3l;m$AkP\>"1lmINB 7u>⨛_278Ǵ1lxgUs0S/\퉲׸e/is㜠']8WbS)3I!g?]6Q#j[װ\–&/ݗ/j,Iσ&hO}T燥+_ci+/+wnq_`Fu,~wW8z"sр$>mI3m)[kK[{ttT:l}n@9ú:Xȣpxa1~<[W#`&ig|>+γ('pqԛdTW1@xbjQ]Q.KsR#i@Q^ Bx9H RfOR(Up;Vo9([7QMd@w)pOǓ吁|W %=eѐH=u>S[Z~j@x-fRX4]7(ʵeL8Vb^ #x&]ij{ƭwXOۓthM{{nj&&;[39uJT?Ue4HM ?^ z'LPt3(}0-?8#sJKU ?}) [=S;\hCʠNjP  e.O࿈`p|$!w!u*ԟVKuNWɁH(4Ϫ wC98L I⩍koڒ/A'V мB`}{bG$5fBo Ihi3HpxYsJ(|"%ZJ՞eJz chk [(0!SIv{ז:l2A ‰gHޘ'BLBʻ{Mw< ~HU 3f Q遤Vaq61]DCyO^L4= Kb>g2扜%MuSRఋ*ߟJLJ$b;paI5(_LH :M1f)p7`铨\ а,H;) TF:A Qƕ wK\ wFAn(Q7/-N42I fxn!Xe+"!@}O:Zۦ(tc5E-V=&8;1T@bo@-Rz3PEAdM d~﯇.`5w_-؝c314Eu..|4>cPx^=|:< RRgEyN7IC#Gj&y59m0kI$5ۺtpwIvȟ&VAx 'zeV, U@75qK[A>[89]qq٫ʰ ijG~ΏDyڥw~ԧq}y쭎{ hONSdj"q74E?hu_ϗAii+)Z8&/}1S0^X$xaB%)&Tr$:#0Q}ʛmdi5T$YZ3jz v.;lYFSg4+7ؔSW{\tN9tvt&$< `!=e\1hQ_rb|F;V6a=2][ z}s   QQmgJO%h!41$UrncXo70F3'%|9 uzq3Aoy{C1Xne4na.X9sBr=H?@SES|E-ou* I+9pY46-sw83s$9F;)t_n';L2RZTV\BJK;z/ಹ;2z^!]Q?ݹz5|=ıq QWC iD_1T(_6]~1 ٛeCE JY30"&Tn'ydAM#@ DE*ъ /•]![}i6?;LJ=/b"ԪWTwثb{cGTlr@+({y_6$,\/=-B&̐g=+KL*eiQs>]AKZNx%q]&!c5q4;wfّ\Z{q63?edbOX~NsriK N5ր?D˴ ƀ6.MKf0=x;Lr4hթi'*+&f@X} 2KY:64|8ց2v)s1 WY88` Divcsl`+ScfA>.,$tcӧKNoU+UqqSG!{^>:,+v-%! 5aJ҄TJA J WBHÃy%0!q1@viNBAx_a )0F,l^BD#&Y&r|1=gjzU>o!,T>E|%qԘ| _~,VjJV94W Nc5hvTkp1aVK]㿋{| :-kgs3LvT 1$E7bAByHps*G]cلG( *r.3Q\NX,sF,L+䲑)2I tr@Wg^av;,O##ٖoͺƕiT,V,Q1 ,k .(KS K BmqdlBG7"if]*iJ R=8M6EDǍn%Hp {pwm%L殈 r|# \}o19}aXT޵%T[2gu 'E-QOc6|&Ԋz|nI3<_DE̮\T;3}%34s5}Y6TjIhM*N]^AMz7|g{ ˲ri`&Udvoez[ dxYcTbPz*Da? ufR َP ̞ԣT6I/io) |FBԓOFӅFUTŭ Tn6]hڀA i~50D)[bq_'% :5AՇOM*knhMЦ [77,4'4"T=p[\ ULqZu U-րMz:J:d8c1=|mH"9b9?ioL5/`/y9ыzWT.@n 5=!]U1B*dFt簀 xZ Sgn[SoAY+̥ QEr@-鬜_8sBt׈ œ'N*R+_hB D4{ݏ,؇Oq?h'r Ef/c/fU PYVU@-=IojзA66MK_J!@3o4@} T5n{&:CThk5qQg}*F3W:ƣfrRL>d fe q#S hޞIɊEx|}IPج g4A_Ա5V\Rqm &~6V dQN k=zaˑe }QTlf Q@_gC֥7!2̓t*s>>λ'98M\'2v. 1师SZza4ckQ؏馊 š.Nd P%hG{p궁EAwU oB4>]L"kv- INK.cbu~G`׃{J}Vٻn<7 dG6\u3)Ete)"{כd__y%+}>ti =KZNtW(z^CNOit -DȀ s#Z!N j?rkAKҧ:\u= dd*DO7,7ގ>cPW(c4f- dDRCB%t v%CTk}Y~x?ށ}^MW07D;S2\"6eo̟^w}agW?&A!BZO#}m = ?ꡫ0 vp2EL>  x$̓9dBZv[ĂjtWC+; )yLKn}|Tu σݝV&y$wQ 6ºq!1: .Dieqmj`b@o͸^F12o;5i?Զz8PdmxWK9FZ;"j8 c&pL6Fg U"GTveyp;H;,ƒg>UVY'iC(XgQ_M׹q8uȷiooktvp4aBAb >0)T$Gd(s!pN#jM5u>8Kl; R'm;5mk B>XFQuy|GRcy_KA88*g_]DTlvY`3]AwPU%-Ґ acSKfN1}W{w]͏Z-WO>o{?Wﻒ> FCnN hǮd5"U4(YʋoJ^6^ *LI)Nj$2Dݨm/otg񈥁Hܻ#BwOECzp/?Ga"-M{% fE &Z yПD> (`1sKW&,S3?w;f=QN4łгޘQL A 8ӎ5/q'%D )dEq3[2Ek.g .df84`svs=\mu!PKZ:{>υ..c~Ťڂ>PZE.~? o[ڜG Nř~tUrɅ=t'?4A4p4vG^W;Ψ:ݒ%GJFdskZdz)LZ͔5ý1:{;pXvgoAyXiHDjMSfa;){--F6S:Aq"o Ҍea֥}b&ƶ+YPNQ!47;~V98LbU,7СB)6ήNwwcKBF4:~> HJR8lKuk[("ٳ)vOi];%ɨӻrIUxÄivTIޙLF$<VToh18.bxX8shpcH(Yo՜i0Ke0.q!pm TdX.tt$瓝tFr< PK92+| fP'yG*'2L#h2lQϛalC 6y /.Er#{j=|xR.U̫Gպ/|i#IR|/$R?&Š-ťHl={4OZv?A44`0堷_&Tɏh:|@Ujdz!8l&[wj{ك?ҽt3Ζsnh{}:w]Yo`'vo.Smaw7a[NWH/!;eV]^7_0¶LE6Bi7h] ',QOKMPiAjhe fb>|0FW,d| Cף!1@NzI|y$/rI|0vP=.wZu ͐$\oUdǽi^_\Q\~ Q c7K/ ?3~}T:,@q D RzjX?: ӷ~ -V8(=Uq="qIIhY:dvW4_y}Sfߝ]}S3P_'/TÛWy™vHZkho2!Zh胱AjX& 0.<ç>v BU~:X8c0YF.7XA9rq6b14j~!-`U)spJ:ZPOt IԣJԒ^jamil@\XLԢ$b}~"`>\JĶv#qj{-"$wRz\K1h*d|AKD/wǃp]Ec\D7XQx1!%gBH:N3Srbpf]4 UFh,аKjP%eZ38Sn^&ZlݟYOWvJ;7\va:xvþ s2sԠטI\Ռ(wq/iJճ^Q)̇+w_zWxgy6ª Ҧ'rB(ܔGckpqw-MW~?o$>(o{wq]ߑFFCCupm{w3檆9 VF6`M.9* Jw"@}obD*Hw) )i"'ś-KlT\nIROZ4{.M'@A UVa-QXQȧ]xlsҘCau,"}!Uõo᤮<S5y Vnr|"X=EN=hSܰrr9l/&?wGn {G> 6u'zr] :JմK,1fM~G w8: :뎙p\7@MTCyisn[ZƹɅ - \6Z-Ɠ 8$jԐf@ߌ*;˔y ^ܢO|D(qboЯm} KU1z ?np>LR B+d&r1WQC Xvn mc7Sbfì@N"<%59b"Tylpm3C'RUHʙ=ԆQp' uqmtEas_?lzR:lTb X,_r~C.@7斋J+P0Ô23 >1%Sz~ЕinUqЧO ]|cHbs7,ɛ o|ТEHG01tdЋ)12J=6(C/sssatVVtՔiâPdvOSYpoo5 Μ)yr3@ȯe\䥉%Ք!q1Lfb ֻ|I C{cz݋_:0a=~Y{}*|$0蹨K }h@(|Cjߥuo@p9ֲEδmffwϯBVڢInw0WًΊ\iɸ;2O)|B;6A*>7]H;2 HsgU-&S=(VFd5O+RvP}EpldNozLtbU)%O م.)U$Dj1qo5 jm0 JFo<5Y.|%T:eXleO%tDD)Vؗbq6^̪kDiϝw dfm*m YFՓbGqvǩלPB-`l O3<#6D-mWune&ܕYNt' w@#z{smQ^|-ȏ1[ ˂*-8+/5mOdz|zguy 4,9`.)Ԍ )0(DM)@#@HmGQ9DTIG5DVνb4j N"s}DZoIU {gA*@EɂަP#Z,WAOZ{)nRa>^l,̘Zw*f) *,L6"DuTB\U9ƴڔN]fQ)OMF>L/R*vF(U !rȗB[QV 4 njp8u_akP\,21vev۸6Ktv,'ZmKnIN?\DRX@bFP.?tW~ \.q]N&(h\ Nt:q0|J=wqΕ^o?$Џ;R)N{]lt71݌fPA #{/.x07]bxVZ!aL8:^}Rh(  PP1]|c f!15Oㅏ&Quuab)s xB>RxzVZʑxę:c/-_ّKFBxWy7]:ұdv+|j)} :/A7zw*}W)~w'{] xzFҖ`Ď c6|6B݊!leO'wSt8?8uB`ލx1S`!9'hn0g>ly,rW{͈#7F2$vm}ZmN VpVWu>cO^rh sBbCr^!Rmd=h/$y #t:/;}y&͜j]ԧ'}XAHt (.ϻ)6L(=,Tıɸ^~Q@ Z]r[_Z . 9NkduGSJ8-A"=9 n^EꕏWKG/P:S!w\ITu(ZU征tQ4ZuGٗ acHlpO~r4=靅2Z#*֥آJ+{fA=wN#Ѥ"߁l2S:B56T5^}OL{L=2 ?bӥ8(@\ DC!*RM~)4+t=>{%&~ly=<(s\BfAԏ6 ֪:&lr Ē 8u6~t$\ēy+izMI Y=:' 5dܝuXSf46YfWxʘ(y<xu28X4C)bbok!8/} iZȶ UV) 8ƨh;@A.'~m>Q Dr?RsX{/,śy~6-~gJÞĻ'@$q$E]g^#[ab H͒|trDC EħM@z2_fIl =!.#u[BVօ1̕S^ZM S.\$W!tRIhkdo GB:\j1Hm@ϫA>]UQq M mGTJԷs.}+G8(+BiItn i '[1twd4?GSu;s/V`L+L:;+3M~ikSDB{`"1S<N \OqapIxNS͎G=ApkT"5EGwhX`:`z[6m< Xi Z(:zc˂+dB?`IA@?Zoeo4 l֖,=xw@J"6>&' l@?%\ĖXԨP>nf wCuPG;orDbNn}~TɼMW]/ z[(9"/{(.=m/`Beu,N 9C: !=62͜B6CnpKVr VPD6;Zy qME`Cs ='PZPݯaG%QUQN X_u{_+Ve"B /=/73YN][{ ]!_d+{&CXw{uEޖo!۹`Y T;!ꄩ\ލ~p7~q|ʘ&FĆw@;3ZlU1iU,?ԅɬ7<7/6D/b:mWTr0tta!~(¢$)BUS'kYXρ>bYSL=]Mn<q8/ĨoI|XE~lNo/!k"Fx8I)իԈUbf 8GbGH1r:E9F|3H):PkSی8ۻgb2m0 忬A>O\5FH࠳@WyZzO~ڌ%.%5 mxxhH?w!#_JNq.ҟQ)5&WпrGd^Ig pd@t(sAQ<][߬h(Vc.Ed^IDNOcZ4$7\o5~%>+oi"&]+Lyl<Ӯ.ܯg^rBg[ V䋂!UC"z9j_QS#g?hH컌K٦P Qn^F·mUBvӝ hH/ ' { 8D* ۡtq&5ۭO"0/p9U9vLwl O.{_2, ":YAS=ݸjFX_O:LLbwtFb7\Wr0SV9jYM#!>{D& &d>ρ4 D-_~yOn6>lrMi/x}[av 1é?<|WC,)4&\>\7-'@)" &!OSE/auFt0bb@;k/rW>?ؿ9^?[o/O_.du<"/*v]޸:|Q d z9 lt-8|%K,0Y :nITg_Ul_<@٫SKgl8.G&n_W;ĥY r(]-a֛5I/0q)/Bx_4a,$1Ȋb!7Zx,%PM :P̔u>;q6ݛ)Jb>U#ˁSM'.-F4wZ-SlpWNHMUba_HcS5U.JF.M3keܲٛKrݐ Z-)&IE^ע Ebu<oX)PmpOZ_P6@سK~sϪD&ǀhT;~ߌ1h#5Pmt1c4!8]c`1H@1`pTc:'NLᯬMT<Ώht{v>q4 LX#ߦEӆ#8>;w`C!Bn#ezwC"xOn[Ai;+5)Xz˪ScxTnHjZ< n ۂ`SUsZQ`y_؂uSKłT/ίc>nʝ:{Lr U(eQeUپ] c B6~BoN|UH7 Xnl<u<̉%}\k)W+0=*wp^7EvM'8==О`܌fyw׼n_/w oȊ'8/у4xc7wek־Xv ǀW{ leSZj`t[R6oSц:{5{yoxc}}kY/}q8o`!{MEK=R? ̒k^~"F(3%{QA_40.F7|C1|ܳw'Ta>ejWijYݽ8 7&sj_hBU$ I!_MDpP̛qr GTG2YYѲ?ryǺp/&DAȝ,P9n G"-_%見h5 ncFeqFg*D%#̹фy4ē{*17!LUFK~iou!a2ۧhGB΃碄Wu:o4,k2a̡{^7/'V2qS5| ߦ?Fw,!+ 2FY vLDY~[,ĺ!2&tWESR0ub 1;|xӒTPC "@ɋt,[G*yT"$0MsU#"MK1wIFaZ ^1e6Z8lEg#St&pYAmTWT4;3=x,i])Y?Ԯv^5n]/Մ| I%+t֢Y fFdo ALNIDBgɂp/nY)i(6K@=&@JÓ8>>Vۅ4ᴳ9χ%'p/xɠ>%CTrRw"Nt;nNz H`e+{{^E-x7^@??@@1V{<I L*8U_{{ o%+t8 PXֽ̍ ?#t14qޓQao$MZҡl*)@43C8z')U} 'H*~H.U)ָk39]b|U2Ԃ>FlmIK3ұpV|֎Rk 4o Tlz@cZFD$lUqbu$HjF(& Wb$[RNBbDA72`(BƬQ0}1qW5?m,*xnW" 5[2~R7_<E4fTKxa, Ub*2044lvSZd 1 Ό wQPU|ە$7etׅAmR 7m](Ep\ɺ w`xhfbA|ΑWIzʊ$] Bi-Yx h;.Ͱ{eK]8cb]:>G]g֊]@5F>;9+k!SFT|viGGDq 1bFN6e=.]rF;|*PF X CP%LBr PM֙cFLHS2k2;YQF)lrc-:g8(y:c`S<"K<[񷰈g0~& |G>ޖEj̺uv py93xц, vJr1'$4S Uׇ|$D:0(B#"1|#jԝi޳eW+:vIH'`u@ƹz7v|#q5\44nZC(S09`cvFxݓFy+Rnr{9)%qvdZ@dq5^J>K: vC!hKK`8TÿEh .9A*.S9C7FI['P(fpogeÐ$$/噬U%+vF6=#IhS#OU`mj=cG43j]xƣ[nF39&&!{XѮg[('eƑ!.o#רuvpA=nSϟX`ŶEsC}pC[n#V"L@ٸd&"}×!'?k0^Vr@*qowSx6 >*hQ nD\9VuԳ)'c#"RrmGL-V+`pMD1U_wSӹUCk^gL"wPjVUIiCorI0~S~Mz! m@9 -he2/!`Eu'da,Yi8aJUFoΠ"Ƅ? 9)O=Hc~*dvY9.DtYIL+V1[l;".^?%U|3C[^,I7RA$#{1,U=wW2YT°FSZhHbc}|=>gwq-ľn[ OCg%(EF L knੁ-Z>/|`wsu[wVc<UmspvԑN~cI}Ɍ 5A>KEat<%@RIap4OS8e!S.*Q5,`CvN' %uWq>ruc;wH׌G"*,$mgl%gDE C㝭-O^ Y.0b2 >.D!KlbT[<_~Gӏ y#Dusʒsy8q`1 }M)[q#'~2fVzК끧p bCtĂiշFg@jTH+-F7pekFkY|x#.=KxX`_QHڏ&E|"B+)\Bm`A4$ l]fu7)iŐޒ>gKu=>w a||s)=*84ހ,Ci*UHr_jxL2VA$1eyḤ޳TP6c؛qc,j2f0ip,輴aG^ ԰"ɤ%BCyۀjGd= wT]$*&_<lhY>72jܦF?ok"oS̛/QRK!N3|$nhl3%+_Is,Vo2$:tۻߥMofC+l(VY-08e5f9n[c{:].)F3՛1O<$,*Up.w[ĒmFG0z"L=7+- )b<'r:g0Mg|a,jo.?HĊ.^O7-/ >`5H1 \[p̯}a|Y(n~|G^pH/OZrՊKx.J}Zo6`[/cu9/"J)-$kyD\AX< |Rz 3I`+Y" g)Ƨj X̠Ѳsq&:pGOFj|D0ͺp.[!=be-F{bQ!L4[QWe3Oƴk@&Y!z$.L;\s^V{ ajM7& PF<_WQ. XW>uv߱\G4kȭM#|K噚nסUIpQ̻:yAx9c%L;8b j+7#{P%pv'xR\E]MfQU>E6s7"Z[!)$<Ջ NO8'ahDvcF86?\04F@mUFՒSŨ@5CTzXRH4lȅG00eT&4|QbUP ueo]-:㟿?vn瓑) v% ]Ceyt˞![I:/jOqޭd Bh*鷳Q$Woۅ6ApͅY,dk}bu gѡ7AoP7%+HY=&+;ޒf}azZ>ۀ/)Vf0&)KHGE@]rMCn Aٷ>I>zէcA޶IDRkr&+GD[AM#u m4myYMD?N*Oyat (2nš="P7b/rŬ6i:M0,aGT<{I {Nq&,Eۮgܑb#Fqb #bf,Z׻7e\n|~ Px\ZxK ,Rjϝ&ᝫ=X f_ Lo$H`p>Oy .d),żcHp>kVa~l#˩xƾ3i3zzՏJαש, (Z\ϴifXJDݢ@{=fl@j}")'ca9ֹ׸$C\Ou++MʶdHΰݬPQ]|]d1P@O+l(†)Pa6?+,aݜ=nD]nw|›MҒ꺽v18pC}(՞ѫ9Ăe 6!V6g8Xj)QX> PTXS!XU5 -=A)cïYG&2+[ӺS vIygC}6M[ҶWE=#戀y5ktw9&INMg7]M?~[5%zkAO=YM&yQy+hVJ[a&+G0nkw+m4v3;:)v:Ht28My>i`b:D7-sVIzi=TgEd|S[An;+--va_n}E\wfHr>"aٵy#Ά"`bwNNAxc]¥Թ?A̰^.N߀JwޑOk;6mbUV"'-*&} q0pXe6HB)kh8a'2Ġf,\Y{8An6[Ybw=LoʨkҺ[_ͼw{FT)AQ:@> {;}ϴOD}XyŝnIW|BwjBW|QL!Iq;OB9m?:?hHxš&!ڄ%'@t^R@ʻXbHp0z}2թA֎My]Z1Fl+qxKQ22}[  .m4tIGhlp)B•H.XlݐD%lץ̜DibmsRFy-W)|EhG!znL(4XP2!tJ A)F6+ƱOaN/c4[#byX YvRa8Px N|P ]V)raZ^@L h3%(w[wMQ^8m +8t$v{J̹{}s鶀kAZ+]@AHyz nPЦnc)Ρ.'vW|R:1IBvM[dNO<>f{lX)\ctd_G/ \hL!x.&: $nb!AfY>I6 O}}M/O`3;_ {Ҭna/AQ!@QAd^.-^ AZl}@쨭*/Wd"֭ DvY d,@7jTrl:0 tt=cb6d ,{YӲ:7!i\U`0_,[k$ =W# ! S͇6g !.yI4NGxW=B?$,WV1¾.*980ɏlo3ɳa qE󖮚'H 9`jغz=xO6*uƋn0zM>E"е YV yk4u~?Ju7=$5j c ͇>]5 5ZZi+yQ}.d Ol51WC*LݟOE͜^Ι%*≭Aҧ-Td} Jzf $S(+7nXfBIK~T4aMW-EV`3O0tɆ.;1dJڨr'@6Ed@͊G-HQ;bo)R= stoa&˽ ˏ W 3 zotz`uSδۨ\e 0o+^&-: U$pAe}Fz HS)lɓJ*I*5C J;nLMh=[Ȱsj?iKIMԾ.˟K1~k>kS,z8s҃ F-BȒmexqqڮ\=rH=EM~h6C:&>꺉@<#I%kaD(:-N% NLqb)ֽDVR[P@ `4Z ְ"bn v ;l4K[b[:dn>_A/ |6¿:xpחC0Qb{~01[qOc8lQ=Duwbrϧ(йIne/s#tA?J@E,H{nEF?oSKYwꑡWCi#$ ,ZmK)k@+I(E*[Y<#Xr@Ccң8ncC`vp\M+t-YVմ%˹uE胈!3Z6(WsjRSCfqA>Lk5%N6؅Y=NKU閱,va8|%2m@dLBC(N7HM%\~i PMBe PEhKyIR>a2dg=C^7zR9--&guk60+Cz ݰu䊖݅ :]:uDޭMM}2Sn8d5s*pzʊ>OaIv]g M^KFk;)t,́:x|J:q c}?az7ה &7ϲTVS>8V+ۚ)#Pu4y{Mnr)2{MDzn+م[6Nyy: Ƚ7CIwf-P"E0ԩw-q)9N<:u>y0/)Q!Kb$tczJ6t)EWP.(P~^BsHvUU͕-4vnf_{[<r>X,Mgg]q6!hRKMDHߡDBM/ΏBIu7\2D&"kDtTdfC$@6cwuEhp۩Lrq͒yUզW_{}3໸}:"ޚT4QVtJsjo.F?`ee+x:5 RL_gwyQ vNBGcvƭjVWr4Ɓ?fCX ˽d8$/6QheƖY!|sb/E, U1CFׯ:W:kU$ɨsR1h 3RPb;y@e8/DWQIdSF(,$Ձ/(a gm$I^09g=*=6=n1tv(އ/w!TǔwXfƌhxS |ƇU|VY$sZ˗]D!:) xv;{A&wiɞpAMΊݘ۩6cnc=,E׈{EbOE0ŝXױ_̸b CҏRAHL`'dSiLu-Fr`$˓;`Tx䟲9zꆶhEUVԺ\UvMJn] ?;7cQ/k6w7}Iv V}Jj+!޽.B: 䎗eMr =}|p!<ߍn-N㷏wwE-#4E/9 7Ku(+]  K2 5y+=Da6{]5yTf#qr(adU;@׀={4dwU(bev (w"w|x-=(|hpփ ;(Ef:O<.,an1~L_ûHtߧwk,뿃Oϛߌ&O=d;``L}>fp|WKR3`\('6)F5Iq}gn hBf} p{P${w/+`xG-&wNc V!ݱw@$'_>㈠r7̗bg=y=xw#o0ÛKpp7ͬct0}Fwʍ.{ׯ=v6c:]Z|1O.d^ȇtFJعZ+xqyDID8|<J)_fj_PJ ֭ e1/rڈ6x DG_H l5)SsQzU1fMk\H``M#g\~ᔝYg՚з:= fQyx*$E$90mX2ʄbRLY#Xwz\zPPy,ZT˖_o^a] Bl=gO'޸\NdNY~ӦX{ a7n.fCOZZks5Q8y]n8'X]zqp]%^VvS:R<PxT=#j)yޓeэmR?M|[2L)QO|=$78Kr#H?v]O{͆"/559|M9t F;uDrk·W/@'LyI/^5HEn? A7dq#'uY|t.krme='ayS/pUξ8ضSƅB;$MX6E[f z Up7KJ QJkV'{](*Oՠ"Lplėb:q݅i:=9Xy)fﻔdx;rGb%ScF0oȞX]Br #SpSFenHKmhzͿ \qʆJLS€k]!4ٷ!.Rr?ɴ)ٕIw 0FHBAT^Cސf4ud( Lh.vIN?*Ÿ{ZA&ݯ=YF_⡿z5k7cb[JRv%a҂q`ta0}I˸tg[3o_ 9=/T{V1n d@h$89I,x,KL@-T)ݩKKڋJ3PO1}=o#HtՋ^ں{Pz#znd ڻîS(}s[ ̤P kN#Pij{}Kh6y aY\6(/AF\эJ/@ʶFFxQ h`- Zp1?xc ;ox1H4x 8z3>L8;qt ~6VfN yɃ|I$ ,V|:K~ xh>,noq;ݏJu z0[p2/x_/>znL >/6uz3mZ%pCŨ<)'qA:Nͦu"ZkZ1 i*෦% 10}.<9\- ǢvwM' ,TMT=d_1k{~(@_Ĵ|e:;a UaQ<ٔ9~lp5w` *)'Ed|MZ銾M!s粁}Mݑ5~9n0 NZܩk;} uq|O\'+&J4>Wvڅk;_|4|aw Ãl;X>ܼtz׭hiѝ(P/d&NOՋ\G:Pp6~THA {$NxGMFw:~pxWܾ1'w>ѩKXd[x" $K]AHU~CK˻t~]ќ x>';O|^FVHpNCo[ݥ% ~2ō$_F~:˜2( xp7{c<|FCagT=Ɠ?7nև} nDZ]ş$VE]@* |ܝ0^\ϩx*WO}RD  ˢu,VsS8ƶ`t(@\]-\FλA|ީWc)f>x mfPFД*V)6#kU"WP1BmAn6?_ͲT!0]'XR 8x ] ]qM)R|uS[#ߒ ?䃙W6)^Wzzslnut1g)9p; 8n 7`aR`0ݕO{)ʰ|ϧ[?mΥdͫQdc2m<f,ķnϘ{p )#k a3xnwq;M9 f}20L#c27|1u'`hE kyH֦ tEC,kW#o0`ln׸k+as9ht E$ࢮ>$[`#FK|"}n?ƒJJ`ck]+1`K)5$ҘNp}-}00۰~ ez*`pE{JQ.`_ЯȊxTbI}=Ԗ@ W2V.  a/F(Mgl ZFi9'[b}ftN4mnpOY^TeqI{> GXJ f9uJ(qg}w\RZNs6V^ugV>f(}跒nTmӂЀr3x1M7s|VFITL+'fB"l%w'p)D82@E*@!OG?OPz·Hk2@T6EMO/Ӱ%3s()S>'BfkgU+ݲipa>I8wt$!5풣m?BVcx[+Ȇ'M>D*Dv8)#ʀ Kپd5ɊDv(Mqtbvp?ӬWOFqf䜲ѺÉ]U$[A6&92( 94W_qdf [YSDk޸wX=tǘݙtuۅ2xP` )Cz&slj\z'7/v݅O_M_(W޹)ogkq !в rcf0v54)ܖNSd )zI'궤ӤNӶi#ήWaKG]z)K¶{~),eEʟv2ՆY_/[N\5桔$ސi%,FC]*E~@Btwň"xMXZo?  $䀚3ͧьJgN[`@n7HƇ?5':|e`F Rf`d/~Pd~7`C޻pcI>J F.n:GS,~hrZSG4UtO>bF X*v1L s7eYY=?j֭ۑ/LBSLޕzl3׬ݜ8aŅ_Jq MV^95*==zH:P^ Qq d%Fn7!b~Z|Gy&ФX&WuKQ&d"{_ .fMh1/H#F+ Ag_s/Z5`¼ӎ!w\Z!9U[t|HؾVO@{{sNYF(M?DWEF }(낏SeZƟF_ŷqr3uy[NsMyeԧ˨/>w*Y?zB2^z%[2SC /TvV8JV &#⏺τ||8ʿSn Xb=!< A$ֵ_4GH8p"Q^AÖSKX!;8o; sLgT /HI\TX"Șב~XG](DQ%[Xpo8C%!y p:X6eE&U.-9 mZJ'qG<N2fiEO_!>^K>wV;^aD(FyZН xƶJ;0œS- xLl>ɥ !/5ӨIIoJSP)2Fd˭il pTUסQ!D>_i8m{Ē]s<8e"O\l5 Gz aM\0IT ^]G-=}1 OF|MTתL rL$PjXX4b? E24Elӗ\zd:c !  ?D ).8)uЍ!tw 1 (AϷq6:p|DP oR"GH=& kۑ)n:CφDku߶peoeu~0#؎'g`6_kͿqz pg!w׌G)q!4?lx|9Q3Sp芘;-fJq7Օnf{םw?:ԸYR (}4Cv -lAZǯzS]1w/Лfz[ϼ]p0# m~:0ł}n>'зv@h%- SHHIsXe2*tW&6 FâŧQG[dzln F#hT ["pŊ?/Ci5ڞtmLB~3 @ч@?Exǀ z ^Kl5Brx-Ƒ1fEBb>B\[~ϟ-ha)X<Nm󓁘20Z* }zh.2yPk~pcKm Ӓ09cF%T8h%dE8Cy:'P55ԗSİ0//Zŧ-/@.I"W5L G;_3(b;'c8epߜ`2 i+؀jT cq)œ^8 obMtv9݆Tyޗa&jJ 3Se.~pFZ}UZ|VԖI5J e3z 盤>y x>n;~ab& .|/ǟ}qw\mD Wmnپ^ FX4E.#l@bKÖپ%zDK ssDt-)H9$pP.42X& TA@Q6Pi!-Ϊ%WI[az3 KC/~Ѽ9~& [CiEug[T@XH`"P>E~Qؑ텯^rx} c5>SDh*>$ܷ5:⌹+ Mδ g?܎ٱS? >ެ]tْ/bJN"̋oC܎l*v@|^)ic\3 si@;ܦMCieJ~6^**pb!nW 8Cܛ< )jGk|n`!DNE'h w6µaOSRv Zz8rϱҫ\ⰆqR,d}蕚jH/a VL2ݺkåG~%Og՜bϸ1[bǰ8{?K ,dCTfFZ@Xwm(QzG9$$"D`N.v`ф8{jyV|LI`p~ vqp w2 ݇wP*WA_nxd?pRJQ{2#Z,GIaSx4G_N:<07t-Eae>2.b> 'IR ^dTxX=9-e1(wRl$oRE 줢(h8t Iஂy7GcY5$Q ~ 1N17IrY!Gd$琠Hp|`]ΰQ}!i'1ơ  ֙.闂©s^ͮX=6Ʋ" i.Uf(>הrtmčIiM_%WʈlY]RCDS"9׃4n1{ c!|M[@:pSGTb7"vwCKbЦLi +L0ө R= 㦐Rxr!Itk S`u3~Gg-EM4bHl 'l7!3YEZuv"..}=)w\~Lza 잶=*ҥ0cK* iEȅ8>ed3ۓJ9Q]RA~樻LSe =%8J}H qQ_w ~eFF"gcJWKyqtVr"s4Ȇ_U_H%(c(FZ'=Ob!R_"Nlي+ЕrS?'D6s;1t`$59tgGZض62үaNslxI*0&ȥc~d+Fx=wlqu?tvƬ4#miCQOZPD~Bn9r਀u.+y$'p"4g{kǃ;wVdd#KNL!@|A[!+7Rl5&-ΗCBw\$i'z1J| aUrW87:-3 y.BwAxCg]lv\ _g<=u!l} F‘I.MyA~/qN櫶boyT.vБ] Vc )kf @ܝ e,q`qm[#BGANdocu)u\| %CGHӛ&mN.GwȐ:$AS 4@%!WKO\ұPxn$Bo5 D"Qfjl\_ίf2iZk53LA پyu"dSa&iy\PMRe2[)9#LtK.](f+ªZQm īxrI=/703ic"my^v D}vk#[đw1cS]0cR>={='TFL"13]M=0zA/@ TnH/KM2vל,7GzCbC%_ Y^8 her!~x7W*{H<RMzF>Md#\߃W*(ja`KF]2^3簩6G\il: &EXݩqo=~@{s,@V!dbH0jLNJG%cz/KO2{y/l?ܓ((NRm ڲT|f#x$K`To F>L `^,9LIFjy.60Jξ6HB/ YcbE4M$݂< 16мa\yyH-#9=|Z]8\@ޟ"YR?LU}zki:2Gaz=Fvᐐ00K(*O">p? }tv_ ^b(ܫMؗpCF?3>SsRՏbW iB B8aIq/?!c]CjܘP ㉤G~aq@Sy%p&'vpi\l|Su@>p~7Y! -ߣ!Y"hO"8#a4h=Bo6)d>Bۧxb'œ;0v?`rlj^U&4-K𤵎ޤ9r͎@i;}Z@xd5ߎJ:Od['w"D UYz0:}@A"UOB]#e f^f"iŇ #T)b$MՁ5)[xOdkVqX q7sl9[ YwI_ܢ`daW)J23-T!u-?ɕY n`[MrχbΈdWRuUƏp'RU.|;xA%kwJ:J%ߓU϶8CCO:ƀӇ6imuIYЧ.(}D5WISv\%]|:JiQvU˯]~j߯?E_R1:uRI]'c~.q juңI x@G=zn^wgǤI)-SO[;xu|oyz.h(Ѻ>2d /F =B,dWv 諏7ܣ?%Iwμl_.-~m_n_O6\',= ǥk~V`ܧwpGUCr^p E&tDm4W;|ލ0K2r*J#%\]ح;>|t@w@W zY=/]JO5`ˍU0?߮O`z%6\O)p!kAu?%kCIkzFɉs=h9EҞ._)'18ĸxc4݈}55l`;P WxeeWkS^)%qGX_hܤ{ SI=De'I$Ǻ\ ([_YTXD.wc s /%$%Uo, ݱ=Jըv%XT_ Ykzx}Ͽ[]v)3|#n;~;|9(C4Sٱ ny+QFja x\t"m˨hVOuD7 'CCn˱Y X!xqdl@kT7-aݱ]N9C.sD~[1yk|ǖ䮒Mk#̋ C|&%A7 J)Za'4 Wߴˌ?J؂$>HM[W@>ZOVMɡ5M- "lG\az\3--sgtRenkF òa=@WtyWvFTABUEBڌкvZ!@ZneVLA`I R<•=4M^!"rݲ8 )[ Cɦ4Q>v|ȇE>6]DPU #h6x1@6:lxŮ)cFY"ه'w2]DWi{8_Ϻ኉P`ϯ5Eq`>nLlpl<.>_3k+ n<(Q_g*?6ۃ;ݟSOv:-섇j(TwbzRaUWkm Q3"qv\Խx#GCݽfUɗ1)yM$hk!R _P(o96k凙 !.hat8W%B 3+5ٶ0?QgRag̈fFbjݕ]w3&zR hS&)lVfnPuYg~x qǜ2u㎭]MEM.DZp,rL/ ր>b(R˺ 0mWY3Ծqٮy/aN0ܺh )I))%_Is?^x37b5:F! 79ԆmM'SV$ VsJV vdշl[@OOtZgps?x ~z3e1V-;xw(܃kXaa qe$/ VZoǭJN/u1cv|yEVlߌ!%0l0|xXpLV:ujÂOL MnL 12ˀL ?:_߯2y=Y8^êт1'ߴ=$|'u.yl XU*"̨ր'Y&jiY@&΍aInA%C@5s`[d~i;,"lZ@=]}L է:>Y Mϊ~v :]{I}`ςk9UP h m\H͒({ʷwD x &+m.`,F{Sh _G^kH[l 6SM"n/Ztl4xe _r\mz_ ⨠ׅ7Ժ'KRică;Ǫ#W7*-QbrPEAf:`x<܍]ɗ1q?%h`(+XãY+D85Ro6GH+.q=R,O83~nw 8a?=S9r̤=է||Ӂ<'lsLuۯEn\Ϲg$Snw0NCpc (8ހ\3D&E@}rwFϝdn.H([}JNhy!䍻ޯmbbțK$؆T7&h.d)ab{"]>:%&:i=У1j_0"t*1bKˣAu[ OXN1vnu7p7˞zYU+eOfll؂h'lm>Bns>2l Rև f&[ZnHן)x1Qhة"b_!6H0Ŗ f>uegŠ>E%9EHv51Z6ڔ0oRaEُ;C#n|S<2Kr Dp {P0c)u4ISZXhUƩ7ViaiRIf,wv"Hq3}E7:FHcHߝ5.?f 8Q$m9/)k[#SXL$؅:rmb[[2U~ {^j6s,3~xm+,a^MY> $΅2dǰ6ICKdFa$1\F$./+$>ld=] [ 5&b2AN$4c<2X(8Ғ2 l~ŧ3'װNXGJ$jݡMD~uk\@1څ _MJr'ULud̝}(l hSMe'  s7RVCT| s*)S陔,r\@g(t"m@2Jޱ3=RӺuɯX>ŝx47Of, zI=fǦr;'S-a=r'6wpS¼BS͢h_C!Y6ڭ)"5 &u6B9E$Ӽ0jծ˾;<%CT4y tpt5~MaIszRPQ#jsu`^@5qv*IiKZ>7lۅqA]EK ȱ)|"-<*V3 zbwϱfa>C8dILg#eaw&-qڢ3ݚ嫻d.iZ0%~qVE*!@CH]l 0oTIKp&ԛ&Ur%GB}QՁ:\Iz z9"Ԧ\0PVbg\Eo$iiޑޥ8)a\2@DD1GMan\c'^ӓԋB`X'ѓ.(sY;ʍ}rg}u'ܛ|:̈́d!v`kŀb.a N(`o nbU6@b k; &䴦/wQAGύ`Hy$ ׄ~?%rׅ.7yHc9変wcsx)7-cM7 b)6ʃC!J/Es̠QSؕI5u_:~Fْ ṏ+l;ACifnK+JF "  kl1QPW 2eL-ep$kXg;1b]y)NpP3ւe3 AW)Ȫo`I ;“ w\MalKcGm٥>@xP!HR񰍓x*["%ދ cһs~ݚ f丕 Q;|k{Xu;[ "[;f"&-kA#켝͚{8I2QhaxKɬXHBOaa]Ŭ=ws8dǯ>7n-'i!#ZN[ ;r t`;q{t#;S7j(O8]K1Z1<I3R=鵀?' h6s";<DCMkl̸vUV@uB41} L9{$= /R}4HSڬnޞH.Ʋ7@S^t}@ό6ʓnY(GPLQ#|)Ҋ"2aK6ts~!r ?=Gu*n)*L$-|A=WT+{?IX/vE޽Tl0*qF(V*t%ҍG@"CN!;oç= %X,UHzX39;h!r]鑤$(AVZrkc7E%?xN`I'03J!9 JhC2<N/˨hp%b< N︢)&q: _F-ucNaXk:apc;8R(&9(;_BK;5e lXSv=&>\ٙblX=Q#!a 7iM`oRIw4gGȌY,MR\zud]I= 8i(ȑB"i"@P5QmjKR)!y#Cƌ%`w|P4*{h?FDa~*^Qm* &]4eyO/N-z Kl/e9Da&8OTԻO `i#$:E 4I?b"DYaAXr!ьM@H@:$TcK@5\%ri􃹑 0r$)}\\ ڋ8fJQ[,Uz ?LOxT6}tM@/Qb]//&=udهߓmD|ͻTw@/!'Pq^4mi¾"dk "nx &((AުEԇb[2^g%/ @bDէXm&~#c3zu-[\ZdhFPS:yI<W!OZޛYX7:2Y yaLzX3ባ `_~]ں",]%ܓfӺɘ72 19v7 b_UEҟ\m%-'ix_'R@ 'Vv= Ҳܪcہ^ Lm D ץx]$rg>hՇky=Q0;iE!-?>=| #2O}PNw}|~ _0B˸ُK U_ǏKqw sO?/wn`>qw }j??pss? PxMPҁaEl*\ۍ!uȫ1O<ݝQl23T8ݍsLG!8*GF)mpz!dyGA;_<&>y#MZ=(('pG}"ii!3nEpxz U/TayG$up JÙ# $ն)'u؋CъwVYyy( {H}yy`mz)vg (Rӂ9_R6m0|݁7$h&xyE dHϡᅡx. fS A<+ްq}g#/d#n{ }qX˔c,Dczl~D%XHGE؁sm_OV#2"$;-O2, ;͖^{u s$]MR썄#k3$ϟEF;lxiZM{wUhߪ~oU? mN> 8߂!湒`w5 ,iZt8qtrE,O%3G"7.\hkie f Xv|.e/I)i/2f~!^)g]wj tL: >P&HqlOdyJsH0OMVI.FƇ^ʦͦ$GI=` nϾE0b&ǢLKzF|pAπ);%cc&h'ox¨C{by3tpuPx-uٙu`ؐ1pBԎ#xڋOUţLmc9CR+WC,``2ze8 'Lt<[k/sx*lP2)tR,-b۪@2#e%xOcyz'vW"^τw+#a~b٘Pdȩzc*D+o;N ܥiH@rdEԸMU -/&R ƴԿVQUm 5EÉu6B'Nai>OcP]ILjj@LTI  !8IĪgsS] H A+oB5^ Eb,Cdd៏c<l.,#P%^;\:s<,)BͱL(Q tRh3t7mb-O;tY%65|46{>k0p06 ݗ|vp},n{EQ TIfC詝nB d;DRgā]10ju^SZՁ!Sx|BRB9dQ\LEr>`aOGru)c+kщrB+2";ư`݂byޅ2n] e@ȁycY\$F}Vۥ&d>M#Ύd-^I\~hN6:H X ddæf|8a.>( b'a|^=h2),#Q#i~{7:W|:;n4p0 w. xJYbgT΋P&Wy`l !wkoj1j?7&w FRZϟC^怠Ң̜UbZŧPA"&w̐ݑɬzM@y.&03ulx!`h1"[A`'S=m $nc-vGK"fػ%U 0n~Mdtp#{}*.-kˋ|qUG= PA"*' bGVg.;C. W*Gx'| dcwu;8s>NɿS:-#w?!<} p>;VK߁d˶V9O9{Iǖ|l@-uQM$^x",_kvy]:i5CAl'$ V4BfpN1H\3ST#=O'oZ8<BJ(?%B94,z w{)"qUMt$ʦ0;nflBSnf\gqȄew A OO?:ijfЅ!1xd|DP9h!  w!kDi|6DrC^p'(H?9}X"DAHt1,ۅ3efuf}(ߔ6V091)/)QSN_7|D~pLQg:bl%ͺ"v>s2܊]>Nh6dddAD*ɉ(91Fˤv T|,*^-JL yѕÑf(èݛJ3f7q<GY(Gd0-bS]aq?nWNOTٜY-q)>Ex2T{Wz`$y ̙=LUTKيz-&sPI{=7Fc7b׹̘h/> 9Lߑ u05'R#\4kg \Cd3DvTTf|_5iz)-h_mhYNk.NJ)LiXH@ZPEY:oMBX f_G@$H O)D};^n2B)r[WKYh^GK3:p>1cn`>zK/qRb6m1˭X"eD4%$0^Ĩg:)x&K82 -b*̼³ N~5qU7bRjwZ̎X<${٧InbFFKX軦0L `Lxl z]hӂv8\X% ?Jakn'T-n@fXZ% Z6US-AdbSV&oy6uE1'7En||fGRz~q[+qtznUkPaw:Ƞ,9ͅ؈u| y]qzO@Po1?iS '@iy')3e [{,[4|哬TmBߴ[p4 C^ӓ>{" Vzkݛ5Cq.)/5Gqq|!yHtSЪ/޺4SF'`DYsAr".@0«/QM7q_ yw}|Wn~wY9=$'[>ԕS'LFSќx? O4Qe-shge7iGǓI >WD?$ >ܾ"֎d끁)Q:4{"&w8FT!R&N2ѭp.'M3+= 16S$*ˑ8Peנ Δ_g q0Pu\vb,TmhLJy&'Y{]E`熠ܖGi?DB{9 ?zlq:n m8y;Cj#B77b*QtgG,o %#=af LUGU4lmTntz;g"f}*<+b+Iw5/-:~HĔAhsNWХCDp\:v~DekĪmԝ[ZL670^gۭJD:FBlղI/ϒS5Jc.z>ulZ8y6Ylef ,l@%L@>Q{6Nn7'oKyGgR^!X֛0$N9Mh KL [M;@V؏@&Jd˙ZvDQ ѐTdq&R^0@WJru1oz:3uݜ{ۂ]4'c2cl|` յ\3^ꠡUr玣ssP$:*CBЅC[nw7B~#yE|G';gq3<9[.Fd0Ǔ1lDp?9pEms%h!_h奅^7#¥0O|hf ice2MwIP"I"cJgdV'\b 6αF23b`Rp{f1, 5 t+$@d~On׻ ?}.&wƺ Dv}3xne!0ɄكKvWѲL{9~4di5*!;;{|~feoD,IG'7`W9=G.**LEUˀ#~c\ٷ+*R{?^mCpKI:!R?Д; G[2 )Қ0 Kkm`k# -FsDK),3EM46ʹAA+x_}|-1r_6LǏs o4&gNߙ+pS}trE #ˍݎgBbǚOqYV0R/{06}~"[,%I2Q:PLcVۋcd ͡/E^b5Rq:$jQS~Cpet0ŁrK_iޟ \E9-Hbv;(,JVt:eV{"3?5Y_mT?beU }e$_g<@ʌ5a1xYRH-ɛm@>f7\6 g{h.m&XÛ/hG靏aՠޞS>_s}yo48qå5&d qwM}b"Zxǚ9Pi;7niIƉhm9pnC,@=,_?Y›acnKUuQpO}d24 l6n0mv?/-u~Ias֬eV q+6uBį;\=aB%Q(Ng֨ңxy*5Ym9d¡,=< db]hqг qMV"s}Z1+"\q/H"aWv+t GfzsTM@85y{p nD|m0[tZW8ZFs<+JYZ,odywGX-k:+#쎮ďR"lQ"ڏ&Mg.d (-8nr 5]K{:(4.hv+6%tSnzDzr/jtx<=1PG`u$hA< $=1yFjG=(S ;%ڸuP:k~1 !V"P cf"攎MgRRcmɆdҮjBw';lI; "ZKt--1@("d+i@ktq,E^d Boߠ^maWE ʿ &˸;݂u=wwWW4%v[8K2&q<6 żb? q/ h8wMwlGKa3R@~>?w78C~HtѳC8wa~۸`nv8u}6F:)R/㉴x|b`-uZ-fxMdH(.v8O n.; mB_Xhw}W~͹&E.)$8:uL)RKФXӗp4n:/mW|4$G=o4|Ɠ%sGyPK XGwl.MS}/l!yOptNk|; SMfžI˒N*l#./>x2ġFOg1sDƹ剙ճ՟4))>2|QtBvɅ"nq?^7$Nnx3e']jsO+ӐFC|}ࣃ7poq2BD,FP6Ćsލ 'D&mK0s֏!d Ua4>k~w#_/ 四^Gtwm4MZ+*Cn@KG1zZ?M|P(f#5$S! /B4u&l$%bƮ\',7PHG)Du|')T^ Ww@+u?{#x!N +$XOތ{p,MBn𜏫cO5{xW:uMhi>2)cS !+H$   S2Z|[yuh?F3d)|ux#$ƅ_GOE4ht)=0i)Hh'<;e:2&-GQafq%:t[b<'wM)aBm>$H><=]~x)T7рsmv/]'I k/JqI*Lfdz^d 0{Bb8 >-FqG%|!CUC[HD~-!BCjrG]"::PvnJ&c:?ٶ9sqV25!#y kd]?H%#5X y4: <#F-paIvl(pMp*=o t:]W mU&UԛΈCP}BWLsqN7eXmH/mw Gɵ\[ 9fH\,`>\Wy*i r _q߸x7qpxܽ;k#84Q0{ŭaՎm1cIm -EΜ<,_WDt$t}\`H3C;0. Bj)uǫYYuS&֟?!r쪽ρI n@/n׌Dk+7o4 CBM~ +]xa┄aW Y mc"871rq1gMIvO7nq>ܿ}«$Oǧ{'P ru oܠE|<{tgWEU;Q$JǑphzA+tu}I»c 3d@pJ~[}C9N;.`ޅOB">?kuo&9O^xm/V:<̫vX>g=B!gHmDr)l~V+ϕkqߜՊ!i+T>xba}yx:}?vGsEP2y70?"Ldg]cbL#Z rBnC8 xm?v$ u35ry) We1c K||y3EO@ D$uӄ^B3b4ຑ,,yœC_KpNqa:"C4Q@$51$xvvDp+2~\01]%0y6`ޑUahߌ=%O_T+ݛDKj8BӔm3AgYf~!AR"~ p r:ruaP85l(X}P~C8BfPNcbٺ+%|yUǨHJ2; R$.n{̓c]0,֍9&D* eٴ r4 Y.R 9ʗ}A !UC{ u!u]W!m&mlRfJ ;c𞀽Ww+a筯W#m`vNIEu_J}_Ɗ>G-clYm}ML/mpǍ\p<١s/N.:źMޅFp6nN #7[8)y1_GBp"#@֫Odx{y4d;$b}q! ttiIB+DZ;L]5QrrX(L.`iʸSPD˖(0G%fԠTGx6Al _on.ǟ9gА}}d"5I&RX/] VMb7d0*@mڑ;"0C^Zn/ ^BFMQH3'hM`#8'r ׂ4k 6VGd+(uhf+/BjвkmS;tO{| E2vKqܰL#V1];<\g*KsK U,w#s8a0EÒ kl vl}` @u1Qj] EՕٳssvU>j”O"Yg;,>wM_qIJh2]SOoKe(,%F'qXpx)\3v r:ϓԅD)V˧7rvi2v9"J[`P q1Q)Ͳ?' wpZT؞(`J ď^`*"?N9k<ݭ2(7]53z~Ajؑz(W'#8˂}O2%_Mt!LU(t7uwtE-s0yhL1Nd NG¼2T: bIZw]U :Vkž8FjP{/9,F,ry{W+@GM_u đv-G2&1xC&kh.sRE֊] e]ãk͐.X{#&M^R%? k-X\U{JL]tcX4`S_QUEZ0;zx0P>B  uG"6@:c#\F%҈#Ѝ$h5-nnXYi-=d$@t#qez_()Dhvax?t>$[4 :~򦏳њZ'S!Xgö~rnQYv2-}zĐPU3h1|lܤ>;w 9FCOWts{ԆtP V+6;BoSVT:/Ovt%&gKf,A>jO6;^dE%TasZiG@!)|iojGY4PcXfmJ?l Y3nDF{@7l\/nisUp<%^po\s䑏Ij ϧ!7Z~^ԢI JLx~ PuՖ xa\s'բsīXHh o#qf+7O2B S (A1)ERD YE5_dl=]dCDrǺ\/G'bqdxfų:U1w1r蛛L!dh?3Նx"u8|yӋWqC oIxp6(;Ko F^ƴ[y43-0.)LJaMI0=<|rr+> }H?øUO+4ӪUgO9ⱕ21a"^?fL/Wp<%}=oMKb<\x_J9\Ǔ' >FY&8M/ID֯`2[7j]n44jJI%$h6oVIWp:~b6y1Tmb1~ofc耛bKxP8$K - v)C(dt7,Fo GhNeT{ MO/Nmq1Cqjf{XPZ==5݃B\/8 ֲ`"y­>,@s}/ϧQ <+o_)zr;w(YHK? UnCymjFzkm3J)O2ykQåWII5̈o@qΈr' 1.}r @B"+Z9a`j`SؠTBȺ2j9_ ({4e O:8F@PHE*PMU!;-2#l## - Nm4ws׼?J Z|n04Њ]Y(lj)I@/GyPղ_ RQγOjn9?i@8_@(;nCl'Md T#/Ku ֔ /B;,Ֆh0 ̘RҩxC["^n[Z͐?~V#ﻘ~q#Oo4`{E '%FoC$Ԏ~Iqh6Cm#]E1 /6|$U]0i?FgHG5zOp U$)L ):- o~'i0;׹N3)鬌GgێyQ[E3jF=A:Mks|_ u覭^$6d72,P]"X[!4^uW@ WdoԖQ4ǧ@y8gsZCooOᵜ*Er)',,͒(# w7nwO1 jw b:J7iG7Nz(G;Mq;A9(L_;,c$z`gO%]6#.h ) H3n^iipJqx4|:@@kKh #=GV4Sіkv K\$bB>LhAKzSZM 0V:B ""0{pד`|vqwGm)J=&wEsM~.YY?a) xq#?.(k$Ya7A6Bؼe$reUqE7_7q\E"U!/EܓjkV?Ŋp46EMB!Ǐ"6,ҁ<De° R)?8# 2GD ġn4MWd%jCh-5XvMrB*EާCIp[6=U$l݆.Uܒ-RSZնuhm닫iBǝ(h†b9Jl) b*H>}m5*$jZ52_A&myZZ۪iA,mٞtfmúYhcP1=B2 ܮe"&a_)fe kjH,#֢w/Nieސdvj@ٲDt岣8hͣH!{]j{nYK8–o_PR><7'$ iY6bΠkvV>~Dz:OkSVP] Q9s8p~F#b\&9⅊{o'V reB1V=vti6VF ~.sLnApCNr(퍾eQY :vWv:]N*_K:tOǮ{zh9J 6p_q b.}e25&Uy**۩|![fhZ͐jwvB3v@q%ioJYި7ff1'1e:2`$ (O$Vg^n 2Ұ펧z,nxɍC2c7Ѽ2qסX+g!JHmOsx 8W0M?2;&?RDf~?p7W8kƻtъz_s!Y2~֎3'{)H,Wxpx#1Nxb7O 3@&#GGȭVWW6>/g~ <V/bͫBiwՄ|Iʮ.En܂G}Ly ^>u^lZd#%@N=os-3,'Ks(~ 6e΢4RLJ t `c%iA/w 6MT)IlO21X뿻 (r%8d/"CkOdGнV$O$?V5qH|l@HzvձWޡ%DRx-َq+ϐ֖d5m7TUug mx#HFw%cZ7HY$6n(ٜⓉly~?~aӝ ҇ > e|>DQPC~]Ə&Ф)n%Vǻt/>-7tP?kOқ˦gg/1^fOڔhx5.1({WΥ.|dԊԪ&f]?> jEc|I2t5AE6u;\H)jld[RU&ۑ JOx0 D q,Aw*1dH#O,-u;g^%9T^:^ "oWi'Н>6-YSOR&qUj6˅ 0Z-hD/$n!&77Lj $j $FбbmPA/4)$)J/اS5QİFR|^o8z6dҺ6: VNO~$4IB$` b%O{UJHڳzWj:Zqh}t "k|bҞ%=h$Җ@@êiZdTysFzqj/.Ҫh97L)$M76JM-b՞k"))\6PɲҔ:H`kGFR?5JqZLQWZЬmbh+7U uahy–&mmJhU77ܮB[ +oTVc&+< G\Aqm,cR=4.[K)KcPkҸ =0&<޵un0GLa.,)Zؽk`o&K_p)/Kh^SxQV'$Ū QJPXK k7и3?+he`Q4!b-Rb?*BAQR-Q;r>|MZO>ه)XQ>.NHH01k'T%/sԲJү\ح9x05^! Ը԰3ssF8"SbjpWcfqU&e{'`)2(L9mm얰<bH4oqk**5|fAl&YQ^kԗrf#u+ѫ&.n$̬4!?a}:[K~7 #boM G%4)bLOT:6@r g wk]l?$CvXCcDzOC-;RA8_8)4ǚ>dJ5ZD} Z\XpA]!^C3 ,TA LqR_J 䉨xp&i#=Bt@vD Dj=-| u\4aݡ:m;7\W*u,jL+iaV9poӓ3j&P~Uwc tOzغN3c!絪!l]gEM0;#tvt#3hF @@^BEtdAZA٘h#_e~w0Z㠈JlBj66?ʕ "4)48 kxIEnyEZl$b?Q$hԿp{I[` B d=t+>vDwN=ե7nBޯޣEWQeQB_|ۯXFmXy~Gkp6-fGyU<9Ta(ev?h{DH`"!0udG}?/jn~۝]&7'l6I;\@MMݝ9g f"ܵH~ ,>VZ䖣|AvPϸq诰HRKN݀#)w(ђ9uY$E JVjԍ646 E)"vR,VRL26=PRH@1BnJ-jlc+[׷ ]'zdݎATvpx!%HDónVr{toQK AlSTJPd:\1,t!|n3onpP1t:mJx<[)6Z,C;V >U]< u(+р'XRbVz&]^}"'rP]4Z-9ls^A„O&J}1-g;ėzs8 jiK$5.}ld.S(L!`4#<,;M$A'b`"%Nf.V481 g8;d\4rGsك])̱Sp{LA(KWTZe)6<wƏSwS(ݏ@r6C] <έlI' :IrvuLG]9vvp,˩;r'61}v~tWc޸ajj-{_SswG~+;g5_w{{*,]+ީ wE;wAdAݟQZxpW I_tf +G$OhTI .  5s28o-^- P=q#MC>f oZk W] 9"ptpVwC4)L le5'sT[#(W6P+ju43df~x e^g$H?Y\[62=$.̃8X p/yWP2;wܝ`re֭C;+;IԻcI>?AAj$K̲Xdbuϸ ΢*shN MD_nt0=4DeSFSr*+##_Rr%]y|@K\>]wd)aa_fRud}ć\ `Z*jaL̲M [u͝hdw?xɤO8Y!CeKk?C$,D?#y<-<༴*f2VكKg΃uussök@zb֩b>Sz3wWmc:u 7)3jf&} \3ٖhfF+3QG~זV2)~ժS z]ytY>XonYN'\c+5K-i1&'4O`훦/G4W~oӘD8R$Vh<" SLԀ;HAJX^8S8Tck^ % <)Dh|JL+!bT:|"4eu㶹, Rj #7^Ἧ:ꙁ ìCAxRb6nƋrq{xct[P;ݡ#Pm]2w{ >c-\r`XmK "N֎D ͎E\4N g}ZVO2y(KA}6˧u;߇أz?Ǎ? f33":/%oWۏN6-@X#<<&͛5QNnMF%MH,Jl27KɃؽ^5eV\}k3+^AJno瘨IRǵQJhmkRa6$ivj76Ώ:3̱WIoGq8miy8 4KZT%67& .."H( U0̣3ˢ^ho9DeIK4fzy-H߆SA A.ỸI g@H^t} e@e*NhDF8:VF?䂪Mi[M)|e{:? V 9biUۮ]}0Ի#/o'zdpPs'sh֛ 4h2%cO1'/*MGY /nn~ɁnOޝeIdĽp8&#@̤dno._JqH \Hȶ .0EшLwTWbabNɭwEsK^tP\E 8p‘T<{q4#DCv7 B}BPin$z-݇[sa ;Y[|s(Tq$%ion`h9TVIIxz*ɣF|%tnT$DF,o8O3r' [ɓ^Kx<`DT (iY?aSףmHq r ,XNv@Dsi9Cc;֌O g?îp$+!OOȝӳMOn6祩8:u~`\Of=1J\SS.>IoӉ'Z4EpCcőW}V ϔ aCGo{Lf|f1G@3-]Uyiwv[>7*YHDlgұMg!hzVMDИph$R'@:Q#㋎ Tmp }4Ix)!r x8a"/ZP| h鬾 Bbt=b)⦺?kAB鲎 dgNb8}'Soy:JDXHdV]Qumjƭro8\YA}L$m}!ED0)<:y8/g ÁPXTmKDJ\ʛe18[OA 6a@0 WukP7SkPpU`WgkId5Tվ^8X:9 /N2%4F;ݻhbJQt "31 1CX]نA?u>PXÉg2= 357 XĤf}-a?YOٜDGź@_ZW"=Bՙީx0L\ .1}p2Ft>v6 l>AwINawd !_?b-D}T1<"{ۭSb38&|_{;Rg,C#xcW ~Oa7 g2Nx4P`J7\8~S րcPa&pR`UJs7rajc_ h nD^Q LFU$w%+VղS X~TE c]UYErZ -3bЊ~!;9T)Bj稆^׳s$;9X\n!D u`Nf !U,wN\ӟt6|&A~| $]2(@</7yos kTeM;}v7&,v:. BMv=;" ;6Nor7u~mg8_ y.i[A>JqᲥUv]\c° ʝL1Ŗ mdƞc.:r"HǕQb7 e6Mgyx'>K+D ٙM9@0WU.{!"e/4!X1oVxmh%G@$G yQr5b|g{@aO9-O ߶X [2eCIRj%]<]B²TE>dؓ ݫz(_ɷ+p4RϹQ1~p;mqf¼EQ+f:OwIt|``sk5xK#;᜛aw>Vǀ: ׽R{E pK Z>Mvv] ѻ—i8|=Ys_!|ɝ%_iw<]͆7 ߂dOM bߣbp7umYYBcc}W0\eqx=a:ͻ.cNG8:nkdϛ@n%/6w{}fp_3#9j%A-eÛpئV| v9y^9b],c1TU2RhY(`NAOpV*/ xͼUtJ뻐lXyٴH?]l؃:G6{$D|'\ uilRyl>~|\#G. ڏOi=nX!kH9LDv_-a,r '3 ˚s*uçϓ ;@8fhDN#;QM<"tM ~T اV7i\4Y >0sKJ™b9vś/iMkN7Gq^h|D AWH|`LFpz^9OSѢK=&7;>%m\@OSC+޻]!r6;Q =7â ~X#dE,T&VO$xPl֗3ОxCY w;Oz߲ol8"PGqNC~v87h8\g`Maw3ud׏lЊtE;rQG9?Tht ѻco%7|2jAhގ~[u2]h"yhBMBO|>qW$/`lY EQ/k Iv+LdSs@͕=.GjKk( :`(סWwe<n]k`w%B1-=cCb:i>JmKxfz6RIH'q p`ZqʞRQ8+]\z +l+iR c-`dX)t]i>lr%Hd3#8V?krs7A0Rq}5 իj O Sl!h nv.%Rр|罊U&1#h1%K׹Vv??+Vׇ(KcD-A1GcM!B#Z}I|sަ܏0l0^GRjZ5tU`^#Mֽ_FQƎ㓻F^‹HFp]3,nCY z~$oa nx3]KE Zd;fpݛ|}Ҽ F۬ڪd1*z8Z +Bkc>]M65)ˬ?e\Ðꑖ4 J=wr]ynN%{C2ݕTL^lb!%l!hiv7!.\6;UuvIAcngrfrp}INh=q}7}F1͇l!&"1S!P #[QG;1[nsGo^P/M\)?N77$ ٚ `jٴ+*e;Dp>F~D!Ʋ]]rYwGd/-y@BʭudgQ^i8n8!i%{0,+c [Xr8ǵD;W}i–sIS5uWb**M,v\m(NzX׵LPfBNɧAf5Hv}x ;:&t]L{wuA߲Nݍm#'86\~/9*эS]!GF i{eס ˃&x+a #U2k|?F9m}g/!Tp-ٔ#lݳޯ$S X=l빩If߆!wȫ}%ތNjj +q?AnMB,J:|uºpsbP%|2wrp():ƻ^~&b)$ct<+K!$܅= y<*e\=Jb_A;W:w8 Csvn%ҏ/I/epΊRGK3۸0aנ9ؙG %D]/ێp>e'́.vyqdˎo _.[rS_U(~A/DeĶU2\.cŒw8eyD''QgyRK^hO{tPH&);SDrD#JBK|_H<},Q9w!Kr|.b^# o8,ҮЎ?~:Vj B i$D$kN;_tGi%t` cTɷc0xbwoq+,w ;f saQN1խȥ- ii_3ώޫe5E?,mt7|1U U./ҏ+a췜W~s;6  e(38!A*+9⁾# 2tjvK!vU6 5uP]!ewހEFI/__ǐ~-~33}?+';GnhrtwgswvӐg_lj| 0ݛb6"툯|Md V$!_b|m`(hNu֔2FpVqťO,G{C`w0Y Ka 4's {%%R| E@\TƃJpj|~h7+WBK Zr\(2$uD7%uu?z+"IT3G5p;עObGlqs' ۊEAt) VC{ra)v1 T80S~YTqYp0J]N:b aY̋,s$וF8 /Y[3m_MptEue8BYn&ҍ$G! 4DvQ9;đ8R~ Tie,,%:P!Nޭ}W__CV >%."{T$Iq:±cp.m )p^>., rK +qTzQa?RM!h*9tY pɳp,k>j ʒڝQuIԛ?cqڱBAgw!|X"$02S'eֹJ *ٶ@(:5MoBBNv_rt #*3+\b}H{Lwt; !gĆZӈˁa$v8'06̿@5w4R,˛ 7r&>Z]rxGm/NUbԌAx̶nm i2 XWOϛt㋂w,&eԨl`ޅx醖C7 TK:)+kdai \iA%_6n%-@ƈ8 rS1{.7p = _0ߘ W>bG#=?fWУJt!(FE>Y-Énܵ(!n# Pv񝢛0l` %qk<K ޴Fq D9>BF1K\eeGYno/e4$ [\/M5iC53;";.uG>2XR[*x(--K2*pR()4w>#86xU¶},?@DyY2>¥Ji-OWȾ _1]wB>uvGqɏTfE-_nj1TmX=@cQ< Bcu%xs}ʑkGETUPľ#z1á`wʢ/ |!G Df|N-\C:qoȐe^TVb@)ҧ6+*:z*Xˑ&γ -vx"R~CE?F- 7U+"]vݒunmE1".Ǘ/#>@''nQ+Q=|ZQ jMAS 0oǛ"zh8a_!:آ/ n6_ RX~@jzZs=IZ㦹\_ʦgMI~v?&ӅMo'xHFҞrI`+\wѼVx>RmAdWze 4 38]~656²L(>Ua6EGFrT2Mh1dp;h68/rijCxGԁvzE8:/_U~LKPrUwߒW[r¶9=}Uk!,G\fx8N XJķ6/CMxgYFVf8. 0tp5l0rԱƃΩX1k64r?^̺o!%hIyZ(!Z5gv U4(MZ_%(@a9r8qu6H003102x̲kXOEv=޴/&ӬĨ{5}NpA,6N% %\<З_]߻w| @I^,y_/jѿ*JV%aBƻxc5_;b""'J^ .'L!Տ镩6@Wt3RB6?>֙m,E {0؀|eO_9P9\T(R>'uS_Ka:9Zca_џw/f˸p߇fi䋅ί {aӻe9UʲW!nE :; L,C(\ SZpn@>&#_|SnnH=lp<\\HsP/β44&zSk(גw5Vp M-|Kš>\L6$p%ӟa&v8p+;sKDkb*\5ksrͅ;VdñT.%.%.4۠(&5F1I L9U^H|4+ C A@SVc LVzxR*7U(ڛ78}8 |n r,0Qw(FKoyXtk>=uSm#0C}ip`$.2 :2p.--X?IA*.5@, Qeyi,rcy`lgL~Gq oW;#$2 >W)Ȉu)ְ7)%n GJKx@dq*;=d2D/T^ dQuZ`oᏥo.eR*\DN*Y!ܕ 9ՠ_Qt/L(GEvcIIE~65R]*'-'z29!K5rs?,v cO~Tݨ~eX-K=%)EI=oO%\Q߂@d/_:ϐH_H&`@?q;FnD6d]ү5'Hw^D(^Ȧx˽Ɏם%YP3#8?׍>/mo~«G%v(ɞq'42)."w;ycjwsޓBu >#;#-/4 u+Iv\C/ϸr g;]`F#p[ӷ`4KaEʠJa_ffXَ6?I\hA skQd]/ȳlV$&8|Og/wwQJv^ zZAtĜM9rgeEAzG>t![gsMWau^ &s KP_@ zfWC8rF9.d%;[\ ?`kXCc@mˮ>VZWgI+!iOcRy}EWiU( JH m`EsSXYe/U O)9IćאW'5 .|.4:L]-»|iـ!-;5fNma%0p܌<ٖ壘^967APO!m@c.գD$9hVET7(ͺG U%B|J ,XB(`/T\azn'waEb [xG_Y^-u2bph@0) |liQ@aÿꟘweK A< 䏻rD %%%6οQIvu?1_@@pu@;nV Dۛ$n4=0%A#ݎ;KNPlA.$8+ZW~׻&?e4v0]yzewuz즘L5d>*${)\^faw\k\c̦mu/OM aj'>ÛY7Ǔqo o3h Sgl8>kU8Gxf; OlJt)j9 ?=5T\c3_ң pqKP9]x~/w܌>kh!7fJ׽.&2JVQZΞPL2%;$)}ՠ"*a ՃmL0?oabx0ghpUT g#9xBc\lr0ī,>w' D:k?ʰ?vy{7HPNh@|pZb?`BGtcw5ZSI6}=^Cߎ;9Vޛ[/Ar'`*xO(\b=\T+o7@( K8uTsF YGL :rډd;k~ 1m2DzIa=g@V|fy4,3o!ForO unBs3:1Z|=X|ь81;07sJY7!i$xmEZQmG@cR5^@Sqa*AoZݒ[HHt1W紛94v'% ڐDXb[Q mkI{F}cykX?6- w:d9*?"e_v=$fC3$g;Q:X:^-Y tlQRx)%Vk3&: >`q"kB‡;O];:|-#?9p|-y{]9H#lٶ4uH F0l vd9`He,z T ~z&Qw˵jY A6MiqI[gQf/Nsz;0:#9_Ն4gs_Wj51@Tii%(HK :-@QїTM-3k*!xiTweвrTSznvĥ̕69XCeU 9*"v=@5ɭ$GVEb /BrCʌdۻmU@fGJF` gpg3}γ|?\^i>.Or=iưyrkν)F@'Af&W9|T l2\Cw9kX(TX~W.4u{#L\Dd͡z)SZXX,a1`@A օ*q 0iRq8K\X0_vbCMqX `-_L`Qb" {ㅏ!?}+;cfŬ;Ǡ>mw<qJFƻi3l4s]"(L޳Cm0֘_ZmARL`r1 FN]< |\?g$+ҊS%Kkc3Lb?QG{7C&{u[L]鐮*.~#_Fn.A?ϠiOiI^Md_"t5t#pxcQv"O\Y~2vDgL~04t5}52B$bhbgk\@`W\я7cϞba/qFHX>R_,bme]fyH! =@U5̆_P>Iԣ5a{*M˒p"x 'XURi_%#y8FQ0xKÓW)Eh9݌*H]tbmQ}R&Q@nިc#8s-CZO)oKnVBh6}{ymʕI,d%û˲4%MV^D(\+A.kJƖ D*lΕͦ؏cxzd/#adtx Žh:! IP6d{n+T nZAocl񓧨jBZ,D)C>Ӳ /uGîV;8iM2}bX\#ڽBo&Ӥk#'߹λ,~ ozlhzi}3w&o,2%LZaa6JZo5,OW ~m]#uxV ePGpTaL8]G #- f]:BZO`ˊ"YjMl2!j8G&7^\.-ghEG!bU`X  M7YVd ] 2rZՋT䟿(NRe2[z`fIX4 7:?o4ë\jŌ'It/]4՟v;^7zܹE!fi?\cMX ifojѸ4_x Mp( vӗKFBQWI%c}Ku~?瘌ŞR\mWVc8La"e -p6vp~/b?X-U?J%e(J=2mX=U&NF $SK~ aԈ;f~r2~e$fsQ&z\&[ +r#A>R҆vn\bJ oYhl%T[I:NȭOFYWAtL6u .55*#>Nu" B{˳PDVdO"1z>ї4{'nnרfab2,M@\ 7 *}p,6;[ *W"9bjޘ"@X_GVI!MOw )"Dfa2ٵ7dDhfReY+8[S颗v[I=f$eȿdUPQo.z*NzZ4ư 4&ݕ&^Я@'\ТxxϘK/oC_i+Iy: rSa+Q.J^lNDum5`P@߾3я~vυ#(F֪~+@Q50@{_1B7SڣK sz۟y;o  p oF8S=!s4/PxHR ='oUAXlܡg6mv`#YFr^4@b}R*L;?''uE}ZǍgZ۩?.pM&j~;"߲d~5tub8-uϩIe?\MRX`; ӺTiH# #kx' (F U¤zROT\{%J}ikAj)>tԌ>8{ި{7P0վwq+ =-buޚ]jxe{Gd>Y $?-bDhb{40^ fDPZy?¾}[ꚨY;8]T%4>$$ҡb){%Rks?b]8E))nqn{`C$qy0}?\ o$x;,R2.yj!涰 wyw\V rkx4nTdGU_thvGNcy~+Yu}>>Rё.$_%G9RO].#_I1WNŴ3 8vG^L@iZO}"~hhhI:;^3|C"%߭3lP>c~<+||Sfjs6ؑʲ8=LW9>JaURF+,my $MX*mj|S Zz*߀!8ݰ[e9DʡP@.tZ<|11mfX tGݤW'b(\Ӹ™J\|dWM%@ԥw5^@s9% c!0<:hxRM::DίUrc\@Rb? ! ]ή"k+U\sRO(֜ THqjv=^{u6_kB,w+&V=N9Ti ' ;Sɵo)~eqtF ٧4?! 7}*'V!WnqMTV쵊ӛK~(i |;SyXZt74qtw[RT)ZV>lWj}oW˩>k vt>@??bW溄^8i,yàC0)0csN e WKSZIyjş`$ Z-sC猂mqEX"T*)k 6&Ta`vur݁~~vݫn O>u 5{#_hUO:akw0ᴑ=V.߭:jŻ0-%8ߗ1NpPPQvW%xaxv:Emk%[Rq.]̔zdpb=_'v/nfRtY 'Mك/ڄUExBXnM= 8pSi0U ɾNPB{Aw|E"w* %#i ϭPGQgG4q r8VbZrl1<)>hl o̫Hcq#El\Z%:\\\= 2.ٯrlGvY=͂E\Q(q6T '؋ <ǝe 1`$呏cu1#p[,>xIg_w1b9.=3') ݾR.1V%Zpʮ0$A=`ZL3%(?ǭ /Ѭʽ)ъΈlF 2&e#,ywH=Lhu]9ѰHR @D\%dZ7`pbl&"KJr !>D|>!2ʤzߺ0./*_ӫ.dYyr/my<;kSNmxjz-rZ'mG[y1ph?hj'Rjo-cyJ7Oc@@Nɱ,+2rD ].R]-Th˄$Hv"L7nkC ­=^z d]I^A+% [n-?rۮ~]Xۅ7M{tەWcҟ &ñ#ql^ۙ:c|c!sllcAh0@LDŽc"m&\\}_)F`&Xc Fΰh#dBruq\v؆b[@O[zͱS̡,bSz4<]àdMvӿ|.P-\Sژ-܊o\NvȄ?ހ'T;\dց3Zؽ"j$ (jq ɜd֥Jy,E@'ݺX>9hŐK$j7U B6ud\f%(MW%thvgKY 2| 9 ~w5(׆R*3!#@+9WyzL{.l9z bM<7NZok݆~:}3ZʼnL:;5`]3t1;O@洛i>WUU|?*rŔ b0ywX5U+T;2m:sqp?=h-m)Hko`*1(p,Be )3^īA_cQ縔XLoZ!IBO4//Y]w0!07lv?Ԓ8@IqiH,Em DzR5AIIsy̨<}p` sJpLAYqq 4Y/Ip$iHqaiT#[M|qQ f *G;լK_ *ga:֊`zHGKc8Nt0qYUu TMjaD-ŀEU#(,eLR?/3ׅÖ;TObſLBX4E|_#cјئ$TQO*]D Iv`f"P~z"4šLEih\*D8p'I|OۊKH|V>2N(R`26wk1 bM7P _ 5טjoEQz 0MhV--h)gHYg-L@L:~KSV;JKA1RO9͛*ZTNZ3NR(LEy2wVZɻп@GR "r8e7ZWi!1Zx4ڪׅ\ю*a;t<ђ6d۱Z»z:$̙ s~ w@#{aID]X6 QPRa˭",ӣ#~B(2K{yG]/~n(,E ' cɸߧ(Kh 35\nvFQ~LH /& [8M&7wTaktg_ޞ{Xf?ǽND;kHcw6G\L&^v\(V<" )@X Ntp$%$`sror]Nѐ<7<`$|$k5EߋN.kZP"5DD0YTVtBE@mE@E@OEhAW r#8p c'H=r5B;5* 13p^5_}χu7j`p;-wiUIHJE $|kЪ,>y[3WeZ.[L̃5P8m@$S |E@R3\ar3oA/l wvM; #_!L !zaj1^E{Bß0lTBR$j1 w]]2g3+Ռ J1SxS>T\Hdl1W#i1<1.A9E/žG =(3 n(l@[D>@E}: X_wM {K㶳&0TEY._^(POAGiJ:N|ɳ},8Dz\!R}IKCO2,.:@GcK.z=S4=j/?BRLKN;X|!۱d>- H`hZVK2V-ǴI$$D*G+*<A PBW3 &->:TUsw=\DʱI2ΰ_Y Ƴ Vg{Py<<WSkgmgmmbFHQS](6ey؇ Bk4SLQar9@}C-\t1ǩ>nX#0tfMW<6rnц7epa"=ؔ8j5шuo/I'}Ix/> KULXkɤ,ٞdڰУiK$J[Zh[#U8nY8B*)>Y =YYQumt[_Oa1lv D,xC&) qS8ZL~m%lG <ھGF'F$󻉰aȰ֡ͭ$B@cs裆Ux@_$mLo)RB*lOS2ݒ.PK-ѮE5EtQ6^rw ~g4Ӵ>m:6ʶa;ƝL ,8M,ၱN:ˉlyzDW 6?ٸ;FWx9KZJm)L"]Z}oSmx$ Tvb,UAiK\ v @L68cGib^>֖%DɐqsxR]|iAh[nG:F{L1oaBT繽{ya"|FH\6Mfqr׆ Yڲlr5l-K[ OQөOʀׁM__ZՀ `-y)4`{~>/Ou)N1>~|1С `Fi%%cǹ\G9')sICnceh_̾c]/pr]P*6K i)SmZtX8LL(Ie(M7RW, ;ۤ Ucyyh@^peCPO#cXgI4lGdÄ@Im^| '搣%Sc0AWD#8R}੉kxpݬ\E8g#OI>hcb)i$1 LpuU׊l^mk#Nh^kW K-~P:$ -!PyOuH}+~]BuCr2O/|.6mp4|xe4?)UɥCi `bƐ-Y֖Z-#"[p,:m,rbx™ړOUkhaꁤeR5 "j|ʭHj @BE4E%x HWi!ARCa&P`eY_<^I :os(h*j""+ HDhIl*-YnG4#\2w:jgpFB{"ߺ,B#=aeB{'_ⅺ6N (|~.ӊX#^Wr!2.uyAihBgң%І>[n|z]BX1B"TJȠ`qe!z. `7p5ͣE':4!d&UMqY$?;njqSl]~iFB,Zhbx7;]zՍ6^Rq~֖~^W֖}H`,} ?5`h=ӜPYja! w’rs2;En1A(V K"{/XlyxDA"݆&ѵk ȥ:^q/Ny8^LXqz;^l `M~i̖ 7B S7$7V饊@QŌg E]ᛎQ0sAt`q;L.z5$]cX`E_JoDbnJ˱q>ƍ޲ho 0.Bظdhg"j5A0ȋ 7*Gb[-{w{T /\?5ƦiP$wk"ܯmjHtB Y=C-mqؿ AIh,]T2Di&DXU:'hm; LjdbQ5K엵(؆\:Sa'/ W;zd=-qD\@>t qvCdm8Q9o-fJCsUt=誒{?nogqY,4vn Mb!L< n߷/vH/Ak~U"_j힎x1B*) &CN訐auOULD.Ly/HT>"CEZ/*ڑ2b-@j(Oe%\jp?I[h>O;M`ggOxΘ?loa#yI$=hIM0JFoyIUV^FALr~Oju[<$" ua&-LbKs< Zn15ҞP,)/cO)Vu3sέ\+qYko&cL;buJ*`"dvE 7eyr!RtgP (h2I1< 'Z/Q]Kut/KQf(S ~BF0`B,Fܗ$UqLa1]t)D/y~EMоAx=\H%-Ҕ `w(.jA8C£$ 0n|KF…^9txGQA Jrh(<SW8e»١>EՑ} @\/º,)Y\ 9l ,$[.Y^ݟ YL4V0gȴrG凎 I|Ts^&}a*x CM=٫#c2-qoqPEyNL h\I,WrɂpUR>.zb6K"Ùy߽:cafTʐ!&i6vC Ͳ}а K],&u[8du5 Zsm;=7Xj׀.\L5bR Y'J t߄'vҁ뚣&ۼ"1.2K3vElKn,zZ6 玄UFjbjq#/PuPe**#&'6Sÿ!-ˬt8޴0[OjF깊'AQy'}|sFZqVBuw KhT8Eje8VJhITDSL<؛ 1Udp(5I"`%;G`l)8!js:Jg%cRyK1]P)W0°c}\ބ {һz uNs -Pߖjqx0Pu:)Fċĵ\>Ԁ:EV=V׌BM9F9zx{7 GA7E2N$WwYK1f6Mr@]v=:#z#@Z )(.LJqq|qW~#Dk' X[jg*4IHiI_TkbOi%qޟ{#f %YU UvPrᣦs8"k1V~Az4RnSo`sI3}lV()["= \TJ\DѰ1'RkO_k>_yyyy;UK ǵ֎Gp'lUmq=YQ4A.N-~66Q!cMϏ>_fq{1h ??BB.~kZLq.v_%3. r3UL6kY2?dѬo_x+v1}'hE{~wK͇;hN1*EtQOnuxN |*\@O Gߛ}̳{gZ-IJKpW/*<ӷ: )!H2?Q& Ik;܂D]:O X~\SK`Jܐu^[(i ;X.&Q$`OwӞ MBdI:iVtyBv]뺹O؞!LKnR/offb n݈vu5~ v~ uXor7.bEu~W&#W zTHsbNټ`BRk.z̚ެ爎~ܛ/xu $ DnOxvBXڶ5sj[gg]du' RC++KCeCLMԤH~;W+f0v'Ǚ ڎiv:!i PɩP9P^.R`.M[>߶ ڨMz D+՟T u|g(leļq7YӖSF^[O X=4#9wrYTK3@i{]LfrhtGyaf  )[[FTM*@jџ5к$[Kű7M18c ]#&4l~U`hq+6`7tYBp(NjDt+Ƙ DLۣK&\j7q߉z2> (dO -~D\JS1T * ywT(]S \;9xj7O Tg͂MRsAKKlV5 jT+#@ǕBXÏNy  ww* Get?Rw71n#~kud7wOJpfq@\ yNx\<.4K6+99qt)jaDomuP,t@l־mhin9ECxVK)b]B^u]~A/Qi7Hw1%Z0؄&\&=Xe_w`61g4gcb2 st1ї~~_?kvl|O o7>b0EzpOx>}~?N©-W}G}b.ve/a\}?ϟH}\g~irT6l2aI9N7&(dWJհ_R­BDpBӈlkWl Nioj֑Ķ54޺ лc%=Nd{H{k`3oDOx2JϬ(7K8^϶a;fKNf;hx 5xPD@4M򈐬ςJJ*߁@؄gkbǤ/E35f_bWv0W -Di?DI1tEtyD (FSvܥ\g^u n+E4l<̵f mқ_ӿ]Vpe0jkUӟ/]腁%7!lQ6tHm.yR>{!KA"%s[/Z|H=殁^”#F.r!S?RR)Gp~3QyB%ѴzhḬx HS.'A}*/>L>6]w8Vq&(q%UySZ' ֿGv 4a%FqoE,ZzMq戠\%It,q$FI;UZ ivU;"9^")y/zdڸZ]`[qMI֖%)zw-KS1Ip6^\szܷ={&s?ٴ=bGIj%6C6$ 2-ħ"*%p6$R)8?b^׉mXKкzֺmk~4#[T[}i #p*8Yf7hOCIm'DX7~odCq9 {mZ hpY6 )?v 6ʛ`R+N絡h1O벳5cm<=5jhBQÔy[㨜ְWabHHQk੶,Rt %ZR | 77X!G@ܛe!7$/Kȣ|-v_)&zg< ׯl>݆Lc"m߿\F;ZFkXj3Y`D4 A#Hu%(cƵ<,F "^ur{d69})0-7 ̂ cyd$ U*FݫʲsD)}Iti- ΞJ;,"ƅZCM޹ǵp=ۢ&Ғvx_+ 69d"llJF$X`,i߳Χf=a_|YXxPSt L>DG:_A]O+o- Z&pKsaBs_Ӝ(˥f tGp} n{숍0:xuǗDp)QU{4˜`$=?ĉ&yD6KZo~)Iji 4CXڥLpDBw7rY@elMn1)IDAI,jOmS3AdZQoJ+SF`o1pS ~ڇ@a€X B$FE7N3h<{Ge4N@ctLvEf&@QJkHH Q]+.m*)E>60MK'bR 2c݁pjXt0h/&W,! `VH-^XGTN|D ߢ }$Nß͸B#\ٯhP\ͬ1:o]Bݘb܏Ʀ6X771D&/C4@kp֒ WsF-`Bh^ Vt1B1`rz{@rA'I/Z_D^Tk>b){ 6_4_Ā'nPΚK>c5}hnF+G '!v4,YBrG½nGCõiV`dZ,v_75 :nMMb;]6d8fwCYރBg8 eH{κGxPRMԁIIf _|r}NO,wSJJZ0r/tHԅC!FSu G2L|DJ+DPzZ!ȒL7Ztsh0f|d_"Fb)F& X8$1꿅>f$ nQt>t`_cW_uX2)J N*U28Vܡ(ǽo|ݡ/U~=Rk1o"jOpk:+w, X\*w@D-mQ:hDCX xZk!3$(_dդj@t*q*ܰ)"|m&}+Il>_CŹeQÖb["0-5V6+on6Ktn+b>f021r8.ɨ9ϟJxq%^#R}o|#q\-[m5.aKa;5O@H;`~Ha߿6:XKnUQsq]YP?ԡ1O{@Uf$V&f9Mh8:y^Y(doƧ"| K3h%-WFcF˶D.#Qou2K ^.7qebAY&|U$.XՂ(iFqVTPosZ>{mV}7+Y[XR/i"duKWӧ[VZVnLk;,ae̫Vn ,  wy- '(qrYxB)D&{xj {%YWdsf?Ýj4JُUF g~qJ4Ј͟ݏ/ -\`|+d?h,~{Q*T k<1.mc򓅜BNy2:un# ъ Cw2*OZ':5(k y=Ppب;1̎3tɐӢmUo+ڷBE)gk=^v 2Jh"[7PŦD"ˬqd[EV:rߎ D!?Ҧ2,=H?$f5mU»Wr hxtD6CfeDa[svwvA_mڐ@(<0o*d$/gFk@wŝ` y,GxMmG7>u\n,XKkq ~Bd@k݄N\oCһ>∛qdIGuw.̲9!P]7zV{QowNz}((p $x (H pȜZ{._c# )>SHsy;zfkVmMy;df8v½k%dL[;50A!!מC9R^%PBևw~yKnxZxQpkrG"^+&}>կn+uv&$_}sJu7>|*}-JU/[UGPL/ԄdtpB`3v|-VEþ<8 %"̭oYy2[yCB=s9hʧ"jN6ɓ^(M ꈓE_/A[$UƅQ0C"bJOx@8G6e8""li}K.&}Kbˈ}Mp_UxEI|KD;t s~j\," cԈe|(-mj8䫓 ݤ6~.cr0ƓɷEرG DH\Wdzu,;!@nq^!fr3=b.qM:gpՆOw6fYpQ;p;b]'6Q^jv¡15-A lȸƀMP1YQ)7tΏH.ÁקrTH =jxT]HXB^8ڝiL6[q6xHdqVgT/ N(t>ݭf'KVU8w'nshhڽ'1?Hû.rG j[._K>4D+2WҮ(ɐTlr#IH4/߇]>fq9,JC0s) OUmlIեbZǹ,RVFvGuq(mJ.")/_ԽƎڹ#Za}?#@ ZOUVT; %awPt3[Y({#@w/9ID6R+7b"q@؆:aHUɲH@ 3%=mdi^6n7?Di/KrĖ6VA)shVjm[}JL Fel"|&x;_5O-n = 2zDXA3$mY/?iEyzY#5g)+Jş*3x3݀ph!ezV4ȌT$Nz}rMǐK!ȁgxUrmhZw0\2ґ =Z62uvt^n2OaZu=-ΧOCTX}ȣ#ftvKnxZ.S)t(s O_>ffEHm2T-_k0}Ş6K|C>1j}:A_4agiM=EG9;UeVW͝sF4ChXP+LR;"z-iaU2Xxߨ%zwN-[ې6} MFksF.hUWʉ`tUʳ=%!;guTGg?ZCrDctrpycn`;\zv&_I@2yߨ4acC%$Uzr\DRN@ћqaoR*z[ Z0ڌ |UJi)Gr]mN+4G1q3\lDx'.qG9֑wJejDBn]@\%W2b5 b^KWU mk5]s1raOJ~pF0:BWPa؝^ [ʝ4SmRJ)="dJ‡9Bdtgne$_"!~U4q-,q*VAJ?QY0A,`Şv[KRj*1w(qҸ ^hi/ | <(դ6=hȜkE\bJ& o G>i#Upi. S_AM3܀J}%oЯAeKS = S|5a$SZD Xճ_W1VsG)|:*nҌTn߇:%6hM[-vWE"yae۬VY(+*Zԭ$#/*Y&Mzu76W;#c*!ߚw{&h\u5o+ >oE{:\^9hWx0ҔMH~Yn1( F;`a%+0x5?{)@x}WNSu;aq*wNaF 9KGme5T㤸E~}#8ࢫ# ,"9bt,P{rCm~ 3p|?p9W;׎sfz-T7 GVqs+p⩞Y ٝpP}W"m)(bn2:J)=[>3T7`S̊}<"(:ف&m-8#CKڕHhԷ]C1~< /gϧ@>t?W@%w]]oʅ %t=E7 #A1\ģ*X\("(^*dU +k>qMmzz&9Z0+ajs~2g$ 77z"Sb(hmPqUq)e#uѥZnEMwǃ{aQzAE*+;!]H;17|:n@l(1X՝wxkm1k=-+g_%.mZj.TFTzNnQ j9דc,^6y`!mK!!ȖIU s4rd?^C,Q$$~\!/PA|qjqiaV1<^86J(rS풾mJ 5PbK#{)O'.W qK@| \UR:0uC!`^?P!A .^AA,Wx6H:?ҳeOaoM(tG-0a(J&pU^d:@{ߣ~ehg?\YjbஔaVSU -,W_#wUBQT:nXnE^1ݽpԉpN{bn`]YkMѝFftu#@|ɴfwء Ɵӈfk0Fݶ(,>tp8*k,mk}~Gf?ܚ 1guSIx>AEwt {7۫/_)U\+BY@s;lg:wŭAz]}2Zq?$논Ƒ'5Z9St~1-Fğk&Hf0j'A?ʸqo^ ,oo3$wJ/iT-M[d ë&r邧?l2O97fk/"Dpw,B'RL#$ h/8`e׮P=?Pd :49nB:1}h,ʘQ=!; ЮTKn_-൜M(oa$)hFυٽˠt],qp:tbNe!,~V<1PdZ^)<ܙ0eO@f(?Y8NRuk5ƝGl&R>/c [vpDGtra&V_5_`GAU"z@ gn(W?^fJ>hc]Z˫;'?y(1 01)A9= ށi'؞E(@fN|\yC^ ʅSaMp #N RVB")u~m<_3 Ja]nRv/to",.#/C77U]2e>>6&D W%o@lW]@nT dw)*1sz*6R%erDhp$r-./mLC2MgpBa4\U|WVpfPg[)T69 GC`63Ks H;n31n]5'&yYt dqEkT]嫲Cyc=ӲzP?ff;D9?C% .@QF%Qᙝm1;U$^jǡ"GKP|popN! lNpY(/-@ >~2oNevؑb\KI+X;ݛ{Fc,(U?ėmP =|@(xqp[T;%d>z>권BG앬yfij(]oAcNK?w r_t2q6zV7; Z^5Rͳdx.Ǥ|fl0$a6SeBI5r.!DdWbvl7Apa{4D^?)ywz5dJ`>YrA3 Wdnׇ'iY ҮWM"%W(e֭c9$`^ʳ/2<J*hs,s f ?qjvkvVK]>VMNiC:ad}h^.SyWB>&+AK@AH( c|9k;n5- xZzW.fR{^ w}1ݖ%1pN5(nBԶ楂l#`+%_$ UHUB»wd|'tmD`{2߻0-y+eyt`cItuYKD7Q_[P7&hw}ob2?@&Q4dI?"V;x|3X4`S+Pʣ2%+r9u'qvT~[EG;zw4m@u `CJG-1yJW&P{__ %nn|izY'P^D=Q!CtxSUO;4G8䙩 eT'pFհJaw\'G r;mg{ [I%Z-X[&6~7?_{ԣ~#dq̇"GPMI;ksJ4( {7u h->Bэж]?Z,iz4: T8bL=m^^)@UR4Tr%:Q&p~mޤy|% my]y+F•,n0g&@-ATf~/{cPJi#&PY^#q%Dk:6m` "I! $tUIf4!|t*I,ߥpգRet݃NY`BɈZE@dEK_,G,`0`ɀј ^2uP&\U+j)сd(K^N5sIaTk+آzdK+2⸽I޿2N'^W]Hk{7Wd× ,;gܱIݸd"ֺ hA=GƑjv-6,aATڎ5gp\ǧW_aS֠6^PKDs1M9u)DnvJbnԖ0Rٺd'ڨH61\ܲqxYqOw?qɟ'^B{0?? T>D̆^Ichgs177 h[|J_i;q|9mdso` !d`Wp8 3+0ʨQK. *( ;;?gwp~*sbƐoB;ĭE qHv y{(GF:_Lܡ-IҼEqDw3~uJ%9(QQdf"wc{^$T G#FTӪj߃}΅Ly*koA%%FO.t5t.ܾ"3ubqb ' fǃxNw br!Nq1`> B{PE5y 8w:'YMTZS}`xsA+̭Yn.l4]O4iE,Tn}nr=آC.4_eH5/!)E b;azتG)hqʒ }X"w8 ./}xb¥K¶*؛C0?jPG&Zwʓء)a`I%1B*O_Dǜx+K$@tbng#=%c4Ёs[&'a.s\bC B#i\N*Z us`6RBgJTfP/G2!;™w׃20/#_c~奭kFˣV.3YYKPA[PCȌCW$'B}vmbZH/""=8QݬB_n0fhy~{=Jb{GnhpZJR9SB._:?H$v.M %Bkd$T=\HUA!"1JhVV@DDU 9 JQ]h j;G2,R*5[%ū](Xar I-踀q'uA^7/=x<B*+vȡX8D%w*Eسle8/qe[)2nޔN0U* oX^8$86K/.?rd1erX+FY\bӷ 'D<KCjBpuS(Ҵ!s}SD{w '*OL,e{fxox/YK[*Pِ7!ݻmHbO.oD6[>{]q1VMG:l8m@Ffu|pӀ]Mے)-^̦xx2HMn1 | I &<"ʈ*k,R ByDVNL[T):0 u%bcR[Fs3s7ۓX[ّD4Đx{\$lKlꬔ0fiKn N /V?=7"d4(Iy"- ua]Q!Vb9O=C||An KId~1-I&(((I8cCPcލ]ÙF>f>|g3{Cs}G$|_Ša?ži8$>'qaH2t1?u5k-Zkzÿ[5LcBTZ-@ lo Fl>kFLKZ +. ! fK ~ԥjRڊTbaJXp`uu dY){TUbUKq~;_qyb}aEK]Rgñ㎞#`ϝWw8yn\oB*Hj@kh1͸dgEY?t" %n 2DX%z?,jq0 P,=S(ZC3\ӗm ޠgPa{4݆zX^ Y&~`΅ ox]2qkuCUP}3c4i7bL0O@g .\O}jt/ _mEBd$7bYBLq{^:DQSű~8gJA1hLnA|"p0Z9pIV$=R|1Wy-0UPj~cJq~I nW@|Y-Zm.eP 'W}Bb^ E;&5wQ}SN+#BV]F+5Ņ#^*w+*2b~"߹rO&V 7Skf%^ DtY .`n z~5nWØV\C!9}hntO}"PТ3WYrh=2 1C|¤ djI]e)v`'952HvssQ~N,s 2YcėД8/oB>h*4zpc&h"M1$jLe1"0U5Gyͩ2|#IX؄Ҟxβ!}OuQkW jPX-nܽ ՋcuaC M.5͒!Uh5ɊP3\jT*l pP6VvjP!-jG.巰h^Έ{̛0Hh'qe҃?2! ԋo9=j_b%PY7+厊!cJ+QB'EE4MIȞ+OѶ XQ惓|că$q`Y4R*U hSWELi$ܯ4:<56i`\ӆۄūdx4D3ML]}iNQfY[$ ]IGmdraYG3*>EuBuPTOCEy+Pr%w;Ok y>Jήj&Y9LO'#2㲫 _UFɘgu 5Yt&>!6)6a>(g J4w:jfyK2{%S9ưFcA|KN`s`>]Me1݀ϺXhKSèh&7"ZC Strc#2Eiv"\dQzQpeK`Ed@FD0F"yZЊN~@L:}ީ3[~,W<ޙ`BWXP8]~:Wg6婄;j{߾{:i-/4#¹vK$({4]眗 l~G?R}JL$J2*rT:B%d9*yOsNQaby>w$:$Dsm<ƧLvk<2M]04\*q\k\Sʈwi ZfxgiM!og^l.|:^ o!!:yc$d rG ,}.ICsYʁ]&QRIҸrcC/lq,sdh?Z^oV5pI S,œ\PE[v 88’۶ ij]7@<:<9 v,N;ȎaXi,~S1л6[,\ ¥# ![zmAا>guxX=OOʟrwe< I+$pd\5~@h(&6ʄ2@dO 90WRI8,W'(0<Ɖi+C!kiN{܇#q8J]<ťaq-$<=Ë WZkBMfcZik"AؕsJ2,HC C3FD^e_a.>0Tve|t#:n_䥪w߿YgBK[S!G񰵡5u-WOtd@.,3Nx`3b!P[+k,݂#'Km4.b }bTw&~glo+ք;vLʺ3I?ʀ&ߌd.2 \4<-ɸ'7 0_DMOi{y=\G|.q6'JܡlP~[VB"-٤%H_wᵠ~ ,c 'o&OcQC4M5&ߤ1 k@"F6u yi |0V]q;KPeΞWS+ʸ%<.#P@c+$ Vgk'9%v4(P\ϡihz`ԟg@0::I0oS61p(,<$0R-3^l/SS R)5'<`xA6~`&+rue>m,#v̭ㅿb-c*N՛:!xGxZt;Q9ϫHm_\ƢFsLX'Ļ5@B{ aZkfBPMLw';bK1;S8jVn GBғCQ"1!mcb} u ?|xpq.6_h5. sT-‚KJw.e-bҏ z Ib\ѱ<WQ|tU𞁓J ؈:9+=ėùΣ9A+YnZʱ^gύqk~d|.ɟ AomΗm1[sc&&eԴ'EhkhlH׌I40qX [ H]PJwnkPd1$ +Gczt?wٛUOC1<*Y@y(C"Mћj=h! Qz@S1f,'3(n״DYoWڣ۞m.iGC/OKe.@ Vޥn2^{he`U/r<pdJx8-r%˯l\[1tm]Q;ϑKɕ97k=&y[K$XOwr QXǂR?rU4ܾim#ɸZ yh]EXeܪS$knNQ_kx%IqɚhI:~mYg;$4 |LAŝxa|C=s5¬ :%fKsUasF%,'8F; i+)6V@≜w0 Mɓmv0:L܋'It|_LP/4?+bkIU%Ps9p"w3%CY'=6 8) 7$"+^E9ݕ $M oPi̾,fUjHGVA mg"0䃛^pW.)CP6+yALbړw*PL&vi=fنbYkBڐ:.^!E8c7$ل7[fR.m}^tcV~?K~2ox>Yϧ@L~Wl#OΙ$,%By7 J s))dI Yܙ$dHƟ1T0%1?lwIf;AeMЉ:ڷX>6%sT,ЅN;PjJp>z`;4xp}h4;_ J/QWāyO6+ לKht^bחYM"Vf['a3#[L5B-xڞLJ7X*+w;>mZ Q2^߼]n%NTxNk¨ ^ٸeRglyW>=AW: 13rҕ"yK1pQ95@mѱ%ҝ1CҎe†ErǡY%mHװXF!O]r4b[!fv.y \n8;N' ]~̢//!'r'x \z#8ʠMs/لdr|lF'!@N-X>KmXx Pß3L'Bj{>RKdw^lXh HMx4zwiђo׮Yd? ȿiU*t[ws2tW_1U3rL;ݕnO<r=_CJTxx*t9:}X-XxZקUu ]ƇwUx:7$WnDןGW%GWC$TucPdg @COBWr77^@=piJwj}`=^Şwaɇ!x\-RU˽ >n6J >CB,LJ 00CQL{6J`WӮ3DSXp$K :“AP%wSpg<ۀ޼͑f>lO - # B6LTm{ץRm^ +WWFJcTo5}ጺ`rw ۸T~~S}nwV_y ,E$?Z7{s;nT촞חQWQb-Eni{VT؜qJn0S'{[wW !xٸc(EbME"*ą R 9wQtX7<]HB>(MF£ח@U 쿸Fs1?H6{mмH6TrʋŌ:Y N\0'ߊDS/a(jVV%A}GAD7`nڟdb$?:ON0x>p+G[qM-tb]Vz8bl[ù-8n{ż[axled6ѝ-$*.<)HT/sׅ.Kb }7(5=š=e/\1Op8mTo%r]O/[ux݋C@CҶcp|)oA6)|>Eu`uIDmt4iȎOhk=Vأl ïV(11/Dx0+]|'.?&L ~mAl*8a^~j^(G ٺB'0ǫd:c̙O}e1Œ{21f؁ΰ4{~>;.|SSsoi-%鸬S?AО3#QB.2φ98H2[g$dҟnfՈOqGp|?{Iގ U3I$gX%i1?6+?g[P~Żi"(N$и$H+({Ԑa湬ߎl?eWٳ9N0;㡟qGA溁hb-4si|9vC{Cg q+q̈34.5Z(PNkk' ׀˹<%94V)ھ V=Jbлin.oaݚd̅*P9$`iw/ gbKLd,,0A.])1CO8vfH;#j- yD^0zfʊ~ƿL7KpMxsHoI:+|e-e$ѿjiW`sG4sH[DX`9 8 m'+i i.U&Q68e+ύQ4,*G/5 g7 fP!5Uќ =4Ǝx9ldeinܐN ipc“0>8-?_<~NHVm7M|Y g[ۇI x/5jɇUWK<ͳ^Nl韬9/!DB!>GL6F1 apZF-%E%lמˆi1O/#:)fzF=F34 4iUǡ[:nM[tJc%70]k%WcvҪ`-obL Ш_0Ö +~=JTEx*moeǎ 7-AƵa$FDrܪQ s.Kiv )[mxVU}+'A& _7ܑj1y<1&/j]"vEȞtVJ*e;`AsNOrSԺ4vј+2.Sw^eU Fp*yxg=@/Jo6oncO4Aӓ8jșCѻڡ %"kW[,Z)M8/9u9#J8 ^){o^@B5`>+oJb= x|Pe) 56Kj}d/̪Nη4xʇ2]hK\IFhWKǓeDާ4}d#in"瑑:T^Dԙ4N4酜7ɫ3)ͥ~G_x\KN/̈́L3uъ3Y516Mv65q4&*3:yf$ȚX3&2>DvG{oE \N(8"e\aR&IU(A1V+aE%V+e+k`{/Iv|PhaQeO9P0G 2V,VhK| kK1FiWU=yB)TI4(-*XvToceFC:k?g5ZĚQ a_S* vI1L8pM6{_&mjqғ#Jg8X'җjh˱̸u*3e*y41;:zI+eyQCNq/sNzQ^\H<8HW)왖T {0rUD>Cm|]/&G|Pꐞ!pE2.KJaX%3MĞrNFe p2-E[ )+"Qn/b78k\g-:CMaxcD  )0j_N@t@X, prT楕144+̮ypHEidCy;T[w%LTҀ֋EgYTT9[8 KZ7 c/e,oU\ 4R*"1zw<Ħк `JgzM_3 ggAư'xv9',󹴬{@ΞJb3\VZFU*<}sޔԈh*#¶qef߳;^拳әԶ\Vr ϸ[@&nnK/w܍$*>.am` [t-!!ᴩ׮Mg ĉ:A>g|*v,Ʉ>vZh=mZU;% CM5&ڗ+H&9Έ~,p@z%vle^Qχ#|X\#ҫn"sˮ.hd~^_3n07vNƧxiCewN ˗hSyHRf"(q/gHK ]]]/a} fc"RA Z o f?i,Nş 1Cΐ N缮^ΚuK7ؗk՗ 3%kd)9± 9vk9Y^\^RDr߂K0@8W0sPNKO<: S[>.wc_ܻU);)2d1p\ RЈvt#%{[xw .D劭O͞qc8G3 :s4/4a0jYa\UI'?[s&⳧u7ؘ(%68½${P >Ax.[oLP!sڀACYyv6f(~"mx?r5qlCf[)l7qϸZrzSGgl``l*\}, (D|Dp880a@FcS9Ưx(!R|Ҹb BыgS 0} *,Y4u60%x+Xt+`Ňcc{"I9$NoL/l%9!*s6O6 ΍9Q&v)X^FQC$:Sw (vzzu 1wҧ0k C~׽6IYxކa}JORR@!E!`cWJE 58#&]{E999\flڻ`?*՛ArӿKׄΫޏ]!}ii2oo{K@̓ ѾOS+mչkn7j+FfN?v{ΰOE;g~J&îigU^f! C e;aq{]s |4~Mֻޚ ͡7{`9|UП o{ԡ욉PO*k%-pp;zKPN6s~vYgPxOݣ33iwgդכ*UMc2v3日(M3<E)A;_50o%]/@wp\XLEJa`܎< ՛wjg~S\-Wcf>Ē<'i+{ǰ&7y̾Dfx8Jv`^XD43=菾f2w`6NE[uFBr4o) ґ4lp-39Ҡݻq'[00)h8gh8d {M7۱M& 5~XA Xco ^WO"3N]WU.>UWE N`~>Q_eY/В1hK 8b,`bTwVDvyR-Qs޾EdOBn:(ݛ&.W&s%a#wqMbm_=}itO1XSxH%Q}KڃsәR0AgR@EßFWY4,#o+ːGSfg+5?_Wq2!DݏGʔ@  z`+ذ ^8Ju/JLVr1 LuAF"s>/fu~{Huw(Ҡ_T Љt9%V L/kY(PTJ+~ӚpYO&[פGO9rvמSjrfFk qY'=Pؤjr3+ '_p3.q_M~?u#x>=p8vcݴ7܌jb@7o} rW O xgAJ[geϵhbk6|;F׃" JQaՒ2F)tET&9B9,K84V|iz\/Έ2ذخڇQ=};&BcN@8.pZ~bg9 %*{129G`H|* K&2?16I n3TL,`=Ɩƴ,a-)$f![xx^wu]︵2UXނ +7<3XhUuEx2hg4SS`7 8٤M : 1[ɓA8ՅiJ9h?/v5th d u+iDi$T }ތ`[%`iS͆Jwrv?aV}i+CӛiO&_4HtZ]ȉQ7# RٯoޏPa8i5onҙ3(FF |>ѳ1GU;-3z.rV)[_w# ɷ0u88fTVqSg86,C?CFV엥\ّNH{'Q +Oj~Q0n=.mRvLK%;|t*@~g!`xo)9-pSe2nz/ңpVȴx^(?.l3rL2-:[z5yBL'Dz38zU6N{!N*R;2/wmx_XS~/,^㠀 O }/>)9˩CdE4^l0b#92WwZVTe  t٥03顔[~]lde+A߇~4j}hэ6S}:K;sh*=1)V$Z[Ty,Iu(Ѽ c F$ЎnaYϠOKG$x>Б3rG.c+Kb8aca%/CiZ"K%04GٝJm3OJ/MxgYDb|XZ>6E9IikC>ZChUDfT1ݻDړ"@'I; DJ\%՗zu-{UN8eo%G hm:uYX@LI1_~)1xB]\&4rzFQ8x><)qHtX@ gU,[ 9qћ-ҿ%'?:cHDЛm밪2Tqnψ ײN+7Y1`d bIF#1!=,v^㮓 p}?|f͔xd3NI!zRLG@@V}n-[ftŸ^%İ# N5Hn t}Y͉G+dOjYI?J2Mp34t6KKi#1\0Չi̚H>Щ](f8n0`Őh3&/N3997wZ90|ѡesGK`A2Qƶ2I \N| o=P>!atdtmmtNn4Ox+!8Z,+Rg8/pǀA{>*EuStXJQ7WחBݛUN gn/ܪ_O8?s ̧.`N^==sGes:p.zo)5g{=@i/[[q/['F{j)y-%[̱p$^) <|DPuHN`dS=͢I,eGI-Ui4mZُ^zlD EP;$AЌalNjMrz?x" \01g!5'DmD9f)v0 \V;"⍺Zl}BȹF&,-jgb],1gc3ʛ1"W>N֞>=q{e} b{8U7b\Y \>>v^XY~ SH(ゃHbJ:u#7Zˇhp=[5ǣDrT2wwjm_]vOë}-RF*2eʨO5>5>7n54ɧ%MMwo } ;lLA퀳yraehXYp9!^+4X.;)6f_>\:^>Of\02\Ʀ2-&Z͊Z844<2u:sMvWH|M:s-P_N/?FUå#Oӛ/LpB00Z2Zm{sdlY^ ~n-p 'i\lNK W_IJ LtJl0Q&j=x/\Ψ5{Jgxy]EUjs x@mq0&dWmՋk|H-t)I(rCY1y~[z?eȐ(SO#ebvt^P]%p5 E$پD4vm</ZaǑԶp9Z1 #u4:o`A=+"J/+u!IG)HU#&JE=#-nk;RG:7dvk f'!ۃ9|XԈe!!v#nZ$g\Ο> 0:uN ~wخhۄzS6ZSC/4@ٌ >ОK}8e6mnn"HJOyȲ1Wzp+F/"sw#;?-Q/s:!t 9aˬel4I%B6ޒ~!bc<8:84 3wtqg9xy^\Ql9vܧ`B974&\4?v7":8M瓀F!_h 1w sIA IBx6aXl7gL*T%HZK2ASy 6pyzшJ I(LRӺb5iEx1<5kӺ|+X5Y3/bD/va&<f,u^澎R {8DֹewGDH,`MV0I)0] ]bٽ: Lއ 2rUBTCL֌pS ͭ] 8+ATz#Ir(,i!F$ h1-$#<}M CA$F؈G6=Kǒi،^_̖ ܴ'!RE5'K2_DZG։n긬qIxNuzԆ-VЇes h= q nz4Ofg-m:gV%ɘsKHx%WZaO4JDh6/@`c_!!pryBۜaw2GVoAn.x4ݙCJC~îj2lK?Zy"z:&{^CDH[fԙ:z]a~ZSW&T+t1;Li9lm#[x`;F 'I?g27ąnsevG[BlQ?"v2s~[]Mr^197}$6 {'q%#/K& A?K<,xtDj.YgQ--ǮgI㨂Vĕ;Rp8:ڄgw,r26S&}8ج~#L_էaex;`;Ip hQk@--r5th9H BtEp|ì}fF@ʷp*HӍ-5<aV"kW߽ =.f]f8sIq4Mmȃy*!]nmD;CXDIut;+ l4d}*T J6NTk AٵwRM 3!9^#U oe%lΜ91nkw)8(04爦|+Ԃw4_{3*EV¸p8:gSyL܂<*Y9$%^;eQ խ4[ޕ2@ s cX_5k%J\bg,q\*6J,b ڽzZX>#B3snuj@:. l,?oPfmO-X)ɹQ%HZ K EX/b*h/B|O.4eXڮ{,*(O90 _z&0} 25X^\p _+5vC#XAw]um_;*=tוֹ>GZA$e y 50^ akFEj 8FZ3 M+ /t4/Ol>IQy4<7_;H~/O9_L\!_9xfw`Y:̰?Oe~ iOd&:q^j1 HsVPاHY|6e54^& <(D\n[)Y\Fk-{ ma5/aXgfEՠgchbVF3}^VqV&fӷtz?ThS(P;v@ԝQXyoHޞq -go{.*.eSB]6颺lJK]6١ZʤLG@IoF4w. I??Hpu,kUDL lsfAgT s-X[ܶ;\ lΎ}GBM6eXͯ5̸3z缦iڛTZц&L~p$~,څD*Ncf|ǔSВjft u=\Gi羚&FХAHJچ;_1Bf\ ~fAn6pHgdrñht4O!S4z~cu b%un2->73ipyV|ORNk`Ph`FASpg7&@Q c`F,*Gt*hwx?"j_M Yf(X+7sNN|\_ԪAA~աftYF5B1bڙKǴx ^3L :3;,碸iǙ}ceXO5af6n|{!Fffon굿ݲY֭F_t?T?Wry9K|ĸnDB8V').thu(m&(ObtU>,zt<ĉ <+0hO*5+]b{2$~f{59Jh9Hg۷ė {#ůA#8!y7,7wz3xVL"f,h,4nP$^*p؇8(+ QD aѐ3!P_ςv5sm/; Z *_{S_7Ք ̡ =Zݒ!XhI4'\Mz>3BDayD8!T_j}0q$hfFZ&6BzYCݑW1i/M蓈Q+nӾv:D̓fJ03( ,:mYLo+= CI(`RO~Gbw*>M#Y_ +^\ l@I%-ajtvpJ)"ze/fkQcl ϛ7$mۜ]H, wuLͬ8:;iEf-ʪOFt&Bc':؂|jOoCnIf01½pNMe? Nk!+"r6?`rʨ_+s뚒Glc\tA? *uowۓy!|`TRc9#6(`.R6x<iQ 4~G߈JiQ-ؙtu?"Z~`z7Z_o;VY7e oWS8 |ZXsb?=mX} (|e=VXz-5pQGaٽđv_aOLtF`'8&$ rqsC<;ϻ_yMXcUn  J)w/Mљc"Fű"<=0BgWgbK~:%mK*sn~R?+/ U|Pj~^)S6gQ˨26EAӓ}z\)Y;:້[{s1|V) gyJa0cPinu8/_׀O KwFAk8fZ-Z+XE1lZ>}u\&иZf#aLm7c4 #Sh {ӸX4/؁.Zo[\9 ;0Y>فx?_,v 퉪D`;*v ݍċjXK3ؗ Q/%̵۬a;+2<92I՚F%%^.}ޖ֩tlh\dW y9(X>eښ/ d)[4D =e%%ٟ{| LF>xD0a``^J_Ѱ;l?*VOYZ#`0cLd$cEQƬ/}5=0gx8$u}d"w/!g'az2 T1{X#X%9xLA0O;K5Kھ cz1?BR:01eQՇˉieuLІH_zq CrW胓H߁T 1iݟr@E*iP&]$G0\cM,|MnoNˀ޽66!<( ֱy;|0-7>,;zQ &ڔ.ƛ7uaw#xg([BsZܯvy{nx{ >.%8Ǻ ;WUR@~}fv4i,>a?g?j-ghud,/ ߐ[m(Ik?]##=vFj6dd$G NU؛?ǧh 4]V܂7 !1G 7) NN Y58*YKbhtO9?]5sqwӛNq%ǩ[g6iP&mP=a4ଡfZEax}K5=3CU }'K +K@ 8ᔕ w.aqi5Cc/NO}h=, OdŐ'Q?2fN$^³aѼ[zWOw>G6,۲9Y/(` j> pg@#^پwYG8xXО},K녬ijgWZ2{qμM88IQׂ',{=%I2ow8o/% 1H[qrx4q5 o页 YRl,/XR dRFn0eJt GhLoRzY{U3UCsN2䈄|~SHfovFqSu J?שvF;g˽ >$m4Nܹ9a1}RLc\p95s,DBDSJqOfYQ[DBH1^2Z8xw؄&P|R:RŮXb{=5@Ic MKhGXa7MoW& Q\~[D/{A\s 8٩xGAn-wZ&͓ɊC4|^hU$p\랦 UHr-Ojbiq!A`ơEr(MX$lk{dC_ AԄtqj̞UE%sQU80^$9*(C E4Ry$F@ÿ99PnKeJ9ц" v ϶ ɛƾݟ>cMV;`cP ?MH!PJ0ƾ!?J\06΀%Bvak}zm1Ǖn;8/'d&AYP:6{I&Qg]'b&wEW$<3y㉆\Ĝx}6 oӡp*5;9O)yh-=yp{#NS ipnZ0ĺ8.)ٜ+QCOؼlg#mode>LJrT^Nx8̇9yM^[4gyʸ4"FT"ݻPh576F̐2z0.HS'T(قf$[Lradeni;8߫:6%wL#fI3MsCu`6f H 7#w1$ߘ|Tf.M~*d5}ݮJսzg i׍XH 1 ~w,ařGG|Q*ctNy $ڠ5nAy:julkg|y3^Q(fCEҏ@8 n!vB5JDPo#7eV}$mjo]E'>fn8L9I cEgoZ[_MRG8^4\U8v`&.5+=5ͻ"#tRr Mc7_}:)-1n'e^K:`|\ݏѸFD ՉǤuTYʷDWeu>,*Cӻa3ZiΕ^jmNCPYB6]Pp ^ӿ[ʉ $n8+PQ*@0nSsP>^񏶮<ΐ!nwSk=:{LBjSB\r~Z}񯲐6Bv%~OS-y0VcfǞAT/ K<{:MJ鹳kd1e+,4Znz9Q:B:=,anM,2y?.^ V*2mԁg]Ɍ ~4F)Z˧C ){4{vD#LG w \QS5V'(ep^ >7{c2f:\CULWJCWބהj $i\l={#-D)ܕD{eW&<Ħ*?짆/~n}i'7cWY s-]E-,!2#gl>q5-ć3blsbhi޺LezC ݐ(f9|L0[|p]F"kujMYKIO1Q?U t/UZF_i{zTNZHrv9Ny\8X}94X%Zrz'Dcܢb%İ'UTHLc'F#EJqPFZc药iduXQq ;fw&s"fV&YqS&i*B jF9'\ü(rgqڻp<|&7Qs͔\,dYvtoB p:#:6awIi(-ã!To:}R7Z<3?wx0/3Fz4]Acnw:I$3E(LXF>dj-hxh<赧wލl &&H#8Җ$;4k5 ާL^zey=S 'jq 0JG,ۧSj5DH* Ir@`,gPoՁϷ3 "iKd܁8+j*10 nؘ)#d|"s3 Mc"hΪX-o/&gf@@V $*jsZpvb#'$==&\y'QOWT$,#ًoׂmNHMp,H1GR7H"bc|KBA<_ ܼT/~K>*=s]i^q\F`]\.mX<,b9+Zp<^'/͵KE旡|)IvʈL%r:?N3xл <|A}CTj0ɋn&KXY9={gGf4*aYsГQ$vOno͌JgjPf]&_lQCo 6o)?ޤE.xPZID ::?[L5~URX Ӷ Dnݬ">pR{F֨.nݑLoML'1q`QAF~(rNQż杉UaJV/z{tpL CTZ֍ѸAUlF*by4j6Tɷ/KV.)Im%qmyi|UK\=xT0Ϋ(SD7;خZ%:B⠃&0"Q4}QUH-vɭFQ.%Q xK5!*p# Qp_(ҢV2x oaÞ3'u?YT(3 |ì0nyL\t(JTvJSBiIW'b+gOF,=tMs0f(;H/ R- Q'Vn$\#4t J)Nk֏kwxm ٪! naTU|8H!;u0%\bs\>Dɭ`mu^T~%R>:v;3>tbc duIÑhp|\lG 9j} A?R{+·q]= ;:bkq$F7+0'G(lI2DwXK@ze' &6c~P&F֢\zӯyAԡ-I{:q#3bu 5y{8R2atS=3j7__ϛ1x/[(l}2baA4x gShc(߄?2Bi=|6cltn{RZY2 XjW%8H%\x!CGZQkZrmd?Sp&~1,{y"/xp \w,mVӉ}f!&JӄQ FfHܲ@gMgMD3N4 O:rĒ5Q+zK^x#BYdwlZ" :Nz(RQ"{u^TBL3;ۜlΨܑ3n|aS .>,iC&:z\J&2z=fۏHy5%웄y|ڋv 5J# 8YwYόIrIc\ekVS+)g+ *{M`9+B*p8ZJ0~ej?W ɻ8ЬT.X%6egވbޅ$tYnCwB#/):Yvh8|(uF[ƄQ-UI~xB_mpZeM77*^4 6d :7 ~). ⚽?2 LlDCWp4`!k4GGz2rz?B>x8"^: !`\t `U#=ڇQbmm5yqBTűu,+8t?9ެZy w}~:.G=N'hF$#~LT8ʲD@HJn֏ ~ ex"`ÿ%Vlu+K!񈆵Zq9q:ϒW$go~`sOW CK;([bte l JZ"_vvi!f~7}6-=' 7z<9qB+ |"M2יLр~>FZb"f@DR/R(lBƊW-:J+H~z7b[0BX (ZaH;K.i7=Lgǭz^_TUk pqVԱ4OV֫}}8m/E rkʙ:V7ᒢTYL<$Un-$-8g@e2 M~[RJyP˻UK z~Zf=ldBS=,SCÕ:@Ӛ"txCT[ v׿b>S7Izp~˸3?_1eŬDߙ=rzsG_yX.}_W0t --D^QCR擔V| F*~(Pc?Y!=F bۭNHXA|fm؉dNޓ610T#z5Z|atiiIofi[HefFPopP.1iEFó[<}z Fbd$cm2ӯ;h 0 6uT: 8135ϯ$u~ !`_b^2dݴp{_ >BE\xl<>hIx eBP? x20򉓍^7 2SEx""9jc:be|++U%턚A*x/ {OP4fTHX+f8( ^HQ~ H q +>P}vȧS.&d,*gn 8bK8K@Cnx= ld"j[u1J4vV4?5/yNkuz%\Y+_w+>M|8B yzi#N^E /| CdU>_2&FrV1ݿ'e(=R)8Ŋ6|YlG%nUB0 ghe0bfT$? y~+׈H -:w ˞9EHVWcEp42Ȭ%A=0}&+LlؒAYȖ k C _K #B|{}98m{>ױi!?<"rJ_:Rr.Qrq)cmB3&)] fMnӊV# )`v69ЬckN s>@KusM8"Qd3}P9Gb3SِWn.06T0' iVo6OAXH_4UfFw[=Jwa{rfΘ/⿢,QhUL!:|.ʉc9/@A`]|5pahݦHu0o*(ɕRmyDҠ6WJ"=WKt}z^mMǩUU{+U^铂;TU՜St^i[^s2Ъ?{d+)hQq/]Kcjp#MpeHZ$U 3S.hѕStӠW׋Ѥ`HMz]u ZT&xѤ/7,WC|^W̗(E@ɿU٥r}&UPis\}ДZYtuS4jq |$$ujjtqܬvq@aT9 x!?| @=+œrݿ9{ﰠƻ~a.T壁Di:#( Xh|;>ֻ:G= qx~#-wCR7.|\yCBBK">p&Q,R#:';(ph+ Z1 R3} U:ЩG~n8(|Ïi<)LºE>ޓ:Wz0+ډHƔ4S'g$;@A4P!EfY=s$G%e~ <y<+MppN@ Ey/mاPȸL0ٹ^|(pMjJmpj䬀ɇR6hÌʞ^5rUQD R%b'Va͞./AoəEOT0>BQm*pTWQPʥNLcE9I(!Ha\烩9ҡVSw^(aVATJ#M9h}NjE fue Y'8mxRT o%V2YE$K0YA])đc&|S̓Nֈ?}"/ uV[9Q3i 5 Zͱ2Di XLa)jTZ&WK46W+YղrZm^TCMTHt U+SU6j SjdUdR MNVU@mXBl X:S+Q՞ZکIuEuEʌ{ͧ&Byeƣ6cef% }fЄO+LY^iy]V&1cFW:j n77OK' ˢEGgW]Tt46qZ]fHwJU/9:ͲDlz&%~f*)t|;(dJrzN:g&|f̦HުH&a -W?D]"F>JBXa')[]Lh+ɉ$ + ;l $|?4o9* *_5 (8Udd%C[ | M-8H ʡ;D^S7nc*ժe |v-gͤޕ|9MK@t.5LYrX Ȉ6XAƩmaMtC_x`.N^RQP%"jw忡ԍqZ"Be~*ꪀҗșkM)1-9jPh9M^#J` a"a/f%f\R-n끝fu`ީ#|d TѸQ_ (ءsyy^ޭsa&ٔϪ}ӛTܴws? ⲇ8A!NW.hrm%LHA{Rg7%M6~XSHap0J"jQ>7mlD6iD1{Dl&smgs!Ϙ JO9jwc;͇m^if]BY7vG5ET>@2["ۣ֞λY_n#d9Н~g04ѨkaD6QDCISpƻ\ٟxR|#\Y\g55'{> ,N}0$ ?8 N&H^\ BZ|)[f&wX(0mXGxz-8# aaXp}@EYt\9z@;T@M9Dy+H͠gjG4%fEbūaׯfm\knT֧4 _ dpA漃|&ަ| Yzy¡gd݅Kp=xwFim+1χpi6x{ZcТlf0q,y bLѾѪ{~1|خ 㺵]gouУRLGHu <AV!NKkK,Dn9i@?z=q9Cάk#{#FpތuuGv7Gz#xRMfըL3m~5Situ=O$5ӵGLNf$_A&`VД}Y_ocI8MvB,eqWvhkV*9pz#cGfS3Mg;n&,c.b)熏-,p8b7/`g"vC⺕)6/Jf?q;$̾s@țֿIqQhNch $-2 q~Ihi-녍G.^cEÞyn.&ipVlqG {Ⴝ݅ZdW oYܦ h͜FVs^AC8L3"U[ ˞huʸt#~T`7į~oyª &ncnt=<643`Pe})B,jz.ğpD|-b<Gzgf%NazFL*KT8gJ z!XزmwL XYkb-Iz6ndgtvgmI.pmmZӑjK SK8by3NXWo$-`9z:r SZ)-6LVCn"1_ I$76Knn˾>l-}K/DŠ*̂Xb:&N~gͱ:sglJ]'>c˷om^x ;bw+%O<Vz,>rQaesClTG#(29( -EDj֥`%E얇DYj'}{;id2BVM30uГ̪?MbYiYwv+Y}:ӟBcSݠz^OJKUjD$ZZH/2$IB˼_¢ɤSLed^?О@4IƽG;}Ӵ&4@5a:O(P~̫lw5w}[:e^G>'Ka?OK\wF\d:g۽ aZd<09?g\I֢Z=2< Ɠ >/0E~v׌-f0]9 b 3wq„eSA=F-ۓYȮ4xVNT0`a>k9(2A{^1~vO<(,X`>c0pZp8Pֱ +iXj˞jh7`abgEӡpZQ؈ܥ5eSΰX^Gl Le uD&LuNKiiT!jj WvTW|(qR gNAjf<Jܧ/k5.-j^d `ZQ/b(.PH qN{%(,OT]ėvOʎ$Y*+UWlhA3hJIRhc +iOVx4rerx3lsMl Lf'$GH)7uJApyz>^=fDή㚃; 5ɝcF]SZ CSW7'KKږ-M iSq_\_kh{a;V촜e@,CN`> 2{v[.O5UIldf(r Fpe'o~o[X pd'Dp9Yk gøJ3u{x2>hTKM؟/;xUǝ}^ue O-~6 5;n~j? XmoZsbCTNJt֒|;?u/_D"j-6'ﭼß'@ r]e2Y􃯅dJ!54~ѠDqY]]'<r*$d5_mGkDъP+ s]TX =v4/pji6r6㚵# I[/oҪ  JZ>@҆0~pՓC$8*"`?مEK#??UoϴaߵкZlmQ~ֳ̢#7Px< :^JyL ~Ix#1 ,1pjdOqKN.%p |2\E)' bNigMտvGw يH6L5GYGme(!I:E)C-&qh AL?~Jss q\GÆn$qSDMSrTfU=p9ɖxr v*"̝Knf-}Ұ'"98e{|pqs!BaPIkys<ةѪTzEmRڹNm_̔w-M>qp۞j*ykhpVs,v%(Pf bMߊ 7ӔN ۶-=قdw1M-@ve b3іiPwP>MǽK<>:&.rOg_2/>C]8/q?bj6I>h]y1#Һ‚l/8aYլ!,"LGFb"Qj`8^iZ^NglH~n\ata6˓TZZg(ldy+&4ʕ8tV_˃9!%q0%N#Bjmep)awnjQl{bDueW앜yI0Z|%[ )\Wؠ>$^YD|'N;^<䦕qiJ3w-g9BNyצ9L]j+ s!8);>9<:5r-LYdC?NaՖIel x<1g1x? hk`6)0ad&˔0 KԵ& _rd<{cnGЮ ɷ,c8 au@02]q$űivh E&CύM jpZI8"8H<2MXW6A >ҏKd -lO.+ {g̸n$Β-'eaβg 3?n,k8iDwƩ5[k}:˚OcxVYbd`z 1u8زIg|nXNw_ެEg1^y2G>UO^b%VRV^ru8 @tǼ$'5ZhBsh?}hN9 iuV ؊V\=A1 ,SXoZ$qfNj/0EՙxSstH>(!Ir q -vp@pe(s˥{=sY4?.[{{2|&90-NeB$eCuqFA˖;A}­R K|3_QVFcMo-e 5+zK{On0;|dHt!h6O2PI6M)2̛c2na֟l]lhw \"<>qh;%{|}޵g W/pO峖)ͻA\އ-jϒi_7DEϗcUQ&Ϧ!bc-2bk{<"fXdzЊm$YG581hr&:Gdž9^i鬩R#k7גRSvfB l՛~ o(K 5Qά)/=ݔ_ I?>n-֞}g.20.GQxg< zpx(˰S7<::WJқT05InY#z sZ/g|!ZxYoxbZwћLVl`g â +;ƅ7I /'>[p>@̽*oΠqv|ku lB F7G Sx V7+MHĠwT7^ mZWprjlkFaw3׃1|ھw`ߑeSI?)Gᕗ-w\/[f8,7|I%dgaܴ7#Rh\ijP6psg :"4!?ޗἺ)bICbNc}S#s#F쎶b?$%'1$$IOUi$/'1}=Iiu '+M9R}cgƟpgl؞?3(OtYjֶߧTX i3ig܅>!4fd'_Q_QHdwDZO_ 7ɴ'E, ]vrR)M3u;HF:^*)">-D04KB#ai5_h)W,?A QͯTB'U T:t/  ֔Xw|_ʧWMTy_>~_|?K;:Gzo=w$iZ{~ 1LNH=9#{6]˹p!4fMp){}4V5*"~ou] i$pǤۙ.wKcJENmqzťqɪ Y;^ oH}3'e2%5 ?2Y" ** bJ4jF/cO_󞕾͆,>pl7V6#/B #"f '-}o5u(wg-H( H~Vh!NR%?j>(f^}cyTQg Y܌wҞU}>P;8Ɲ4 lNԢx47"SYŦ%vj288kݡ3$v;F 寊6*-.DBw)Dć 9n m\6 6V;m>n\Rƅ%m߻3 L|8֯F7'U;]P@I~~b8r`hqg~1ƹr`q&v%^OӘO$$?|QkT kϰiӟO-{1هEI#!0%΁qǽ'ÈUh&f.E@1+uz:6EI-+Ir ߇#+I89"`]p?S!9>vxycLlHy[`Ĵ7€MχJd.^<1Cy g:~2v:~] ŦHޗ R~'x~ Xb"p.yS<\Mt8,O6[#U(n,`_߉d…(&ўn d1λQgȱJx bm~[ȃu4ooe^gB{d{ ڥtpM)n v秊P+iO|ϓRVkC9ໃڂjkkF%4_*4YBOdIxa>^HI.]jFO0)fJ"N\2 Zzbb'MEVj+Fre~g ۋ}I^R\jMx#^`b ^i<|Oa`>x!h|@h=ҕ5!tJ֐G'ϸ‹ާ쁅0"ҡetIuߌ?qR6RPt{sk栓Q+[; jB"f;Fg˾7QRl-]ZϿ(2x'8,&P;8%^͠;JI]zci 4_`Ӱ'Xtoި'ڐo?j=;I?ޏ17joyfLmA qiOa){R^s4%1(NjUO]p9rؕ2Atj- bzT{<êR_|_breR[`N"ߪH98uTVQ޵mZ+tWuzC,WGi/ymrʲϼמ_]hbo( EcP}Us>wykE (w.euwKsq > )܍ и8eV0}#X^E+اDíݻ\Oto@s_Iyl p#ՐhW\YY^.Oa$`&cXB˹FܹV)Hݥ8(_%G;_fZػ H/6s}:gdJnI nsd2\&d, ;i?.9c+җ}{UȂU 7@?A W%;w4+lE$Ih|JX Ÿck›1!ZI{f(XWu;Hu&uz0WDDw` PCԲ]E@F_׬$r)a4 wY ]mGTpyN/`xmݬֵQ)'N (Y]zM,ݶw7c|P^vDΗƏDoǖZmq+6{'Q\ot;Z-!qa $j#!樜hi0VQ _6eU pބe[GtXX]>cAxlXPiZZggk vZ8"ެoY8PqD/'{PpD#1`Ipb^Y!^9T fQ~H)ô0"|_`'B$:Sp!T*Op_jZR;pۊE b0o"xc;E!hzw %'ǞhԵkF:9 ;-mEBNɡ8ޓշQy8!ۣ-0jGX'4y\>…ecsI0&xNBwj lɧ]`o 71 9[g@B;;(pr ֑V`3z& lᇷmk6q|f|3)3~1ДrA" "jHvJĥV˺8Q9ױvlPܒaCto 9芄EI4)*٪Ƈq&4c XXPY/'.nA9k'xG*I(ef. z_4ǽ|$MC.Ԇhsٸ4:?k\L S+rkʥ$Ne p/[/3| /tf%}k͈]NpR\Dcp4^ÑbW5`r@Eݛ3ߧXgUEA#36{XK4 U mo*spiЧF4Mq~[ =X~4bcB{Mj.I "wmBJ(L&Ɠ #֔W6LMDNi+;8AhtmVQ֘)/Gsl-S|U2S.,]JMӀ-:gHd%T :lfIq$"6,+ Ln3߹؛ZZɚEsբhOF['Ɔ)՜eh8@-`:gGO&85Jqx9.}4?8G4Plȗ6G{01'複)*YIxTU\JYfR:F1Fy I \dUch&@_Є&!?7!4!݄tl_WWO'1b/쓓9wʝFDпBB#8o8qЎ[P:pV)c-y (G@+VRZ?K,8BB8l]:ăH=JGf. N#vrtzb;phn4Ia(m8VJC3Ul! Ac.cY3iEvAdkoa>_GIC䴑[aKpEƋ 0| >֕5>@`cu.M#e3OY9v%dmB4O3!f $!qd~s~.M"Dhqx ,, evid0v&zixElm3 g2p-c+a jp_Rf)h(z#$av'*|@> 9Cf6IHP\JOk i+6͘zdقWM"gQ+Y F=^ ЪɪZKQjlg![eZVd o؀o ]6V鏉b՜4fc>D3adqd1t$"l›-Ђz-=B6fW.(TM8uabUH~$mlZʥ!7, 7ufj@kR۰ WxDqהp50GFēU!(" Zӱ\$us|( riB薲4- !B7dCt21)LT̄H%)?Gv=-U XrchQ&*M$?R׋7Z}a-fʡy[^.La0$vNJNhcP !vZ?K&bJ G{ ,+&HEZr B*O׊Tlظ~9b]@TYe+}w 9jp6/*q3?\hbtba4T*gb z{8s(DF[#Nk ͦ4>%I+=8Gx"mgGW+U bu3`~ \AmXְ%5Q`WsT-#}\q\חػ.OpWn~ɨ [[BY%s?W.R vTKayZ:_zߝ)b]h3[GZf\~d Bc<^.}E6)}@lO>K_㬃f F")Z̷2Xk"Bl5-6$2d=[T6+{Ltˤ⡸LpٿK|͡07Ǘ+ѿ[ή=5DILת7#+T; 1*"wu6.9$~)H(N]$^ B:Ƥt D\α'uDN\$brʑ\0VL|R.c&1'J2q^H@@pMM"dN}VDՄP)d&6\[1mk*"Fʬ &dTSCU;Ϫ_݂[Ay 奄h1V)fJRoJY-6JwNL*$*єP)6k2|S(D"ua% e(KVl2%6*rJ-Y2lRxҚ!2|M6ح6'꬈vyIuݘh@4)AYmBD^-js~"T)i(M,zx3Ds ʻ7s K61A(tJb`0;HaJݭ e%.R@|e3)3B4 vN\ #+̍K=b\=aR2cE3.-o&ΈFQk3?|.:'ZO=iaOn: S$pշpz'6 עIqZ_!8bdb2kvX>g<Ɍøs֪}Vx/ X }&i[|=x#HX74%)cNA͹VQV›V pqq<ש7F#wn/"zGny&@PFiߎ=8T1WXC”NWLy\6{Zi Ov};Z|i8e,&Z:oĮ6gh$֌68fK2ԏ_kK^azPā B(SM :-oc}ȉG9a_8vdP bĈ=Dsx >Db5ͽZPp_p.*.X#%1,yW8,W5 XAI 9ùo ʡ' 9''}|( #`@?F)4pJ 5#J32k~VUC,4 ˀ,Ҵ+P :F !Oy(Jomh Gx1OEJ;qbh\\WƗ7L|=\BPĬubfZJT~}Fio8֋@lkލf ) jqgO [c!#I"߁|%:lK2=ԍJ ǣð7,~S? ! yV "ۃ`0ξ$(~Ӟ B$\ǖ6Ȳd(p@Q;h,pn@ma;txe ZŽ4{ebH XRaOIp< efI-PLJf7/g!KAfA$D"',MC)7_AAvA)7pXyqZ891-ͥ^麶y!$uj6W4] ~Tݵ]*p}\x)(dbElvOԄg :ť%N޵46qqC8~NNWNFwx xc'459 '`N!<70p͡W?ծ~7@S81?Ҷ=鍄>.t~W?χ_G.C#9?Oo{T:xxy5)H!ZH1joj:m£taoµK봆G^8@a."x^c0F 1Ƽ(w&x a @;HTf<?n %$!hTMB:I%$nqH1 Sp"?Y h xl@`ǿ),#տ5F|fe2T<4m)03$D̾-oA q_n fg!U0ikmOSҲD+96iCfačNx:R8g;MqryB'Իoc{\ NpuNԿ~M3ۭ 7yFyU |[&#'߰ <^l1ػEl4 CbQ=$MUEvM@En!L4o1‡,+K(mxt-ȏ[ ;mwH L [zt#2x,as]"aˍY!81Zjڮe-@~9k~`뀯O/D[z>NI Hv&[q1y:«L0PDʉ0Wڤ󁸋f6ߓvr1ΖV6:0b\90?x:2~Zr:ˍi" [Xܨ{r^Ҷth,،rCt=vdP㗦㾵~#或߂xAOw%[e fyz]Ѿ>"W?7o|g]Jt'vV#)zBH#5\L¼A$VyO5<=RumK 0O}pRJu3{ ٧B@K&$LR?8Cԁ ,tE2њ5ӑqegfEu%bN 0 { fmt.Fprintf(&s, "[bits=%d]", i.Bits) } return s.String() } func (i *Int) TypeName() string { return i.Name } func (i *Int) size() uint32 { return i.Size } func (i *Int) walk(*typeDeque) {} func (i *Int) copy() Type { cpy := *i return &cpy } func (i *Int) isBitfield() bool { return i.OffsetBits > 0 } // Pointer is a pointer to another type. type Pointer struct { TypeID Target Type } func (p *Pointer) String() string { return fmt.Sprintf("pointer#%d[target=#%d]", p.TypeID, p.Target.ID()) } func (p *Pointer) size() uint32 { return 8 } func (p *Pointer) walk(tdq *typeDeque) { tdq.push(&p.Target) } func (p *Pointer) copy() Type { cpy := *p return &cpy } // Array is an array with a fixed number of elements. type Array struct { TypeID Type Type Nelems uint32 } func (arr *Array) String() string { return fmt.Sprintf("array#%d[type=#%d n=%d]", arr.TypeID, arr.Type.ID(), arr.Nelems) } func (arr *Array) walk(tdq *typeDeque) { tdq.push(&arr.Type) } func (arr *Array) copy() Type { cpy := *arr return &cpy } // Struct is a compound type of consecutive members. type Struct struct { TypeID Name string // The size of the struct including padding, in bytes Size uint32 Members []Member } func (s *Struct) String() string { return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name) } func (s *Struct) TypeName() string { return s.Name } func (s *Struct) size() uint32 { return s.Size } func (s *Struct) walk(tdq *typeDeque) { for i := range s.Members { tdq.push(&s.Members[i].Type) } } 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 { TypeID Name string // The size of the union including padding, in bytes. Size uint32 Members []Member } func (u *Union) String() string { return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name) } func (u *Union) TypeName() string { return u.Name } func (u *Union) size() uint32 { return u.Size } func (u *Union) walk(tdq *typeDeque) { for i := range u.Members { tdq.push(&u.Members[i].Type) } } 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 { members() []Member } var ( _ composite = (*Struct)(nil) _ composite = (*Union)(nil) ) // Member is part of a Struct or Union. // // It is not a valid Type. type Member struct { Name string Type Type // OffsetBits is the bit offset of this member. OffsetBits uint32 BitfieldSize uint32 } // Enum lists possible values. type Enum struct { TypeID Name string Values []EnumValue } func (e *Enum) String() string { return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name) } 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 int32 } func (e *Enum) size() uint32 { return 4 } func (e *Enum) walk(*typeDeque) {} func (e *Enum) copy() Type { cpy := *e cpy.Values = make([]EnumValue, len(e.Values)) copy(cpy.Values, e.Values) return &cpy } // 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 { TypeID Name string Kind FwdKind } func (f *Fwd) String() string { return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name) } func (f *Fwd) TypeName() string { return f.Name } func (f *Fwd) walk(*typeDeque) {} func (f *Fwd) copy() Type { cpy := *f return &cpy } // Typedef is an alias of a Type. type Typedef struct { TypeID Name string Type Type } func (td *Typedef) String() string { return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID()) } func (td *Typedef) TypeName() string { return td.Name } func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) } func (td *Typedef) copy() Type { cpy := *td return &cpy } // Volatile is a qualifier. type Volatile struct { TypeID Type Type } func (v *Volatile) String() string { return fmt.Sprintf("volatile#%d[#%d]", v.TypeID, v.Type.ID()) } func (v *Volatile) qualify() Type { return v.Type } func (v *Volatile) walk(tdq *typeDeque) { tdq.push(&v.Type) } func (v *Volatile) copy() Type { cpy := *v return &cpy } // Const is a qualifier. type Const struct { TypeID Type Type } func (c *Const) String() string { return fmt.Sprintf("const#%d[#%d]", c.TypeID, c.Type.ID()) } func (c *Const) qualify() Type { return c.Type } func (c *Const) walk(tdq *typeDeque) { tdq.push(&c.Type) } func (c *Const) copy() Type { cpy := *c return &cpy } // Restrict is a qualifier. type Restrict struct { TypeID Type Type } func (r *Restrict) String() string { return fmt.Sprintf("restrict#%d[#%d]", r.TypeID, r.Type.ID()) } func (r *Restrict) qualify() Type { return r.Type } func (r *Restrict) walk(tdq *typeDeque) { tdq.push(&r.Type) } func (r *Restrict) copy() Type { cpy := *r return &cpy } // Func is a function definition. type Func struct { TypeID Name string Type Type Linkage FuncLinkage } func (f *Func) String() string { return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID()) } func (f *Func) TypeName() string { return f.Name } func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) } func (f *Func) copy() Type { cpy := *f return &cpy } // FuncProto is a function declaration. type FuncProto struct { TypeID Return Type Params []FuncParam } func (fp *FuncProto) String() string { var s strings.Builder fmt.Fprintf(&s, "proto#%d[", fp.TypeID) for _, param := range fp.Params { fmt.Fprintf(&s, "%q=#%d, ", param.Name, param.Type.ID()) } fmt.Fprintf(&s, "return=#%d]", fp.Return.ID()) return s.String() } func (fp *FuncProto) walk(tdq *typeDeque) { tdq.push(&fp.Return) for i := range fp.Params { tdq.push(&fp.Params[i].Type) } } 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 { TypeID Name string Type Type Linkage VarLinkage } func (v *Var) String() string { return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name) } func (v *Var) TypeName() string { return v.Name } func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) } func (v *Var) copy() Type { cpy := *v return &cpy } // Datasec is a global program section containing data. type Datasec struct { TypeID Name string Size uint32 Vars []VarSecinfo } func (ds *Datasec) String() string { return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name) } func (ds *Datasec) TypeName() string { return ds.Name } func (ds *Datasec) size() uint32 { return ds.Size } func (ds *Datasec) walk(tdq *typeDeque) { for i := range ds.Vars { tdq.push(&ds.Vars[i].Type) } } 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 { Type Type Offset uint32 Size uint32 } // Float is a float of a given length. type Float struct { TypeID Name string // The size of the float in bytes. Size uint32 } func (f *Float) String() string { return fmt.Sprintf("float%d#%d[%q]", f.Size*8, f.TypeID, f.Name) } func (f *Float) TypeName() string { return f.Name } func (f *Float) size() uint32 { return f.Size } func (f *Float) walk(*typeDeque) {} func (f *Float) copy() Type { cpy := *f 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) ) // 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("unsized type %T", typ) } 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) } // copy a Type recursively. // // typ may form a cycle. // // Returns any errors from transform verbatim. func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) { copies := make(copier) return typ, copies.copy(&typ, transform) } // copy a slice of Types recursively. // // Types may form a cycle. // // Returns any errors from transform verbatim. func copyTypes(types []Type, transform func(Type) (Type, error)) ([]Type, error) { result := make([]Type, len(types)) copy(result, types) copies := make(copier) for i := range result { if err := copies.copy(&result[i], transform); err != nil { return nil, err } } return result, nil } type copier map[Type]Type func (c copier) copy(typ *Type, transform func(Type) (Type, error)) error { var work typeDeque for t := typ; t != nil; t = work.pop() { // *t is the identity of the type. if cpy := c[*t]; cpy != nil { *t = cpy continue } var cpy Type if transform != nil { tf, err := transform(*t) if err != nil { return fmt.Errorf("copy %s: %w", *t, err) } cpy = tf.copy() } else { cpy = (*t).copy() } c[*t] = cpy *t = cpy // Mark any nested types for copying. cpy.walk(&work) } return nil } // typeDeque keeps track of pointers to types which still // need to be visited. type typeDeque struct { types []*Type read, write uint64 mask uint64 } func (dq *typeDeque) empty() bool { return dq.read == dq.write } // push adds a type to the stack. func (dq *typeDeque) push(t *Type) { if dq.write-dq.read < uint64(len(dq.types)) { dq.types[dq.write&dq.mask] = t dq.write++ return } new := len(dq.types) * 2 if new == 0 { new = 8 } types := make([]*Type, new) pivot := dq.read & dq.mask n := copy(types, dq.types[pivot:]) n += copy(types[n:], dq.types[:pivot]) types[n] = t dq.types = types dq.mask = uint64(new) - 1 dq.read, dq.write = 0, uint64(n+1) } // shift returns the first element or null. func (dq *typeDeque) shift() *Type { if dq.empty() { return nil } index := dq.read & dq.mask t := dq.types[index] dq.types[index] = nil dq.read++ return t } // pop returns the last element or null. func (dq *typeDeque) pop() *Type { if dq.empty() { return nil } dq.write-- index := dq.write & dq.mask t := dq.types[index] dq.types[index] = nil return t } // all returns all elements. // // The deque is empty after calling this method. func (dq *typeDeque) all() []*Type { length := dq.write - dq.read types := make([]*Type, 0, length) for t := dq.shift(); t != nil; t = dq.shift() { types = append(types, t) } return types } // inflateRawTypes takes a list of raw btf types linked via type IDs, and turns // it into a graph of Types connected via pointers. // // Returns a map of named types (so, where NameOff is non-zero) and 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) (types []Type, namedTypes map[string][]NamedType, err error) { type fixupDef struct { id TypeID expectedKind btfKind typ *Type } var fixups []fixupDef fixup := func(id TypeID, expectedKind btfKind, typ *Type) { fixups = append(fixups, fixupDef{id, expectedKind, typ}) } 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) } m := Member{ Name: name, OffsetBits: btfMember.Offset, } if kindFlag { m.BitfieldSize = btfMember.Offset >> 24 m.OffsetBits &= 0xffffff } members = append(members, m) } for i := range members { fixup(raw[i].Type, kindUnknown, &members[i].Type) } return members, nil } types = make([]Type, 0, len(rawTypes)) types = append(types, (*Void)(nil)) namedTypes = make(map[string][]NamedType) for i, raw := range rawTypes { var ( // Void is defined to always be type ID 0, and is thus // omitted from BTF. id = TypeID(i + 1) typ Type ) name, err := rawStrings.Lookup(raw.NameOff) if err != nil { return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err) } switch raw.Kind() { case kindInt: encoding, offset, bits := intEncoding(*raw.data.(*uint32)) typ = &Int{id, name, raw.Size(), encoding, offset, bits} case kindPointer: ptr := &Pointer{id, nil} fixup(raw.Type(), kindUnknown, &ptr.Target) typ = ptr case kindArray: btfArr := raw.data.(*btfArray) // IndexType is unused according to btf.rst. // Don't make it available right now. arr := &Array{id, nil, btfArr.Nelems} fixup(btfArr.Type, kindUnknown, &arr.Type) typ = arr case kindStruct: members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag()) if err != nil { return nil, nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } typ = &Struct{id, name, raw.Size(), members} case kindUnion: members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag()) if err != nil { return nil, nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } typ = &Union{id, name, raw.Size(), members} case kindEnum: rawvals := raw.data.([]btfEnum) vals := make([]EnumValue, 0, len(rawvals)) for i, btfVal := range rawvals { name, err := rawStrings.Lookup(btfVal.NameOff) if err != nil { return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err) } vals = append(vals, EnumValue{ Name: name, Value: btfVal.Val, }) } typ = &Enum{id, name, vals} case kindForward: if raw.KindFlag() { typ = &Fwd{id, name, FwdUnion} } else { typ = &Fwd{id, name, FwdStruct} } case kindTypedef: typedef := &Typedef{id, name, nil} fixup(raw.Type(), kindUnknown, &typedef.Type) typ = typedef case kindVolatile: volatile := &Volatile{id, nil} fixup(raw.Type(), kindUnknown, &volatile.Type) typ = volatile case kindConst: cnst := &Const{id, nil} fixup(raw.Type(), kindUnknown, &cnst.Type) typ = cnst case kindRestrict: restrict := &Restrict{id, nil} fixup(raw.Type(), kindUnknown, &restrict.Type) typ = restrict case kindFunc: fn := &Func{id, name, nil, raw.Linkage()} fixup(raw.Type(), kindFuncProto, &fn.Type) 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, 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, kindUnknown, ¶ms[i].Type) } fp := &FuncProto{id, nil, params} fixup(raw.Type(), kindUnknown, &fp.Return) typ = fp case kindVar: variable := raw.data.(*btfVariable) v := &Var{id, name, nil, VarLinkage(variable.Linkage)} fixup(raw.Type(), kindUnknown, &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, kindVar, &vars[i].Type) } typ = &Datasec{id, name, raw.SizeType, vars} case kindFloat: typ = &Float{id, name, raw.Size()} default: return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) } types = append(types, typ) if named, ok := typ.(NamedType); ok { if name := essentialName(named.TypeName()); name != "" { namedTypes[name] = append(namedTypes[name], named) } } } for _, fixup := range fixups { i := int(fixup.id) if i >= len(types) { return nil, nil, fmt.Errorf("reference to invalid type id: %d", fixup.id) } // Default void (id 0) to unknown rawKind := kindUnknown if i > 0 { rawKind = rawTypes[i-1].Kind() } if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected { return nil, nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) } *fixup.typ = types[i] } return types, namedTypes, nil } // essentialName returns name without a ___ suffix. func essentialName(name string) string { lastIdx := strings.LastIndex(name, "___") if lastIdx > 0 { return name[:lastIdx] } return name } golang-github-cilium-ebpf-0.7.0/internal/btf/types_test.go000066400000000000000000000114531414524555700236000ustar00rootroot00000000000000package btf import ( "fmt" "testing" 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}}, {4, &Enum{}}, {0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}}, {12, &Array{Type: &Enum{}, 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 TestCopyType(t *testing.T) { _, _ = copyType((*Void)(nil), nil) in := &Int{Size: 4} out, _ := copyType(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) { _, _ = copyType(newCyclicalType(2), nil) }) t.Run("identity", func(t *testing.T) { u16 := &Int{Size: 2} out, _ := copyType(&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) }) } // 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{} } func TestType(t *testing.T) { types := []func() Type{ func() Type { return &Void{} }, func() Type { return &Int{Size: 2, Bits: 3} }, 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{}}}, } }, } 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 first, second typeDeque typ.walk(&first) typ.walk(&second) if diff := cmp.Diff(first.all(), second.all(), compareTypes); diff != "" { t.Errorf("Walk mismatch (-want +got):\n%s", diff) } }) } } func TestTypeDeque(t *testing.T) { a, b := new(Type), new(Type) t.Run("pop", func(t *testing.T) { var td typeDeque td.push(a) td.push(b) if td.pop() != b { t.Error("Didn't pop b first") } if td.pop() != a { t.Error("Didn't pop a second") } if td.pop() != nil { t.Error("Didn't pop nil") } }) t.Run("shift", func(t *testing.T) { var td typeDeque td.push(a) td.push(b) if td.shift() != a { t.Error("Didn't shift a second") } if td.shift() != b { t.Error("Didn't shift b first") } if td.shift() != nil { t.Error("Didn't shift nil") } }) t.Run("push", func(t *testing.T) { var td typeDeque td.push(a) td.push(b) td.shift() ts := make([]Type, 12) for i := range ts { td.push(&ts[i]) } if td.shift() != b { t.Error("Didn't shift b first") } for i := range ts { if td.shift() != &ts[i] { t.Fatal("Shifted wrong Type at pos", i) } } }) t.Run("all", func(t *testing.T) { var td typeDeque td.push(a) td.push(b) all := td.all() if len(all) != 2 { t.Fatal("Expected 2 elements, got", len(all)) } if all[0] != a || all[1] != b { t.Fatal("Elements don't match") } }) } 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} } } ptr.Target = prev return ptr } golang-github-cilium-ebpf-0.7.0/internal/cpu.go000066400000000000000000000025411414524555700214070ustar00rootroot00000000000000package internal import ( "fmt" "os" "strings" "sync" ) var sysCPU struct { once sync.Once err error num int } // PossibleCPUs returns the max number of CPUs a system may possibly have // Logical CPU numbers must be of the form 0-n func PossibleCPUs() (int, error) { sysCPU.once.Do(func() { sysCPU.num, sysCPU.err = parseCPUsFromFile("/sys/devices/system/cpu/possible") }) return sysCPU.num, sysCPU.err } 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.7.0/internal/cpu_test.go000066400000000000000000000010061414524555700224410ustar00rootroot00000000000000package 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.7.0/internal/elf.go000066400000000000000000000024241414524555700213660ustar00rootroot00000000000000package 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 } // 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 } golang-github-cilium-ebpf-0.7.0/internal/endian.go000066400000000000000000000011231414524555700220510ustar00rootroot00000000000000package internal import ( "encoding/binary" "unsafe" ) // NativeEndian is set to either binary.BigEndian or binary.LittleEndian, // depending on the host's endianness. var NativeEndian binary.ByteOrder // Clang is set to either "el" or "eb" depending on the host's endianness. var ClangEndian string func init() { if isBigEndian() { NativeEndian = binary.BigEndian ClangEndian = "eb" } else { NativeEndian = binary.LittleEndian ClangEndian = "el" } } func isBigEndian() (ret bool) { i := int(0x1) bs := (*[int(unsafe.Sizeof(i))]byte)(unsafe.Pointer(&i)) return bs[0] == 0 } golang-github-cilium-ebpf-0.7.0/internal/errors.go000066400000000000000000000020511414524555700221300ustar00rootroot00000000000000package internal import ( "bytes" "errors" "fmt" "strings" "github.com/cilium/ebpf/internal/unix" ) // ErrorWithLog returns an error that includes logs from the // kernel verifier. // // logErr should be the error returned by the syscall that generated // the log. It is used to check for truncation of the output. func ErrorWithLog(err error, log []byte, logErr error) error { logStr := strings.Trim(CString(log), "\t\r\n ") if errors.Is(logErr, unix.ENOSPC) { logStr += " (truncated...)" } return &VerifierError{err, logStr} } // VerifierError includes information from the eBPF verifier. type VerifierError struct { cause error log string } func (le *VerifierError) Unwrap() error { return le.cause } func (le *VerifierError) Error() string { if le.log == "" { return le.cause.Error() } return fmt.Sprintf("%s: %s", le.cause, le.log) } // CString turns a NUL / zero terminated byte buffer into a string. func CString(in []byte) string { inLen := bytes.IndexByte(in, 0) if inLen == -1 { return "" } return string(in[:inLen]) } golang-github-cilium-ebpf-0.7.0/internal/fd.go000066400000000000000000000020521414524555700212060ustar00rootroot00000000000000package internal import ( "errors" "fmt" "os" "runtime" "strconv" "github.com/cilium/ebpf/internal/unix" ) var ErrClosedFd = errors.New("use of closed file descriptor") type FD struct { raw int64 } func NewFD(value uint32) *FD { fd := &FD{int64(value)} runtime.SetFinalizer(fd, (*FD).Close) return fd } func (fd *FD) String() string { return strconv.FormatInt(fd.raw, 10) } func (fd *FD) Value() (uint32, error) { if fd.raw < 0 { return 0, ErrClosedFd } return uint32(fd.raw), nil } func (fd *FD) Close() error { if fd.raw < 0 { return nil } value := int(fd.raw) fd.raw = -1 fd.Forget() return unix.Close(value) } func (fd *FD) Forget() { runtime.SetFinalizer(fd, nil) } func (fd *FD) Dup() (*FD, error) { if fd.raw < 0 { return nil, ErrClosedFd } dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) if err != nil { return nil, fmt.Errorf("can't dup fd: %v", err) } return NewFD(uint32(dup)), nil } func (fd *FD) File(name string) *os.File { fd.Forget() return os.NewFile(uintptr(fd.raw), name) } golang-github-cilium-ebpf-0.7.0/internal/feature.go000066400000000000000000000047471414524555700222650ustar00rootroot00000000000000package 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 } type featureTest struct { sync.RWMutex successful 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 // FeatureTest wraps a function so that it is run at most once. // // name should identify the tested feature, while version must be in the // form Major.Minor[.Patch]. // // Returns an error wrapping ErrNotSupported if the feature is not supported. func FeatureTest(name, version string, fn FeatureTestFn) func() error { v, err := NewVersion(version) if err != nil { return func() error { return err } } ft := new(featureTest) return func() error { ft.RLock() if ft.successful { defer ft.RUnlock() return ft.result } ft.RUnlock() ft.Lock() defer ft.Unlock() // check one more time on the off // chance that two go routines // were able to call into the write // lock if ft.successful { return ft.result } err := fn() switch { case errors.Is(err, ErrNotSupported): ft.result = &UnsupportedFeatureError{ MinimumVersion: v, Name: name, } fallthrough case err == nil: ft.successful = true default: // 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", name, err) } return ft.result } } golang-github-cilium-ebpf-0.7.0/internal/feature_test.go000066400000000000000000000022041414524555700233060ustar00rootroot00000000000000package internal import ( "errors" "strings" "testing" ) func TestFeatureTest(t *testing.T) { var called bool fn := FeatureTest("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 = FeatureTest("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 = FeatureTest("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.7.0/internal/io.go000066400000000000000000000005021414524555700212220ustar00rootroot00000000000000package internal import "errors" // 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 } golang-github-cilium-ebpf-0.7.0/internal/io_test.go000066400000000000000000000006011414524555700222610ustar00rootroot00000000000000package 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.7.0/internal/pinning.go000066400000000000000000000017751414524555700222720ustar00rootroot00000000000000package internal import ( "errors" "fmt" "os" "github.com/cilium/ebpf/internal/unix" ) func Pin(currentPath, newPath string, fd *FD) error { if newPath == "" { return errors.New("given pinning path cannot be empty") } if currentPath == newPath { return nil } if currentPath == "" { return BPFObjPin(newPath, fd) } var err error // Renameat2 is used instead of os.Rename to disallow the new path replacing // an existing path. if err = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE); 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 BPFObjPin(newPath, fd) } 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.7.0/internal/ptr.go000066400000000000000000000012021414524555700214160ustar00rootroot00000000000000package internal 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])} } // 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)} } golang-github-cilium-ebpf-0.7.0/internal/ptr_32_be.go000066400000000000000000000004011414524555700223700ustar00rootroot00000000000000//go:build armbe || mips || mips64p32 // +build armbe mips mips64p32 package internal 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.7.0/internal/ptr_32_le.go000066400000000000000000000004451414524555700224120ustar00rootroot00000000000000//go:build 386 || amd64p32 || arm || mipsle || mips64p32le // +build 386 amd64p32 arm mipsle mips64p32le package internal 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.7.0/internal/ptr_64.go000066400000000000000000000005341414524555700217360ustar00rootroot00000000000000//go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32 // +build !386,!amd64p32,!arm,!mipsle,!mips64p32le,!armbe,!mips,!mips64p32 package internal 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.7.0/internal/syscall.go000066400000000000000000000152771414524555700223040ustar00rootroot00000000000000package internal import ( "errors" "fmt" "path/filepath" "runtime" "syscall" "unsafe" "github.com/cilium/ebpf/internal/unix" ) //go:generate stringer -output syscall_string.go -type=BPFCmd // BPFCmd identifies a subcommand of the bpf syscall. type BPFCmd int // Well known BPF commands. const ( BPF_MAP_CREATE BPFCmd = iota BPF_MAP_LOOKUP_ELEM BPF_MAP_UPDATE_ELEM BPF_MAP_DELETE_ELEM BPF_MAP_GET_NEXT_KEY BPF_PROG_LOAD BPF_OBJ_PIN BPF_OBJ_GET BPF_PROG_ATTACH BPF_PROG_DETACH BPF_PROG_TEST_RUN BPF_PROG_GET_NEXT_ID BPF_MAP_GET_NEXT_ID BPF_PROG_GET_FD_BY_ID BPF_MAP_GET_FD_BY_ID BPF_OBJ_GET_INFO_BY_FD BPF_PROG_QUERY BPF_RAW_TRACEPOINT_OPEN BPF_BTF_LOAD BPF_BTF_GET_FD_BY_ID BPF_TASK_FD_QUERY BPF_MAP_LOOKUP_AND_DELETE_ELEM BPF_MAP_FREEZE BPF_BTF_GET_NEXT_ID BPF_MAP_LOOKUP_BATCH BPF_MAP_LOOKUP_AND_DELETE_BATCH BPF_MAP_UPDATE_BATCH BPF_MAP_DELETE_BATCH BPF_LINK_CREATE BPF_LINK_UPDATE BPF_LINK_GET_FD_BY_ID BPF_LINK_GET_NEXT_ID BPF_ENABLE_STATS BPF_ITER_CREATE ) // BPF wraps SYS_BPF. // // Any pointers contained in attr must use the Pointer type from this package. func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) runtime.KeepAlive(attr) var err error if errNo != 0 { err = wrappedErrno{errNo} } return r1, err } type BPFProgLoadAttr struct { ProgType uint32 InsCount uint32 Instructions Pointer License Pointer LogLevel uint32 LogSize uint32 LogBuf Pointer KernelVersion uint32 // since 4.1 2541517c32be ProgFlags uint32 // since 4.11 e07b98d9bffe ProgName BPFObjName // since 4.15 067cae47771c ProgIfIndex uint32 // since 4.15 1f6f4cb7ba21 ExpectedAttachType uint32 // since 4.17 5e43f899b03a ProgBTFFd uint32 FuncInfoRecSize uint32 FuncInfo Pointer FuncInfoCnt uint32 LineInfoRecSize uint32 LineInfo Pointer LineInfoCnt uint32 AttachBTFID uint32 AttachProgFd uint32 } // BPFProgLoad wraps BPF_PROG_LOAD. func BPFProgLoad(attr *BPFProgLoadAttr) (*FD, error) { for { fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) // As of ~4.20 the verifier can be interrupted by a signal, // and returns EAGAIN in that case. if errors.Is(err, unix.EAGAIN) { continue } if err != nil { return nil, err } return NewFD(uint32(fd)), nil } } type BPFProgAttachAttr struct { TargetFd uint32 AttachBpfFd uint32 AttachType uint32 AttachFlags uint32 ReplaceBpfFd uint32 } func BPFProgAttach(attr *BPFProgAttachAttr) error { _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type BPFProgDetachAttr struct { TargetFd uint32 AttachBpfFd uint32 AttachType uint32 } func BPFProgDetach(attr *BPFProgDetachAttr) error { _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type BPFEnableStatsAttr struct { StatsType uint32 } func BPFEnableStats(attr *BPFEnableStatsAttr) (*FD, error) { ptr, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, fmt.Errorf("enable stats: %w", err) } return NewFD(uint32(ptr)), nil } type bpfObjAttr struct { fileName Pointer fd uint32 fileFlags uint32 } const bpfFSType = 0xcafe4a11 // BPFObjPin wraps BPF_OBJ_PIN. func BPFObjPin(fileName string, fd *FD) error { dirName := filepath.Dir(fileName) var statfs unix.Statfs_t if err := unix.Statfs(dirName, &statfs); err != nil { return err } if uint64(statfs.Type) != bpfFSType { return fmt.Errorf("%s is not on a bpf filesystem", fileName) } value, err := fd.Value() if err != nil { return err } attr := bpfObjAttr{ fileName: NewStringPointer(fileName), fd: value, } _, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return fmt.Errorf("pin object %s: %w", fileName, err) } return nil } // BPFObjGet wraps BPF_OBJ_GET. func BPFObjGet(fileName string, flags uint32) (*FD, error) { attr := bpfObjAttr{ fileName: NewStringPointer(fileName), fileFlags: flags, } ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return nil, fmt.Errorf("get object %s: %w", fileName, err) } return NewFD(uint32(ptr)), nil } type bpfObjGetInfoByFDAttr struct { fd uint32 infoLen uint32 info Pointer } // BPFObjGetInfoByFD wraps BPF_OBJ_GET_INFO_BY_FD. // // Available from 4.13. func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error { value, err := fd.Value() if err != nil { return err } attr := bpfObjGetInfoByFDAttr{ fd: value, infoLen: uint32(size), info: NewPointer(info), } _, err = BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return fmt.Errorf("fd %v: %w", fd, err) } return nil } type bpfGetFDByIDAttr struct { id uint32 next uint32 } // BPFObjGetInfoByFD wraps BPF_*_GET_FD_BY_ID. // // Available from 4.13. func BPFObjGetFDByID(cmd BPFCmd, id uint32) (*FD, error) { attr := bpfGetFDByIDAttr{ id: id, } ptr, err := BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return NewFD(uint32(ptr)), err } // BPFObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte // NewBPFObjName truncates the result if it is too long. func NewBPFObjName(name string) BPFObjName { var result BPFObjName copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) return result } type BPFMapCreateAttr struct { MapType uint32 KeySize uint32 ValueSize uint32 MaxEntries uint32 Flags uint32 InnerMapFd uint32 // since 4.12 56f668dfe00d NumaNode uint32 // since 4.14 96eabe7a40aa MapName BPFObjName // since 4.15 ad5b177bd73f MapIfIndex uint32 BTFFd uint32 BTFKeyTypeID uint32 BTFValueTypeID uint32 } func BPFMapCreate(attr *BPFMapCreateAttr) (*FD, error) { fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(uint32(fd)), nil } // 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 } type syscallError struct { error errno syscall.Errno } func SyscallError(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.7.0/internal/syscall_string.go000066400000000000000000000044771414524555700236720ustar00rootroot00000000000000// Code generated by "stringer -output syscall_string.go -type=BPFCmd"; DO NOT EDIT. package internal 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_MAP_CREATE-0] _ = x[BPF_MAP_LOOKUP_ELEM-1] _ = x[BPF_MAP_UPDATE_ELEM-2] _ = x[BPF_MAP_DELETE_ELEM-3] _ = x[BPF_MAP_GET_NEXT_KEY-4] _ = x[BPF_PROG_LOAD-5] _ = x[BPF_OBJ_PIN-6] _ = x[BPF_OBJ_GET-7] _ = x[BPF_PROG_ATTACH-8] _ = x[BPF_PROG_DETACH-9] _ = x[BPF_PROG_TEST_RUN-10] _ = x[BPF_PROG_GET_NEXT_ID-11] _ = x[BPF_MAP_GET_NEXT_ID-12] _ = x[BPF_PROG_GET_FD_BY_ID-13] _ = x[BPF_MAP_GET_FD_BY_ID-14] _ = x[BPF_OBJ_GET_INFO_BY_FD-15] _ = x[BPF_PROG_QUERY-16] _ = x[BPF_RAW_TRACEPOINT_OPEN-17] _ = x[BPF_BTF_LOAD-18] _ = x[BPF_BTF_GET_FD_BY_ID-19] _ = x[BPF_TASK_FD_QUERY-20] _ = x[BPF_MAP_LOOKUP_AND_DELETE_ELEM-21] _ = x[BPF_MAP_FREEZE-22] _ = x[BPF_BTF_GET_NEXT_ID-23] _ = x[BPF_MAP_LOOKUP_BATCH-24] _ = x[BPF_MAP_LOOKUP_AND_DELETE_BATCH-25] _ = x[BPF_MAP_UPDATE_BATCH-26] _ = x[BPF_MAP_DELETE_BATCH-27] _ = x[BPF_LINK_CREATE-28] _ = x[BPF_LINK_UPDATE-29] _ = x[BPF_LINK_GET_FD_BY_ID-30] _ = x[BPF_LINK_GET_NEXT_ID-31] _ = x[BPF_ENABLE_STATS-32] _ = x[BPF_ITER_CREATE-33] } const _BPFCmd_name = "BPF_MAP_CREATEBPF_MAP_LOOKUP_ELEMBPF_MAP_UPDATE_ELEMBPF_MAP_DELETE_ELEMBPF_MAP_GET_NEXT_KEYBPF_PROG_LOADBPF_OBJ_PINBPF_OBJ_GETBPF_PROG_ATTACHBPF_PROG_DETACHBPF_PROG_TEST_RUNBPF_PROG_GET_NEXT_IDBPF_MAP_GET_NEXT_IDBPF_PROG_GET_FD_BY_IDBPF_MAP_GET_FD_BY_IDBPF_OBJ_GET_INFO_BY_FDBPF_PROG_QUERYBPF_RAW_TRACEPOINT_OPENBPF_BTF_LOADBPF_BTF_GET_FD_BY_IDBPF_TASK_FD_QUERYBPF_MAP_LOOKUP_AND_DELETE_ELEMBPF_MAP_FREEZEBPF_BTF_GET_NEXT_IDBPF_MAP_LOOKUP_BATCHBPF_MAP_LOOKUP_AND_DELETE_BATCHBPF_MAP_UPDATE_BATCHBPF_MAP_DELETE_BATCHBPF_LINK_CREATEBPF_LINK_UPDATEBPF_LINK_GET_FD_BY_IDBPF_LINK_GET_NEXT_IDBPF_ENABLE_STATSBPF_ITER_CREATE" var _BPFCmd_index = [...]uint16{0, 14, 33, 52, 71, 91, 104, 115, 126, 141, 156, 173, 193, 212, 233, 253, 275, 289, 312, 324, 344, 361, 391, 405, 424, 444, 475, 495, 515, 530, 545, 566, 586, 602, 617} func (i BPFCmd) String() string { if i < 0 || i >= BPFCmd(len(_BPFCmd_index)-1) { return "BPFCmd(" + strconv.FormatInt(int64(i), 10) + ")" } return _BPFCmd_name[_BPFCmd_index[i]:_BPFCmd_index[i+1]] } golang-github-cilium-ebpf-0.7.0/internal/syscall_test.go000066400000000000000000000022241414524555700233270ustar00rootroot00000000000000package internal import ( "errors" "testing" "github.com/cilium/ebpf/internal/unix" ) func TestObjName(t *testing.T) { name := NewBPFObjName("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") } } func TestSyscallError(t *testing.T) { err := errors.New("foo") foo := SyscallError(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.7.0/internal/testutils/000077500000000000000000000000001414524555700223275ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/internal/testutils/bpffs.go000066400000000000000000000006441414524555700237620ustar00rootroot00000000000000package 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.7.0/internal/testutils/cgroup.go000066400000000000000000000024141414524555700241560ustar00rootroot00000000000000package testutils import ( "errors" "os" "strings" "sync" "testing" "golang.org/x/sys/unix" ) var cgroup2 = struct { once sync.Once path string err error }{} func cgroup2Path() (string, error) { cgroup2.once.Do(func() { mounts, err := os.ReadFile("/proc/mounts") if err != nil { cgroup2.err = err return } for _, line := range strings.Split(string(mounts), "\n") { mount := strings.SplitN(line, " ", 3) if mount[0] == "cgroup2" { cgroup2.path = mount[1] return } continue } cgroup2.err = errors.New("cgroup2 not mounted") }) if cgroup2.err != nil { return "", cgroup2.err } return cgroup2.path, nil } 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.7.0/internal/testutils/feature.go000066400000000000000000000026211414524555700243120ustar00rootroot00000000000000package testutils import ( "errors" "testing" "github.com/cilium/ebpf/internal" ) func MustKernelVersion() internal.Version { v, err := internal.KernelVersion() if err != nil { panic(err) } return v } func CheckFeatureTest(t *testing.T, fn func() error) { t.Helper() err := fn() if err == nil { return } var ufe *internal.UnsupportedFeatureError if errors.As(err, &ufe) { checkKernelVersion(t, ufe) } else { t.Error("Feature test failed:", err) } } func SkipIfNotSupported(tb testing.TB, err error) { 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 } kernelVersion := MustKernelVersion() if ufe.MinimumVersion.Less(kernelVersion) { tb.Helper() tb.Fatalf("Feature '%s' isn't supported even though kernel %s is newer than %s", ufe.Name, kernelVersion, ufe.MinimumVersion) } } func SkipOnOldKernel(tb testing.TB, minVersion, feature string) { tb.Helper() minv, err := internal.NewVersion(minVersion) if err != nil { tb.Fatalf("Invalid version %s: %s", minVersion, err) } if MustKernelVersion().Less(minv) { tb.Skipf("Test requires at least kernel %s (due to missing %s)", minv, feature) } } golang-github-cilium-ebpf-0.7.0/internal/testutils/glob.go000066400000000000000000000022511414524555700236010ustar00rootroot00000000000000package 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.7.0/internal/testutils/rlimit.go000066400000000000000000000005651414524555700241640ustar00rootroot00000000000000package 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.7.0/internal/unix/000077500000000000000000000000001414524555700212525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/internal/unix/types_linux.go000066400000000000000000000132661414524555700241740ustar00rootroot00000000000000//go:build linux // +build linux package unix import ( "bytes" "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 // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP ENOTSUPP = syscall.Errno(0x20c) 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_MMAPABLE = linux.BPF_F_MMAPABLE BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP 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_READ = linux.PROT_READ PROT_WRITE = linux.PROT_WRITE MAP_SHARED = linux.MAP_SHARED 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 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 ) // Statfs_t is a wrapper type Statfs_t = linux.Statfs_t // Rlimit is a wrapper type Rlimit = linux.Rlimit // Syscall is a wrapper func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) } // FcntlInt is a wrapper func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return linux.FcntlInt(fd, cmd, arg) } // IoctlSetInt is a wrapper func IoctlSetInt(fd int, req uint, value int) error { return linux.IoctlSetInt(fd, req, value) } // Statfs is a wrapper func Statfs(path string, buf *Statfs_t) (err error) { return linux.Statfs(path, buf) } // Close is a wrapper func Close(fd int) (err error) { return linux.Close(fd) } // EpollEvent is a wrapper type EpollEvent = linux.EpollEvent // EpollWait is a wrapper func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return linux.EpollWait(epfd, events, msec) } // EpollCtl is a wrapper func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return linux.EpollCtl(epfd, op, fd, event) } // Eventfd is a wrapper func Eventfd(initval uint, flags int) (fd int, err error) { return linux.Eventfd(initval, flags) } // Write is a wrapper func Write(fd int, p []byte) (n int, err error) { return linux.Write(fd, p) } // EpollCreate1 is a wrapper func EpollCreate1(flag int) (fd int, err error) { return linux.EpollCreate1(flag) } // PerfEventMmapPage is a wrapper type PerfEventMmapPage linux.PerfEventMmapPage // SetNonblock is a wrapper func SetNonblock(fd int, nonblocking bool) (err error) { return linux.SetNonblock(fd, nonblocking) } // Mmap is a wrapper func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return linux.Mmap(fd, offset, length, prot, flags) } // Munmap is a wrapper func Munmap(b []byte) (err error) { return linux.Munmap(b) } // PerfEventAttr is a wrapper type PerfEventAttr = linux.PerfEventAttr // PerfEventOpen is a wrapper func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags) } // Utsname is a wrapper type Utsname = linux.Utsname // Uname is a wrapper func Uname(buf *Utsname) (err error) { return linux.Uname(buf) } // Getpid is a wrapper func Getpid() int { return linux.Getpid() } // Gettid is a wrapper func Gettid() int { return linux.Gettid() } // Tgkill is a wrapper func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return linux.Tgkill(tgid, tid, sig) } // BytePtrFromString is a wrapper func BytePtrFromString(s string) (*byte, error) { return linux.BytePtrFromString(s) } // ByteSliceToString is a wrapper func ByteSliceToString(s []byte) string { return linux.ByteSliceToString(s) } // Renameat2 is a wrapper func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags) } func KernelRelease() (string, error) { var uname Utsname err := Uname(&uname) if err != nil { return "", err } end := bytes.IndexByte(uname.Release[:], 0) release := string(uname.Release[:end]) return release, nil } func Prlimit(pid, resource int, new, old *Rlimit) error { return linux.Prlimit(pid, resource, new, old) } golang-github-cilium-ebpf-0.7.0/internal/unix/types_other.go000066400000000000000000000133071414524555700241520ustar00rootroot00000000000000//go:build !linux // +build !linux package unix import ( "fmt" "runtime" "syscall" ) var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) const ( ENOENT = syscall.ENOENT EEXIST = syscall.EEXIST EAGAIN = syscall.EAGAIN ENOSPC = syscall.ENOSPC EINVAL = syscall.EINVAL EINTR = syscall.EINTR EPERM = syscall.EPERM ESRCH = syscall.ESRCH ENODEV = syscall.ENODEV EBADF = syscall.Errno(0) E2BIG = syscall.Errno(0) // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP ENOTSUPP = syscall.Errno(0x20c) BPF_F_NO_PREALLOC = 0 BPF_F_NUMA_NODE = 0 BPF_F_RDONLY = 0 BPF_F_WRONLY = 0 BPF_F_RDONLY_PROG = 0 BPF_F_WRONLY_PROG = 0 BPF_F_SLEEPABLE = 0 BPF_F_MMAPABLE = 0 BPF_F_INNER_MAP = 0 BPF_OBJ_NAME_LEN = 0x10 BPF_TAG_SIZE = 0x8 BPF_RINGBUF_BUSY_BIT = 0 BPF_RINGBUF_DISCARD_BIT = 0 BPF_RINGBUF_HDR_SZ = 0 SYS_BPF = 321 F_DUPFD_CLOEXEC = 0x406 EPOLLIN = 0x1 EPOLL_CTL_ADD = 0x1 EPOLL_CLOEXEC = 0x80000 O_CLOEXEC = 0x80000 O_NONBLOCK = 0x800 PROT_READ = 0x1 PROT_WRITE = 0x2 MAP_SHARED = 0x1 PERF_ATTR_SIZE_VER1 = 0 PERF_TYPE_SOFTWARE = 0x1 PERF_TYPE_TRACEPOINT = 0 PERF_COUNT_SW_BPF_OUTPUT = 0xa PERF_EVENT_IOC_DISABLE = 0 PERF_EVENT_IOC_ENABLE = 0 PERF_EVENT_IOC_SET_BPF = 0 PerfBitWatermark = 0x4000 PERF_SAMPLE_RAW = 0x400 PERF_FLAG_FD_CLOEXEC = 0x8 RLIM_INFINITY = 0x7fffffffffffffff RLIMIT_MEMLOCK = 8 BPF_STATS_RUN_TIME = 0 PERF_RECORD_LOST = 2 PERF_RECORD_SAMPLE = 9 AT_FDCWD = -0x2 RENAME_NOREPLACE = 0x1 ) // Statfs_t is a wrapper 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 } // Rlimit is a wrapper type Rlimit struct { Cur uint64 Max uint64 } // Syscall is a wrapper func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, syscall.Errno(1) } // FcntlInt is a wrapper func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return -1, errNonLinux } // IoctlSetInt is a wrapper func IoctlSetInt(fd int, req uint, value int) error { return errNonLinux } // Statfs is a wrapper func Statfs(path string, buf *Statfs_t) error { return errNonLinux } // Close is a wrapper func Close(fd int) (err error) { return errNonLinux } // EpollEvent is a wrapper type EpollEvent struct { Events uint32 Fd int32 Pad int32 } // EpollWait is a wrapper func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return 0, errNonLinux } // EpollCtl is a wrapper func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return errNonLinux } // Eventfd is a wrapper func Eventfd(initval uint, flags int) (fd int, err error) { return 0, errNonLinux } // Write is a wrapper func Write(fd int, p []byte) (n int, err error) { return 0, errNonLinux } // EpollCreate1 is a wrapper func EpollCreate1(flag int) (fd int, err error) { return 0, errNonLinux } // PerfEventMmapPage is a wrapper 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 } // SetNonblock is a wrapper func SetNonblock(fd int, nonblocking bool) (err error) { return errNonLinux } // Mmap is a wrapper func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return []byte{}, errNonLinux } // Munmap is a wrapper func Munmap(b []byte) (err error) { return errNonLinux } // PerfEventAttr is a wrapper 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 } // PerfEventOpen is a wrapper func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return 0, errNonLinux } // Utsname is a wrapper type Utsname struct { Release [65]byte Version [65]byte } // Uname is a wrapper func Uname(buf *Utsname) (err error) { return errNonLinux } // Getpid is a wrapper func Getpid() int { return -1 } // Gettid is a wrapper func Gettid() int { return -1 } // Tgkill is a wrapper func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return errNonLinux } // BytePtrFromString is a wrapper func BytePtrFromString(s string) (*byte, error) { return nil, errNonLinux } // ByteSliceToString is a wrapper func ByteSliceToString(s []byte) string { return "" } // Renameat2 is a wrapper func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return errNonLinux } func KernelRelease() (string, error) { return "", errNonLinux } func Prlimit(pid, resource int, new, old *Rlimit) error { return errNonLinux } golang-github-cilium-ebpf-0.7.0/internal/version.go000066400000000000000000000116311414524555700223050ustar00rootroot00000000000000package internal import ( "fmt" "os" "regexp" "sync" "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 ) var ( // Match between one and three decimals separated by dots, with the last // segment (patch level) being optional on some kernels. // The x.y.z string must appear at the start of a string or right after // whitespace to prevent sequences like 'x.y.z-a.b.c' from matching 'a.b.c'. rgxKernelVersion = regexp.MustCompile(`(?:\A|\s)\d{1,3}\.\d{1,3}(?:\.\d{1,3})?`) kernelVersion = struct { once sync.Once version Version err error }{} ) // 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 } 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. func KernelVersion() (Version, error) { kernelVersion.once.Do(func() { kernelVersion.version, kernelVersion.err = detectKernelVersion() }) if kernelVersion.err != nil { return Version{}, kernelVersion.err } return kernelVersion.version, nil } // detectKernelVersion returns the version of the running kernel. It scans the // following sources in order: /proc/version_signature, uname -v, uname -r. // In each of those locations, the last-appearing x.y(.z) value is selected // for parsing. The first location that yields a usable version number is // returned. func detectKernelVersion() (Version, error) { // Try reading /proc/version_signature for Ubuntu compatibility. // Example format: Ubuntu 4.15.0-91.92-generic 4.15.18 // This method exists in the kernel itself, see d18acd15c // ("perf tools: Fix kernel version error in ubuntu"). if pvs, err := os.ReadFile("/proc/version_signature"); err == nil { // If /proc/version_signature exists, failing to parse it is an error. // It only exists on Ubuntu, where the real patch level is not obtainable // through any other method. v, err := findKernelVersion(string(pvs)) if err != nil { return Version{}, err } return v, nil } var uname unix.Utsname if err := unix.Uname(&uname); err != nil { return Version{}, fmt.Errorf("calling uname: %w", err) } // Debian puts the version including the patch level in uname.Version. // It is not an error if there's no version number in uname.Version, // as most distributions don't use it. Parsing can continue on uname.Release. // Example format: #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) if v, err := findKernelVersion(unix.ByteSliceToString(uname.Version[:])); err == nil { return v, nil } // Most other distributions have the full kernel version including patch // level in uname.Release. // Example format: 4.19.0-5-amd64, 5.5.10-arch1-1 v, err := findKernelVersion(unix.ByteSliceToString(uname.Release[:])) if err != nil { return Version{}, err } return v, nil } // findKernelVersion matches s against rgxKernelVersion and parses the result // into a Version. If s contains multiple matches, the last entry is selected. func findKernelVersion(s string) (Version, error) { m := rgxKernelVersion.FindAllString(s, -1) if m == nil { return Version{}, fmt.Errorf("no kernel version in string: %s", s) } // Pick the last match of the string in case there are multiple. s = m[len(m)-1] v, err := NewVersion(s) if err != nil { return Version{}, fmt.Errorf("parsing version string %s: %w", s, err) } return v, nil } golang-github-cilium-ebpf-0.7.0/internal/version_test.go000066400000000000000000000057201414524555700233460ustar00rootroot00000000000000package internal import ( "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 TestVersionDetection(t *testing.T) { var tests = []struct { name string s string v Version err bool }{ {"ubuntu version_signature", "Ubuntu 4.15.0-91.92-generic 4.15.18", Version{4, 15, 18}, false}, {"debian uname version", "#1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08)", Version{4, 19, 37}, false}, {"debian uname release (missing patch)", "4.19.0-5-amd64", Version{4, 19, 0}, false}, {"debian uname all", "Linux foo 5.6.0-0.bpo.2-amd64 #1 SMP Debian 5.6.14-2~bpo10+1 (2020-06-09) x86_64 GNU/Linux", Version{5, 6, 14}, false}, {"debian custom uname version", "#1577309 SMP Thu Dec 31 08:32:02 UTC 2020", Version{}, true}, {"debian custom uname release (missing patch)", "4.19-ovh-xxxx-std-ipv6-64", Version{4, 19, 0}, false}, {"arch uname version", "#1 SMP PREEMPT Thu, 11 Mar 2021 21:27:06 +0000", Version{}, true}, {"arch uname release", "5.5.10-arch1-1", Version{5, 5, 10}, false}, {"alpine uname version", "#1-Alpine SMP Thu Jan 23 10:58:18 UTC 2020", Version{}, true}, {"alpine uname release", "4.14.167-0-virt", Version{4, 14, 167}, false}, {"fedora uname version", "#1 SMP Tue May 14 18:22:28 UTC 2019", Version{}, true}, {"fedora uname release", "5.0.16-100.fc28.x86_64", Version{5, 0, 16}, false}, {"centos8 uname version", "#1 SMP Mon Mar 1 17:16:16 UTC 2021", Version{}, true}, {"centos8 uname release", "4.18.0-240.15.1.el8_3.x86_64", Version{4, 18, 0}, false}, {"devuan uname version", "#1 SMP Debian 4.19.181-1 (2021-03-19)", Version{4, 19, 181}, false}, {"devuan uname release", "4.19.0-16-amd64", Version{4, 19, 0}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v, err := findKernelVersion(tt.s) if err != nil { if !tt.err { t.Error("unexpected error:", err) } return } if tt.err { t.Error("expected error, but got none") } if v != tt.v { t.Errorf("unexpected version for string '%s'. got: %v, want: %v", tt.s, v, tt.v) } }) } } golang-github-cilium-ebpf-0.7.0/link/000077500000000000000000000000001414524555700174105ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/link/cgroup.go000066400000000000000000000073351414524555700212460ustar00rootroot00000000000000package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" ) type cgroupAttachFlags uint32 // cgroup attach flags const ( flagAllowOverride cgroupAttachFlags = 1 << iota flagAllowMulti 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. func AttachCgroup(opts CgroupOptions) (Link, error) { cgroup, err := os.Open(opts.Path) if err != nil { return nil, fmt.Errorf("can't open cgroup: %s", err) } clone, err := opts.Program.Clone() if err != nil { cgroup.Close() return nil, err } var cg Link cg, err = newLinkCgroup(cgroup, opts.Attach, clone) if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti) } if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride) } if err != nil { cgroup.Close() clone.Close() return nil, err } return cg, nil } // LoadPinnedCgroup loads a pinned cgroup from a bpffs. func LoadPinnedCgroup(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { link, err := LoadPinnedRawLink(fileName, CgroupType, opts) if err != nil { return nil, err } return &linkCgroup{*link}, nil } type progAttachCgroup struct { cgroup *os.File current *ebpf.Program attachType ebpf.AttachType flags cgroupAttachFlags } var _ Link = (*progAttachCgroup)(nil) func (cg *progAttachCgroup) isLink() {} 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) } } err := RawAttachProgram(RawAttachProgramOptions{ Target: int(cgroup.Fd()), Program: prog, Flags: uint32(flags), Attach: attach, }) if err != nil { return nil, fmt.Errorf("cgroup: %w", err) } return &progAttachCgroup{cgroup, prog, 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 pin cgroup: %w", ErrNotSupported) } type linkCgroup struct { RawLink } var _ Link = (*linkCgroup)(nil) 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.7.0/link/cgroup_test.go000066400000000000000000000033021414524555700222730ustar00rootroot00000000000000package 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) } 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, testLinkOptions{ prog: 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 := mustCgroupEgressProgram(t) testLink(t, link, testLinkOptions{ prog: 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, testLinkOptions{ prog: prog, loadPinned: LoadPinnedCgroup, }) } golang-github-cilium-ebpf-0.7.0/link/doc.go000066400000000000000000000001251414524555700205020ustar00rootroot00000000000000// Package link allows attaching eBPF programs to various kernel hooks. package link golang-github-cilium-ebpf-0.7.0/link/freplace.go000066400000000000000000000043361414524555700215260ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/btf" ) type FreplaceLink struct { RawLink } // 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) (*FreplaceLink, 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 { info, err := targetProg.Info() if err != nil { return nil, err } btfID, ok := info.BTFID() if !ok { return nil, fmt.Errorf("could not get BTF ID for program %s: %w", info.Name, errInvalidInput) } btfHandle, err := btf.NewHandleFromID(btfID) if err != nil { return nil, err } defer btfHandle.Close() var function *btf.Func if err := btfHandle.Spec().FindType(name, &function); err != nil { return nil, err } target = targetProg.FD() typeID = function.ID() } link, err := AttachRawLink(RawLinkOptions{ Target: target, Program: prog, Attach: ebpf.AttachNone, BTF: typeID, }) if err != nil { return nil, err } return &FreplaceLink{*link}, nil } // Update implements the Link interface. func (f *FreplaceLink) Update(new *ebpf.Program) error { return fmt.Errorf("freplace update: %w", ErrNotSupported) } // LoadPinnedFreplace loads a pinned iterator from a bpffs. func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (*FreplaceLink, error) { link, err := LoadPinnedRawLink(fileName, TracingType, opts) if err != nil { return nil, err } return &FreplaceLink{*link}, err } golang-github-cilium-ebpf-0.7.0/link/freplace_test.go000066400000000000000000000024521414524555700225620ustar00rootroot00000000000000package 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) if err != nil { t.Fatal("Can't create freplace:", err) } testLink(t, freplace, testLinkOptions{ prog: replacement, loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) { return LoadPinnedFreplace(s, opts) }, }) }) } golang-github-cilium-ebpf-0.7.0/link/iter.go000066400000000000000000000043101414524555700207000ustar00rootroot00000000000000package link import ( "fmt" "io" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" ) 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", internal.ErrClosedFd) } var info bpfIterLinkInfoMap if opts.Map != nil { mapFd := opts.Map.FD() if mapFd < 0 { return nil, fmt.Errorf("invalid map: %w", internal.ErrClosedFd) } info.map_fd = uint32(mapFd) } attr := bpfLinkCreateIterAttr{ prog_fd: uint32(progFd), attach_type: ebpf.AttachTraceIter, iter_info: internal.NewPointer(unsafe.Pointer(&info)), iter_info_len: uint32(unsafe.Sizeof(info)), } fd, err := bpfLinkCreateIter(&attr) if err != nil { return nil, fmt.Errorf("can't link iterator: %w", err) } return &Iter{RawLink{fd, ""}}, err } // LoadPinnedIter loads a pinned iterator from a bpffs. func LoadPinnedIter(fileName string, opts *ebpf.LoadPinOptions) (*Iter, error) { link, err := LoadPinnedRawLink(fileName, IterType, opts) if err != nil { return nil, err } return &Iter{*link}, 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) { linkFd, err := it.fd.Value() if err != nil { return nil, err } attr := &bpfIterCreateAttr{ linkFd: linkFd, } fd, err := bpfIterCreate(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.7.0/link/iter_test.go000066400000000000000000000040701414524555700217420ustar00rootroot00000000000000package link import ( "io" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestIter(t *testing.T) { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.Tracing, AttachType: ebpf.AttachTraceIter, AttachTo: "bpf_map", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load program:", err) } defer prog.Close() 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, testLinkOptions{ prog: prog, loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) { return LoadPinnedIter(s, opts) }, }) } func TestIterMapElements(t *testing.T) { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.Tracing, AttachType: ebpf.AttachTraceIter, AttachTo: "bpf_map_elem", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load program:", err) } defer prog.Close() 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)) } } golang-github-cilium-ebpf-0.7.0/link/kprobe.go000066400000000000000000000332731414524555700212310ustar00rootroot00000000000000package link import ( "bytes" "crypto/rand" "errors" "fmt" "os" "path/filepath" "runtime" "sync" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events") kprobeRetprobeBit = struct { once sync.Once value uint64 err error }{} ) type probeType uint8 const ( kprobeType probeType = iota uprobeType ) func (pt probeType) String() string { if pt == kprobeType { return "kprobe" } return "uprobe" } func (pt probeType) EventsPath() string { if pt == kprobeType { return kprobeEventsPath } return uprobeEventsPath } func (pt probeType) PerfEventType(ret bool) perfEventType { if pt == kprobeType { if ret { return kretprobeEvent } return kprobeEvent } if ret { return uretprobeEvent } return uprobeEvent } func (pt probeType) RetprobeBit() (uint64, error) { if pt == kprobeType { return kretprobeBit() } return uretprobeBit() } // 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) // // 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. func Kprobe(symbol string, prog *ebpf.Program) (Link, error) { k, err := kprobe(symbol, prog, false) if err != nil { return nil, err } err = k.attach(prog) if err != nil { k.Close() return nil, err } return k, 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) // // 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. func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) { k, err := kprobe(symbol, prog, true) if err != nil { return nil, err } err = k.attach(prog) if err != nil { k.Close() return nil, err } return k, nil } // 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, 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 !rgxTraceEvent.MatchString(symbol) { return nil, fmt.Errorf("symbol '%s' must be alphanumeric or underscore: %w", symbol, errInvalidInput) } if prog.Type() != ebpf.Kprobe { return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput) } // Use kprobe PMU if the kernel has it available. tp, err := pmuKprobe(platformPrefix(symbol), ret) if errors.Is(err, os.ErrNotExist) { tp, err = pmuKprobe(symbol, ret) } if err == nil { return tp, nil } if err != nil && !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_kprobe PMU: %w", err) } // Use tracefs if kprobe PMU is missing. tp, err = tracefsKprobe(platformPrefix(symbol), ret) if errors.Is(err, os.ErrNotExist) { tp, err = tracefsKprobe(symbol, ret) } if err != nil { return nil, fmt.Errorf("creating trace event '%s' in tracefs: %w", symbol, err) } return tp, nil } // pmuKprobe opens a perf event based on the kprobe PMU. // Returns os.ErrNotExist if the given symbol does not exist in the kernel. func pmuKprobe(symbol string, ret bool) (*perfEvent, error) { return pmuProbe(kprobeType, symbol, "", 0, perfAllThreads, ret) } // 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(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { // Getting the PMU type will fail if the kernel doesn't support // the perf_[k,u]probe PMU. et, err := getPMUEventType(typ) if err != nil { return nil, err } var config uint64 if ret { bit, err := typ.RetprobeBit() if err != nil { return nil, err } config |= 1 << bit } var ( attr unix.PerfEventAttr sp unsafe.Pointer ) switch typ { case kprobeType: // Create a pointer to a NUL-terminated string for the kernel. sp, err = unsafeStringPtr(symbol) if err != nil { return nil, err } attr = unix.PerfEventAttr{ Type: uint32(et), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Kernel symbol to trace Config: config, // Retprobe flag } case uprobeType: sp, err = unsafeStringPtr(path) if err != nil { return nil, err } 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(et), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Uprobe path Ext2: offset, // Uprobe offset Config: config, // Retprobe flag } } fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a kretprobe for a missing symbol. Make sure ENOENT // is returned to the caller. if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, os.ErrNotExist) } // Since at least commit cb9a19fe4aa51, ENOTSUPP is returned // when attempting to set a uprobe on a trap instruction. if errors.Is(err, unix.ENOTSUPP) { return nil, fmt.Errorf("failed setting uprobe on offset %#x (possible trap insn): %w", offset, err) } if err != nil { return nil, fmt.Errorf("opening perf event: %w", err) } // Ensure the string pointer is not collected before PerfEventOpen returns. runtime.KeepAlive(sp) // Kernel has perf_[k,u]probe PMU available, initialize perf event. return &perfEvent{ fd: internal.NewFD(uint32(fd)), pmuID: et, name: symbol, typ: typ.PerfEventType(ret), }, nil } // tracefsKprobe creates a Kprobe tracefs entry. func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) { return tracefsProbe(kprobeType, symbol, "", 0, perfAllThreads, ret) } // 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(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { // 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 := randomGroup("ebpf") if err != nil { return nil, fmt.Errorf("randomizing group name: %w", err) } // 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. _, err = getTraceEventID(group, symbol) if err == nil { return nil, fmt.Errorf("trace event already exists: %s/%s", group, symbol) } if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("checking trace event %s/%s: %w", group, symbol, err) } // Create the [k,u]probe trace event using tracefs. if err := createTraceFSProbeEvent(typ, group, symbol, path, offset, ret); err != nil { return nil, fmt.Errorf("creating probe entry on tracefs: %w", err) } // Get the newly-created trace event's id. tid, err := getTraceEventID(group, symbol) if err != nil { return nil, fmt.Errorf("getting trace event id: %w", err) } // Kprobes are ephemeral tracepoints and share the same perf event type. fd, err := openTracepointPerfEvent(tid, pid) if err != nil { return nil, err } return &perfEvent{ fd: fd, group: group, name: symbol, tracefsID: tid, typ: typ.PerfEventType(ret), }, nil } // createTraceFSProbeEvent creates a new ephemeral trace event by writing to // /[k,u]probe_events. 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. func createTraceFSProbeEvent(typ probeType, group, symbol, path string, offset uint64, ret bool) error { // Open the kprobe_events file in tracefs. f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666) if err != nil { return fmt.Errorf("error opening '%s': %w", typ.EventsPath(), err) } defer f.Close() var pe string switch typ { case kprobeType: // 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. pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, symbol) case uprobeType: // 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 // // See Documentation/trace/uprobetracer.txt for more details. pathOffset := uprobePathOffset(path, offset) pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, pathOffset) } _, err = f.WriteString(pe) // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a kretprobe for a missing symbol. Make sure ENOENT // is returned to the caller. if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { return fmt.Errorf("symbol %s not found: %w", symbol, os.ErrNotExist) } if err != nil { return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err) } return nil } // closeTraceFSProbeEvent removes the [k,u]probe with the given type, group and symbol // from /[k,u]probe_events. func closeTraceFSProbeEvent(typ probeType, group, symbol string) error { f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666) if err != nil { return fmt.Errorf("error opening %s: %w", typ.EventsPath(), err) } defer f.Close() // See [k,u]probe_events syntax above. The probe type does not need to be specified // for removals. pe := fmt.Sprintf("-:%s/%s", group, symbol) if _, err = f.WriteString(pe); err != nil { return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err) } return nil } // 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 rgxTraceEvent. func randomGroup(prefix string) (string, error) { if !rgxTraceEvent.MatchString(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 } func probePrefix(ret bool) string { if ret { return "r" } return "p" } // determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit // from /sys/bus/event_source/devices//format/retprobe. func determineRetprobeBit(typ probeType) (uint64, error) { p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe") data, err := os.ReadFile(p) if err != nil { return 0, err } var rp uint64 n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp) if err != nil { return 0, fmt.Errorf("parse retprobe bit: %w", err) } if n != 1 { return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n) } return rp, nil } func kretprobeBit() (uint64, error) { kprobeRetprobeBit.once.Do(func() { kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit(kprobeType) }) return kprobeRetprobeBit.value, kprobeRetprobeBit.err } golang-github-cilium-ebpf-0.7.0/link/kprobe_test.go000066400000000000000000000237071414524555700222710ustar00rootroot00000000000000package 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/unix" ) var ( kprobeSpec = ebpf.ProgramSpec{ Type: ebpf.Kprobe, License: "MIT", Instructions: asm.Instructions{ // set exit code to 0 asm.Mov.Imm(asm.R0, 0), asm.Return(), }, } ) func TestKprobe(t *testing.T) { c := qt.New(t) prog, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() k, err := Kprobe("printk", prog) c.Assert(err, qt.IsNil) defer k.Close() testLink(t, k, testLinkOptions{ prog: prog, }) k, err = Kprobe("bogus", prog) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err)) if k != nil { k.Close() } } func TestKretprobe(t *testing.T) { c := qt.New(t) prog, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() k, err := Kretprobe("printk", prog) c.Assert(err, qt.IsNil) defer k.Close() testLink(t, k, testLinkOptions{ prog: prog, }) k, err = Kretprobe("bogus", prog) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err)) if k != nil { k.Close() } } 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) // empty symbol c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe("_", nil) // empty prog c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe(".", &ebpf.Program{}) // illegal chars in symbol c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Kprobe("foo", &ebpf.Program{}) // 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 := pmuKprobe("printk", false) c.Assert(err, qt.IsNil) defer pk.Close() c.Assert(pk.typ, qt.Equals, kprobeEvent) // kretprobe happy path. pr, err := pmuKprobe("printk", true) c.Assert(err, qt.IsNil) defer pr.Close() c.Assert(pr.typ, qt.Equals, kretprobeEvent) // Expect os.ErrNotExist when specifying a non-existent kernel symbol // on kernels 4.17 and up. _, err = pmuKprobe("bogus", false) 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 = pmuKprobe("bogus", 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 := pmuKprobe("printk", false) 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 := pmuKprobe("printk", false) 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) symbol := "printk" // Open and close tracefs k(ret)probes, checking all errors. kp, err := tracefsKprobe(symbol, false) c.Assert(err, qt.IsNil) c.Assert(kp.Close(), qt.IsNil) c.Assert(kp.typ, qt.Equals, kprobeEvent) kp, err = tracefsKprobe(symbol, true) c.Assert(err, qt.IsNil) c.Assert(kp.Close(), qt.IsNil) c.Assert(kp.typ, qt.Equals, kretprobeEvent) // Create two identical trace events, ensure their IDs differ. k1, err := tracefsKprobe(symbol, false) c.Assert(err, qt.IsNil) defer k1.Close() c.Assert(k1.tracefsID, qt.Not(qt.Equals), 0) k2, err := tracefsKprobe(symbol, false) c.Assert(err, qt.IsNil) defer k2.Close() c.Assert(k2.tracefsID, qt.Not(qt.Equals), 0) // Compare the kprobes' tracefs IDs. c.Assert(k1.tracefsID, qt.Not(qt.CmpEquals()), k2.tracefsID) // Write a k(ret)probe event for a non-existing symbol. err = createTraceFSProbeEvent(kprobeType, "testgroup", "bogus", "", 0, false) 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 = createTraceFSProbeEvent(kprobeType, "testgroup", "bogus", "", 0, true) c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", 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 := tracefsKprobe("printk", false) 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) } } } // Test k(ret)probe creation writing directly to /kprobe_events. // Only runs on 5.0 and over. Earlier versions ignored writes of duplicate // events, while 5.0 started returning -EEXIST when a kprobe event already // exists. func TestKprobeCreateTraceFS(t *testing.T) { testutils.SkipOnOldKernel(t, "5.0", "/kprobe_events doesn't reject duplicate events") c := qt.New(t) symbol := "printk" pg, _ := randomGroup("ebpftest") rg, _ := randomGroup("ebpftest") // Tee up cleanups in case any of the Asserts abort the function. defer func() { _ = closeTraceFSProbeEvent(kprobeType, pg, symbol) _ = closeTraceFSProbeEvent(kprobeType, rg, symbol) }() // Create a kprobe. err := createTraceFSProbeEvent(kprobeType, pg, symbol, "", 0, false) c.Assert(err, qt.IsNil) // Attempt to create an identical kprobe using tracefs, // expect it to fail with os.ErrExist. err = createTraceFSProbeEvent(kprobeType, pg, symbol, "", 0, false) c.Assert(errors.Is(err, os.ErrExist), qt.IsTrue, qt.Commentf("expected consecutive kprobe creation to contain os.ErrExist, got: %v", err)) // Expect a successful close of the kprobe. c.Assert(closeTraceFSProbeEvent(kprobeType, pg, symbol), qt.IsNil) // Same test for a kretprobe. err = createTraceFSProbeEvent(kprobeType, rg, symbol, "", 0, true) c.Assert(err, qt.IsNil) err = createTraceFSProbeEvent(kprobeType, rg, symbol, "", 0, true) c.Assert(os.IsExist(err), qt.IsFalse, qt.Commentf("expected consecutive kretprobe creation to contain os.ErrExist, got: %v", err)) // Expect a successful close of the kretprobe. c.Assert(closeTraceFSProbeEvent(kprobeType, rg, symbol), qt.IsNil) } 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 TestDetermineRetprobeBit(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU") c := qt.New(t) rpk, err := kretprobeBit() c.Assert(err, qt.IsNil) c.Assert(rpk, qt.Equals, uint64(0)) rpu, err := uretprobeBit() c.Assert(err, qt.IsNil) c.Assert(rpu, qt.Equals, uint64(0)) } func TestKprobeProgramCall(t *testing.T) { m, p := newUpdaterMapProg(t, ebpf.Kprobe) // Open Kprobe on `sys_getpid` and attach it // to the ebpf program created above. k, err := Kprobe("sys_getpid", p) 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) (*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(), }, 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) } } golang-github-cilium-ebpf-0.7.0/link/link.go000066400000000000000000000123401414524555700206740ustar00rootroot00000000000000package link import ( "fmt" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" ) 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 // Prevent external users from implementing this interface. isLink() } // ID uniquely identifies a BPF link. type ID uint32 // 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 } // RawLinkInfo contains metadata on a link. type RawLinkInfo struct { Type Type ID ID Program ebpf.ProgramID } // 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 *internal.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", internal.ErrClosedFd) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd) } attr := bpfLinkCreateAttr{ targetFd: uint32(opts.Target), progFd: uint32(progFd), attachType: opts.Attach, targetBTFID: uint32(opts.BTF), } fd, err := bpfLinkCreate(&attr) if err != nil { return nil, fmt.Errorf("can't create link: %s", err) } return &RawLink{fd, ""}, nil } // LoadPinnedRawLink loads a persisted link from a bpffs. // // Returns an error if the pinned link type doesn't match linkType. Pass // UnspecifiedType to disable this behaviour. func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions) (*RawLink, error) { fd, err := internal.BPFObjGet(fileName, opts.Marshal()) if err != nil { return nil, fmt.Errorf("load pinned link: %w", err) } link := &RawLink{fd, fileName} if linkType == UnspecifiedType { return link, nil } info, err := link.Info() if err != nil { link.Close() return nil, fmt.Errorf("get pinned link info: %s", err) } if info.Type != linkType { link.Close() return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, linkType) } return link, nil } func (l *RawLink) isLink() {} // FD returns the raw file descriptor. func (l *RawLink) FD() int { fd, err := l.fd.Value() if err != nil { return -1 } return int(fd) } // 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 } // 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", internal.ErrClosedFd) } var oldFd int if opts.Old != nil { oldFd = opts.Old.FD() if oldFd < 0 { return fmt.Errorf("invalid replacement program: %s", internal.ErrClosedFd) } } linkFd, err := l.fd.Value() if err != nil { return fmt.Errorf("can't update link: %s", err) } attr := bpfLinkUpdateAttr{ linkFd: linkFd, newProgFd: uint32(newFd), oldProgFd: uint32(oldFd), flags: opts.Flags, } return bpfLinkUpdate(&attr) } // struct bpf_link_info type bpfLinkInfo struct { typ uint32 id uint32 prog_id uint32 } // Info returns metadata about the link. func (l *RawLink) Info() (*RawLinkInfo, error) { var info bpfLinkInfo err := internal.BPFObjGetInfoByFD(l.fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) if err != nil { return nil, fmt.Errorf("link info: %s", err) } return &RawLinkInfo{ Type(info.typ), ID(info.id), ebpf.ProgramID(info.prog_id), }, nil } golang-github-cilium-ebpf-0.7.0/link/link_test.go000066400000000000000000000100551414524555700217340ustar00rootroot00000000000000package link import ( "errors" "math" "os" "path/filepath" "reflect" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) 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, link, testLinkOptions{ prog: prog, loadPinned: func(f string, opts *ebpf.LoadPinOptions) (Link, error) { return LoadPinnedRawLink(f, UnspecifiedType, opts) }, }) } func TestRawLinkLoadPinnedWithOptions(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) } path := filepath.Join(testutils.TempBPFFS(t), "link") err = link.Pin(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } // It seems like the kernel ignores BPF_F_RDONLY when updating a link, // so we can't test this. _, err = LoadPinnedRawLink(path, UnspecifiedType, &ebpf.LoadPinOptions{ Flags: math.MaxUint32, }) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags don't trigger an error:", err) } } func mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program) { t.Helper() testutils.SkipIfNotSupported(t, haveProgAttach()) return testutils.CreateCgroup(t), mustCgroupEgressProgram(t) } func mustCgroupEgressProgram(t *testing.T) *ebpf.Program { t.Helper() prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, AttachType: ebpf.AttachCGroupInetEgress, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { t.Fatal(err) } t.Cleanup(func() { prog.Close() }) return prog } type testLinkOptions struct { prog *ebpf.Program loadPinned func(string, *ebpf.LoadPinOptions) (Link, error) } func testLink(t *testing.T, link Link, opts testLinkOptions) { t.Helper() tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) path := filepath.Join(tmp, "link") err = link.Pin(path) if err == ErrNotSupported { t.Errorf("%T.Pin returns unwrapped ErrNotSupported", link) } if opts.loadPinned == nil { if !errors.Is(err, ErrNotSupported) { t.Errorf("%T.Pin doesn't return ErrNotSupported: %s", link, err) } } else { if err != nil { t.Fatalf("Can't pin %T: %s", link, err) } link2, err := opts.loadPinned(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 = opts.loadPinned(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("update", func(t *testing.T) { err := link.Update(opts.prog) if err == ErrNotSupported { t.Fatal("Update returns unwrapped ErrNotSupported", link) } if errors.Is(err, ErrNotSupported) { return } 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) } }() }) if err := link.Close(); err != nil { t.Fatalf("%T.Close returns an error: %s", link, err) } } golang-github-cilium-ebpf-0.7.0/link/netns.go000066400000000000000000000024101414524555700210630ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" ) // NetNsInfo contains metadata about a network namespace link. type NetNsInfo struct { RawLinkInfo } // 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 } // LoadPinnedNetNs loads a network namespace link from bpffs. func LoadPinnedNetNs(fileName string, opts *ebpf.LoadPinOptions) (*NetNsLink, error) { link, err := LoadPinnedRawLink(fileName, NetNsType, opts) if err != nil { return nil, err } return &NetNsLink{link}, nil } // Info returns information about the link. func (nns *NetNsLink) Info() (*NetNsInfo, error) { info, err := nns.RawLink.Info() if err != nil { return nil, err } return &NetNsInfo{*info}, nil } golang-github-cilium-ebpf-0.7.0/link/netns_test.go000066400000000000000000000032641414524555700221320ustar00rootroot00000000000000package 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 := mustCreateSkLookupProgram(t) 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) } _, err = link.Info() if err != nil { t.Fatal("Info returns an error:", err) } testLink(t, link, testLinkOptions{ prog: prog, loadPinned: func(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { return LoadPinnedNetNs(fileName, opts) }, }) } func mustCreateSkLookupProgram(tb testing.TB) *ebpf.Program { tb.Helper() prog, err := createSkLookupProgram() if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return 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.7.0/link/perf_event.go000066400000000000000000000207701414524555700221020ustar00rootroot00000000000000package link import ( "bytes" "errors" "fmt" "os" "path/filepath" "regexp" "runtime" "strconv" "strings" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "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 ( tracefsPath = "/sys/kernel/debug/tracing" // 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. rgxTraceEvent = regexp.MustCompile("^[a-zA-Z_][0-9a-zA-Z_]*$") errInvalidInput = errors.New("invalid input") ) const ( perfAllThreads = -1 ) type perfEventType uint8 const ( tracepointEvent perfEventType = iota kprobeEvent kretprobeEvent uprobeEvent uretprobeEvent ) // 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 { // Group and name of the tracepoint/kprobe/uprobe. group string name string // PMU event ID read from sysfs. Valid IDs are non-zero. pmuID uint64 // ID of the trace event read from tracefs. Valid IDs are non-zero. tracefsID uint64 // The event type determines the types of programs that can be attached. typ perfEventType fd *internal.FD } func (pe *perfEvent) isLink() {} func (pe *perfEvent) Pin(string) error { return fmt.Errorf("pin perf event: %w", ErrNotSupported) } func (pe *perfEvent) Unpin() error { return fmt.Errorf("unpin perf event: %w", ErrNotSupported) } // 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 (pe *perfEvent) Update(prog *ebpf.Program) error { return fmt.Errorf("can't replace eBPF program in perf event: %w", ErrNotSupported) } func (pe *perfEvent) Close() error { if pe.fd == nil { return nil } pfd, err := pe.fd.Value() if err != nil { return fmt.Errorf("getting perf event fd: %w", err) } err = unix.IoctlSetInt(int(pfd), unix.PERF_EVENT_IOC_DISABLE, 0) if err != nil { return fmt.Errorf("disabling perf event: %w", err) } err = pe.fd.Close() if err != nil { return fmt.Errorf("closing perf event fd: %w", err) } switch pe.typ { case kprobeEvent, kretprobeEvent: // Clean up kprobe tracefs entry. if pe.tracefsID != 0 { return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name) } case uprobeEvent, uretprobeEvent: // Clean up uprobe tracefs entry. if pe.tracefsID != 0 { return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name) } case tracepointEvent: // Tracepoint trace events don't hold any extra resources. return nil } return nil } // 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 (pe *perfEvent) attach(prog *ebpf.Program) error { if prog == nil { return errors.New("cannot attach a nil program") } if pe.fd == nil { return errors.New("cannot attach to nil perf event") } if prog.FD() < 0 { return fmt.Errorf("invalid program: %w", internal.ErrClosedFd) } switch pe.typ { case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent: if t := prog.Type(); t != ebpf.Kprobe { return fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t) } case tracepointEvent: if t := prog.Type(); t != ebpf.TracePoint { return fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t) } default: return fmt.Errorf("unknown perf event type: %d", pe.typ) } // The ioctl below will fail when the fd is invalid. kfd, _ := pe.fd.Value() // Assign the eBPF program to the perf event. err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_SET_BPF, prog.FD()) if err != nil { return fmt.Errorf("setting perf event bpf program: %w", err) } // PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values. if err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil { return fmt.Errorf("enable perf event: %s", err) } // Close the perf event when its reference is lost to avoid leaking system resources. runtime.SetFinalizer(pe, (*perfEvent).Close) return 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 } // getTraceEventID reads a trace event's ID from tracefs given its group and name. // group and name must be alphanumeric or underscore, as required by the kernel. func getTraceEventID(group, name string) (uint64, error) { tid, err := uint64FromFile(tracefsPath, "events", group, name, "id") if errors.Is(err, os.ErrNotExist) { return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist) } if err != nil { return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err) } return tid, nil } // getPMUEventType reads a Performance Monitoring Unit's type (numeric identifier) // from /sys/bus/event_source/devices//type. // // Returns ErrNotSupported if the pmu type is not supported. func getPMUEventType(typ probeType) (uint64, error) { et, err := uint64FromFile("/sys/bus/event_source/devices", typ.String(), "type") if errors.Is(err, os.ErrNotExist) { return 0, fmt.Errorf("pmu type %s: %w", typ, ErrNotSupported) } if err != nil { return 0, fmt.Errorf("reading pmu type %s: %w", typ, err) } return et, 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) (*internal.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 internal.NewFD(uint32(fd)), nil } // uint64FromFile reads a uint64 from a file. All elements of path are sanitized // and joined onto base. Returns error if base no longer prefixes the path after // joining all components. func uint64FromFile(base string, path ...string) (uint64, error) { l := filepath.Join(path...) p := filepath.Join(base, l) if !strings.HasPrefix(p, base) { return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput) } data, err := os.ReadFile(p) if err != nil { return 0, fmt.Errorf("reading file %s: %w", p, err) } et := bytes.TrimSpace(data) return strconv.ParseUint(string(et), 10, 64) } golang-github-cilium-ebpf-0.7.0/link/perf_event_test.go000066400000000000000000000032721414524555700231370ustar00rootroot00000000000000package link import ( "errors" "os" "testing" "github.com/cilium/ebpf/internal/testutils" qt "github.com/frankban/quicktest" ) func TestTraceEventTypePMU(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) et, err := getPMUEventType(kprobeType) c.Assert(err, qt.IsNil) c.Assert(et, qt.Not(qt.Equals), 0) et, err = getPMUEventType(uprobeType) c.Assert(err, qt.IsNil) c.Assert(et, qt.Not(qt.Equals), 0) } func TestTraceEventID(t *testing.T) { c := qt.New(t) eid, err := getTraceEventID("syscalls", "sys_enter_execve") c.Assert(err, qt.IsNil) c.Assert(eid, qt.Not(qt.Equals), 0) } func TestTraceReadID(t *testing.T) { _, err := uint64FromFile("/base/path/", "../escaped") if !errors.Is(err, errInvalidInput) { t.Errorf("expected error %s, got: %s", errInvalidInput, err) } _, err = uint64FromFile("/base/path/not", "../not/escaped") if !errors.Is(err, os.ErrNotExist) { t.Errorf("expected os.ErrNotExist, got: %s", err) } } func TestTraceEventRegex(t *testing.T) { var 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 rgxTraceEvent.MatchString(tt.in) == tt.fail { t.Errorf("expected string '%s' to %s regex match", tt.in, exp) } }) } } golang-github-cilium-ebpf-0.7.0/link/platform.go000066400000000000000000000006251414524555700215660ustar00rootroot00000000000000package link import ( "fmt" "runtime" ) func platformPrefix(symbol string) string { prefix := runtime.GOARCH // per https://github.com/golang/go/blob/master/src/go/build/syslist.go switch prefix { case "386": prefix = "ia32" case "amd64", "amd64p32": prefix = "x64" case "arm64", "arm64be": prefix = "arm64" default: return symbol } return fmt.Sprintf("__%s_%s", prefix, symbol) } golang-github-cilium-ebpf-0.7.0/link/program.go000066400000000000000000000035421414524555700214120ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" ) 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 := internal.BPFProgAttachAttr{ TargetFd: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), ReplaceBpfFd: replaceFd, AttachType: uint32(opts.Attach), AttachFlags: uint32(opts.Flags), } if err := internal.BPFProgAttach(&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 := internal.BPFProgDetachAttr{ TargetFd: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), AttachType: uint32(opts.Attach), } if err := internal.BPFProgDetach(&attr); err != nil { return fmt.Errorf("can't detach program: %w", err) } return nil } golang-github-cilium-ebpf-0.7.0/link/program_test.go000066400000000000000000000021021414524555700224400ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestProgramAlter(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "SkSKB type") var err error var prog *ebpf.Program prog, err = ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SkSKB, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() 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.7.0/link/raw_tracepoint.go000066400000000000000000000027751414524555700227730ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" ) 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", internal.ErrClosedFd) } fd, err := bpfRawTracepointOpen(&bpfRawTracepointOpenAttr{ name: internal.NewStringPointer(opts.Name), fd: uint32(opts.Program.FD()), }) if err != nil { return nil, err } return &progAttachRawTracepoint{fd: fd}, nil } type progAttachRawTracepoint struct { fd *internal.FD } var _ Link = (*progAttachRawTracepoint)(nil) func (rt *progAttachRawTracepoint) isLink() {} func (rt *progAttachRawTracepoint) Close() error { return rt.fd.Close() } func (rt *progAttachRawTracepoint) Update(_ *ebpf.Program) error { return fmt.Errorf("can't update raw_tracepoint: %w", ErrNotSupported) } func (rt *progAttachRawTracepoint) Pin(_ string) error { return fmt.Errorf("can't pin raw_tracepoint: %w", ErrNotSupported) } func (rt *progAttachRawTracepoint) Unpin() error { return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported) } golang-github-cilium-ebpf-0.7.0/link/raw_tracepoint_test.go000066400000000000000000000024711414524555700240230ustar00rootroot00000000000000package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API") prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.RawTracepoint, AttachType: ebpf.AttachNone, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", }) if err != nil { t.Fatal(err) } defer prog.Close() link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_mkdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, testLinkOptions{ prog: prog, }) } func TestRawTracepoint_writable(t *testing.T) { testutils.SkipOnOldKernel(t, "5.2", "BPF_RAW_TRACEPOINT_WRITABLE API") prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.RawTracepointWritable, AttachType: ebpf.AttachNone, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", }) if err != nil { t.Fatal(err) } defer prog.Close() link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_rmdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, testLinkOptions{ prog: prog, }) } golang-github-cilium-ebpf-0.7.0/link/syscalls.go000066400000000000000000000106711414524555700216010ustar00rootroot00000000000000package link import ( "errors" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) // Type is the kind of link. type Type uint32 // Valid link types. // // Equivalent to enum bpf_link_type. const ( UnspecifiedType Type = iota RawTracepointType TracingType CgroupType IterType NetNsType XDPType ) var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error { 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 } // 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.FeatureTest("BPF_PROG_ATTACH atomic replacement", "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 := internal.BPFProgAttachAttr{ // We rely on this being checked after attachFlags. TargetFd: ^uint32(0), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(ebpf.AttachCGroupInetIngress), AttachFlags: uint32(flagReplace), } err = internal.BPFProgAttach(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) type bpfLinkCreateAttr struct { progFd uint32 targetFd uint32 attachType ebpf.AttachType flags uint32 targetBTFID uint32 } func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) { ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return internal.NewFD(uint32(ptr)), nil } type bpfLinkCreateIterAttr struct { prog_fd uint32 target_fd uint32 attach_type ebpf.AttachType flags uint32 iter_info internal.Pointer iter_info_len uint32 } func bpfLinkCreateIter(attr *bpfLinkCreateIterAttr) (*internal.FD, error) { ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return internal.NewFD(uint32(ptr)), nil } type bpfLinkUpdateAttr struct { linkFd uint32 newProgFd uint32 flags uint32 oldProgFd uint32 } func bpfLinkUpdate(attr *bpfLinkUpdateAttr) error { _, err := internal.BPF(internal.BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error { 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() attr := bpfLinkCreateAttr{ // This is a hopefully invalid file descriptor, which triggers EBADF. targetFd: ^uint32(0), progFd: uint32(prog.FD()), attachType: ebpf.AttachCGroupInetIngress, } _, err = bpfLinkCreate(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }) type bpfIterCreateAttr struct { linkFd uint32 flags uint32 } func bpfIterCreate(attr *bpfIterCreateAttr) (*internal.FD, error) { ptr, err := internal.BPF(internal.BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err == nil { return internal.NewFD(uint32(ptr)), nil } return nil, err } type bpfRawTracepointOpenAttr struct { name internal.Pointer fd uint32 _ uint32 } func bpfRawTracepointOpen(attr *bpfRawTracepointOpenAttr) (*internal.FD, error) { ptr, err := internal.BPF(internal.BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err == nil { return internal.NewFD(uint32(ptr)), nil } return nil, err } golang-github-cilium-ebpf-0.7.0/link/syscalls_test.go000066400000000000000000000005501414524555700226330ustar00rootroot00000000000000package 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.7.0/link/tracepoint.go000066400000000000000000000033131414524555700221070ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" ) // Tracepoint attaches the given eBPF program to the tracepoint with the given // group and name. See /sys/kernel/debug/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) // // 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) (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 !rgxTraceEvent.MatchString(group) || !rgxTraceEvent.MatchString(name) { return nil, fmt.Errorf("group and name '%s/%s' must be alphanumeric or underscore: %w", group, name, errInvalidInput) } if prog.Type() != ebpf.TracePoint { return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput) } tid, err := getTraceEventID(group, name) if err != nil { return nil, err } fd, err := openTracepointPerfEvent(tid, perfAllThreads) if err != nil { return nil, err } pe := &perfEvent{ fd: fd, tracefsID: tid, group: group, name: name, typ: tracepointEvent, } if err := pe.attach(prog); err != nil { pe.Close() return nil, err } return pe, nil } golang-github-cilium-ebpf-0.7.0/link/tracepoint_test.go000066400000000000000000000060611414524555700231510ustar00rootroot00000000000000package link import ( "errors" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) var ( tracepointSpec = ebpf.ProgramSpec{ Type: ebpf.TracePoint, License: "MIT", Instructions: asm.Instructions{ // set exit code to 0 asm.Mov.Imm(asm.R0, 0), asm.Return(), }, } ) 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, err := ebpf.NewProgram(&tracepointSpec) if err != nil { t.Fatal(err) } defer prog.Close() // printk is guaranteed to be present. // Kernels before 4.14 don't support attaching to syscall tracepoints. tp, err := Tracepoint("printk", "console", prog) if err != nil { t.Fatal(err) } if err := tp.Close(); err != nil { t.Error("closing tracepoint:", err) } } func TestTracepointMissing(t *testing.T) { prog, err := ebpf.NewProgram(&tracepointSpec) if err != nil { t.Fatal(err) } defer prog.Close() _, err = Tracepoint("missing", "foobazbar", prog) 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) // empty names c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint("_", "_", nil) // empty prog c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint(".", "+", &ebpf.Program{}) // illegal chars in group/name c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) _, err = Tracepoint("foo", "bar", &ebpf.Program{}) // wrong prog type c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue) } func TestTraceGetEventID(t *testing.T) { _, err := getTraceEventID("syscalls", "sys_enter_openat") if err != nil { t.Fatal("Can't read trace event ID:", err) } _, err = getTraceEventID("totally", "bogus") if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected os.ErrNotExist, got", err) } } 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) // Open Tracepoint at /sys/kernel/debug/tracing/events/syscalls/sys_enter_getpid // and attach it to the ebpf program created above. tp, err := Tracepoint("syscalls", "sys_enter_getpid", p) 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.7.0/link/uprobe.go000066400000000000000000000177621414524555700212500ustar00rootroot00000000000000package link import ( "debug/elf" "errors" "fmt" "os" "path/filepath" "regexp" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" ) var ( uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events") // rgxUprobeSymbol is used to strip invalid characters from the uprobe symbol // as they are not allowed to be used as the EVENT token in tracefs. rgxUprobeSymbol = regexp.MustCompile("[^a-zA-Z0-9]+") uprobeRetprobeBit = struct { once sync.Once value uint64 err error }{} // 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 symbols and dynamic symbols offsets. offsets map[string]uint64 } // UprobeOptions defines additional parameters that will be used // when loading Uprobes. type UprobeOptions struct { // Symbol offset. Must be provided in case of external symbols (shared libs). // If set, overrides the offset eventually parsed from the executable. 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 } // 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 := os.Open(path) if err != nil { return nil, fmt.Errorf("open file '%s': %w", path, err) } defer f.Close() se, err := internal.NewSafeELFFile(f) if err != nil { return nil, fmt.Errorf("parse ELF file: %w", err) } if se.Type != elf.ET_EXEC && se.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") } ex := Executable{ path: path, offsets: make(map[string]uint64), } if err := ex.load(se); err != nil { return nil, err } return &ex, 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 } off := 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 off = s.Value - prog.Vaddr + prog.Off break } } ex.offsets[s.Name] = off } return nil } func (ex *Executable) offset(symbol string) (uint64, error) { if off, ok := ex.offsets[symbol]; ok { // 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 off == 0 { return 0, fmt.Errorf("cannot resolve %s library call '%s', "+ "consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported) } return off, nil } return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol) } // 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}) // // 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 } err = u.attach(prog) if err != nil { u.Close() return nil, err } return u, 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}) // // 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 } err = u.attach(prog) if err != nil { u.Close() return nil, err } return u, 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) } var offset uint64 if opts != nil && opts.Offset != 0 { offset = opts.Offset } else { off, err := ex.offset(symbol) if err != nil { return nil, err } offset = off } pid := perfAllThreads if opts != nil && opts.PID != 0 { pid = opts.PID } // Use uprobe PMU if the kernel has it available. tp, err := pmuUprobe(symbol, ex.path, offset, pid, ret) 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 = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, pid, ret) if err != nil { return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) } return tp, nil } // pmuUprobe opens a perf event based on the uprobe PMU. func pmuUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { return pmuProbe(uprobeType, symbol, path, offset, pid, ret) } // tracefsUprobe creates a Uprobe tracefs entry. func tracefsUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { return tracefsProbe(uprobeType, symbol, path, offset, pid, ret) } // uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore. func uprobeSanitizedSymbol(symbol string) string { return rgxUprobeSymbol.ReplaceAllString(symbol, "_") } // uprobePathOffset creates the PATH:OFFSET token for the tracefs api. func uprobePathOffset(path string, offset uint64) string { return fmt.Sprintf("%s:%#x", path, offset) } func uretprobeBit() (uint64, error) { uprobeRetprobeBit.once.Do(func() { uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType) }) return uprobeRetprobeBit.value, uprobeRetprobeBit.err } golang-github-cilium-ebpf-0.7.0/link/uprobe_test.go000066400000000000000000000247741414524555700223100ustar00rootroot00000000000000package link import ( "errors" "fmt" "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/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.offset(bashSym) if err != nil { t.Fatalf("find offset: %v", err) } _, err = bashEx.offset("bogus") if err == nil { t.Fatal("find symbol: expected error") } } func TestUprobe(t *testing.T) { c := qt.New(t) prog, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() up, err := bashEx.Uprobe(bashSym, prog, nil) c.Assert(err, qt.IsNil) defer up.Close() testLink(t, up, testLinkOptions{ prog: prog, }) } func TestUprobeExtNotFound(t *testing.T) { prog, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() // 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, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() // This Uprobe is broken and will not work because the offset is not // correct. This is expected since the offset is provided by the user. up, err := bashEx.Uprobe("open", prog, &UprobeOptions{Offset: 0x1}) if err != nil { t.Fatal(err) } defer up.Close() } func TestUprobeWithPID(t *testing.T) { prog, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() 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, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() // 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, err := ebpf.NewProgram(&kprobeSpec) if err != nil { t.Fatal(err) } defer prog.Close() up, err := bashEx.Uretprobe(bashSym, prog, nil) c.Assert(err, qt.IsNil) defer up.Close() testLink(t, up, testLinkOptions{ prog: 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.offset(bashSym) c.Assert(err, qt.IsNil) // uprobe PMU pu, err := pmuUprobe(bashSym, bashEx.path, off, perfAllThreads, false) c.Assert(err, qt.IsNil) defer pu.Close() c.Assert(pu.typ, qt.Equals, uprobeEvent) // uretprobe PMU pr, err := pmuUprobe(bashSym, bashEx.path, off, perfAllThreads, true) c.Assert(err, qt.IsNil) defer pr.Close() c.Assert(pr.typ, qt.Equals, uretprobeEvent) } // 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.offset(bashSym) c.Assert(err, qt.IsNil) pk, err := pmuUprobe(bashSym, bashEx.path, off, perfAllThreads, false) 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.offset(bashSym) c.Assert(err, qt.IsNil) // Sanitize the symbol in order to be used in tracefs API. ssym := uprobeSanitizedSymbol(bashSym) // Open and close tracefs u(ret)probes, checking all errors. up, err := tracefsUprobe(ssym, bashEx.path, off, perfAllThreads, false) c.Assert(err, qt.IsNil) c.Assert(up.Close(), qt.IsNil) c.Assert(up.typ, qt.Equals, uprobeEvent) up, err = tracefsUprobe(ssym, bashEx.path, off, perfAllThreads, true) c.Assert(err, qt.IsNil) c.Assert(up.Close(), qt.IsNil) c.Assert(up.typ, qt.Equals, uretprobeEvent) // Create two identical trace events, ensure their IDs differ. u1, err := tracefsUprobe(ssym, bashEx.path, off, perfAllThreads, false) c.Assert(err, qt.IsNil) defer u1.Close() c.Assert(u1.tracefsID, qt.Not(qt.Equals), 0) u2, err := tracefsUprobe(ssym, bashEx.path, off, perfAllThreads, false) c.Assert(err, qt.IsNil) defer u2.Close() c.Assert(u2.tracefsID, qt.Not(qt.Equals), 0) // Compare the uprobes' tracefs IDs. c.Assert(u1.tracefsID, qt.Not(qt.CmpEquals()), u2.tracefsID) } // Test u(ret)probe creation writing directly to /uprobe_events. // Only runs on 5.0 and over. Earlier versions ignored writes of duplicate // events, while 5.0 started returning -EEXIST when a uprobe event already // exists. func TestUprobeCreateTraceFS(t *testing.T) { testutils.SkipOnOldKernel(t, "5.0", "/uprobe_events doesn't reject duplicate events") c := qt.New(t) // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.offset(bashSym) c.Assert(err, qt.IsNil) // Sanitize the symbol in order to be used in tracefs API. ssym := uprobeSanitizedSymbol(bashSym) pg, _ := randomGroup("ebpftest") rg, _ := randomGroup("ebpftest") // Tee up cleanups in case any of the Asserts abort the function. defer func() { _ = closeTraceFSProbeEvent(uprobeType, pg, ssym) _ = closeTraceFSProbeEvent(uprobeType, rg, ssym) }() // Create a uprobe. err = createTraceFSProbeEvent(uprobeType, pg, ssym, bashEx.path, off, false) c.Assert(err, qt.IsNil) // Attempt to create an identical uprobe using tracefs, // expect it to fail with os.ErrExist. err = createTraceFSProbeEvent(uprobeType, pg, ssym, bashEx.path, off, false) c.Assert(errors.Is(err, os.ErrExist), qt.IsTrue, qt.Commentf("expected consecutive uprobe creation to contain os.ErrExist, got: %v", err)) // Expect a successful close of the kprobe. c.Assert(closeTraceFSProbeEvent(uprobeType, pg, ssym), qt.IsNil) // Same test for a kretprobe. err = createTraceFSProbeEvent(uprobeType, rg, ssym, bashEx.path, off, true) c.Assert(err, qt.IsNil) err = createTraceFSProbeEvent(uprobeType, rg, ssym, bashEx.path, off, true) c.Assert(os.IsExist(err), qt.IsFalse, qt.Commentf("expected consecutive uretprobe creation to contain os.ErrExist, got: %v", err)) // Expect a successful close of the uretprobe. c.Assert(closeTraceFSProbeEvent(uprobeType, rg, ssym), qt.IsNil) } func TestUprobeSanitizedSymbol(t *testing.T) { tests := []struct { symbol string expected string }{ {"readline", "readline"}, {"main.Func", "main_Func"}, {"a.....a", "a_a"}, {"./;'{}[]a", "_a"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { sanitized := uprobeSanitizedSymbol(tt.symbol) if tt.expected != sanitized { t.Errorf("Expected sanitized symbol to be '%s', got '%s'", tt.expected, sanitized) } }) } } func TestUprobePathOffset(t *testing.T) { tests := []struct { path string offset uint64 expected string }{ {"/bin/bash", 0, "/bin/bash:0x0"}, {"/bin/bash", 1, "/bin/bash:0x1"}, {"/bin/bash", 65535, "/bin/bash:0xffff"}, {"/bin/bash", 65536, "/bin/bash:0x10000"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { po := uprobePathOffset(tt.path, tt.offset) if tt.expected != po { t.Errorf("Expected path:offset to be '%s', got '%s'", tt.expected, po) } }) } } 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) // 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) // 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) } golang-github-cilium-ebpf-0.7.0/linker.go000066400000000000000000000072501414524555700202720ustar00rootroot00000000000000package ebpf import ( "fmt" "github.com/cilium/ebpf/asm" ) // link resolves bpf-to-bpf calls. // // Each library may contain multiple functions / labels, and is only linked // if prog references one of these functions. // // Libraries also linked. func link(prog *ProgramSpec, libs []*ProgramSpec) error { var ( linked = make(map[*ProgramSpec]bool) pending = []asm.Instructions{prog.Instructions} insns asm.Instructions ) for len(pending) > 0 { insns, pending = pending[0], pending[1:] for _, lib := range libs { if linked[lib] { continue } needed, err := needSection(insns, lib.Instructions) if err != nil { return fmt.Errorf("linking %s: %w", lib.Name, err) } if !needed { continue } linked[lib] = true prog.Instructions = append(prog.Instructions, lib.Instructions...) pending = append(pending, lib.Instructions) if prog.BTF != nil && lib.BTF != nil { if err := prog.BTF.Append(lib.BTF); err != nil { return fmt.Errorf("linking BTF of %s: %w", lib.Name, err) } } } } return nil } func needSection(insns, section asm.Instructions) (bool, error) { // A map of symbols to the libraries which contain them. symbols, err := section.SymbolOffsets() if err != nil { return false, err } for _, ins := range insns { if ins.Reference == "" { continue } if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.PseudoCall { continue } if ins.Constant != -1 { // This is already a valid call, no need to link again. continue } if _, ok := symbols[ins.Reference]; !ok { // Symbol isn't available in this section continue } // At this point we know that at least one function in the // library is called from insns, so we have to link it. return true, nil } // None of the functions in the section are called. return false, nil } func fixupJumpsAndCalls(insns asm.Instructions) error { symbolOffsets := make(map[string]asm.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 } iter = insns.Iterate() for iter.Next() { i := iter.Index offset := iter.Offset ins := iter.Ins if ins.Reference == "" { continue } switch { case ins.IsFunctionCall() && ins.Constant == -1: // Rewrite bpf to bpf call callOffset, ok := symbolOffsets[ins.Reference] if !ok { return fmt.Errorf("call at %d: reference to missing symbol %q", i, ins.Reference) } ins.Constant = int64(callOffset - offset - 1) case ins.OpCode.Class() == asm.JumpClass && ins.Offset == -1: // Rewrite jump to label jumpOffset, ok := symbolOffsets[ins.Reference] if !ok { return fmt.Errorf("jump at %d: reference to missing symbol %q", i, ins.Reference) } ins.Offset = int16(jumpOffset - offset - 1) case ins.IsLoadFromMap() && ins.MapPtr() == -1: return fmt.Errorf("map %s: %w", ins.Reference, errUnsatisfiedReference) } } // fixupBPFCalls replaces bpf_probe_read_{kernel,user}[_str] with bpf_probe_read[_str] on older kernels // https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L6009 iter = insns.Iterate() for iter.Next() { ins := iter.Ins if !ins.IsBuiltinCall() { continue } switch asm.BuiltinFunc(ins.Constant) { case asm.FnProbeReadKernel, asm.FnProbeReadUser: if err := haveProbeReadKernel(); err != nil { ins.Constant = int64(asm.FnProbeRead) } case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr: if err := haveProbeReadKernel(); err != nil { ins.Constant = int64(asm.FnProbeReadStr) } } } return nil } golang-github-cilium-ebpf-0.7.0/linker_test.go000066400000000000000000000020731414524555700213270ustar00rootroot00000000000000package ebpf import ( "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestLink(t *testing.T) { spec := &ProgramSpec{ 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", } libs := []*ProgramSpec{ { Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 1337, asm.DWord).Sym("my_other_func"), asm.Return(), }, }, { Instructions: asm.Instructions{ asm.Call.Label("my_other_func").Sym("my_func"), asm.Return(), }, }, } err := link(spec, libs) if err != nil { t.Fatal(err) } t.Log(spec.Instructions) testutils.SkipOnOldKernel(t, "4.16", "bpf2bpf calls") prog, err := NewProgram(spec) if err != nil { t.Fatal(err) } ret, _, err := prog.Test(make([]byte, 14)) if err != nil { t.Fatal(err) } if ret != 1337 { t.Errorf("Expected return code 1337, got %d", ret) } } golang-github-cilium-ebpf-0.7.0/map.go000066400000000000000000001005321414524555700175600ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "io" "path/filepath" "reflect" "strings" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "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's spec is incompatible with pinned map") ) // 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. Must be empty before instantiating // the MapSpec into a Map. Extra bytes.Reader // The BTF associated with this map. BTF *btf.Map } 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 } // MapKV is used to initialize the contents of a Map. type MapKV struct { Key interface{} Value interface{} } func (ms *MapSpec) checkCompatibility(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 m.maxEntries != ms.MaxEntries: return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible) case 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 *internal.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) { if fd < 0 { return nil, errors.New("invalid fd") } return newMapFromFD(internal.NewFD(uint32(fd))) } func newMapFromFD(fd *internal.FD) (*Map, error) { info, err := newMapInfoFromFd(fd) if err != nil { fd.Close() return nil, fmt.Errorf("get map info: %s", 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) { handles := newHandleCache() defer handles.close() m, err := newMapWithOptions(spec, opts, handles) if err != nil { return nil, fmt.Errorf("creating map: %w", err) } err = m.finalize(spec) if err != nil { return nil, fmt.Errorf("populating map: %w", err) } return m, nil } func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *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.checkCompatibility(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 *internal.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, handles) 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, handles) 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: %s", 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 *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) { closeOnError := func(closer io.Closer) { if err != nil { closer.Close() } } spec = spec.Copy() // 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 _, 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.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") } spec.KeySize = 4 if spec.ValueSize != 0 && spec.ValueSize != 4 { return nil, errors.New("ValueSize must be zero or four for perf event array") } 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) } } attr := internal.BPFMapCreateAttr{ MapType: uint32(spec.Type), KeySize: spec.KeySize, ValueSize: spec.ValueSize, MaxEntries: spec.MaxEntries, Flags: spec.Flags, NumaNode: spec.NumaNode, } if inner != nil { var err error attr.InnerMapFd, err = inner.Value() if err != nil { return nil, fmt.Errorf("map create: %w", err) } } if haveObjName() == nil { attr.MapName = internal.NewBPFObjName(spec.Name) } var btfDisabled bool if spec.BTF != nil { handle, err := handles.btfHandle(spec.BTF.Spec) btfDisabled = errors.Is(err, btf.ErrNotSupported) if err != nil && !btfDisabled { return nil, fmt.Errorf("load BTF: %w", err) } if handle != nil { attr.BTFFd = uint32(handle.FD()) attr.BTFKeyTypeID = uint32(spec.BTF.Key.ID()) attr.BTFValueTypeID = uint32(spec.BTF.Value.ID()) } } fd, err := internal.BPFMapCreate(&attr) if err != nil { if errors.Is(err, unix.EPERM) { return nil, fmt.Errorf("map create: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", err) } if btfDisabled { return nil, fmt.Errorf("map create without BTF: %w", 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 *internal.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 = internal.Align(int(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) } // 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 { valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) if err := m.lookup(key, valuePtr); 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 { valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { return fmt.Errorf("lookup and delete failed: %w", 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 := internal.NewSlicePointer(valueBytes) err := m.lookup(key, valuePtr) if errors.Is(err, ErrKeyNotExist) { return nil, nil } return valueBytes, err } func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil { return fmt.Errorf("lookup failed: %w", 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 ) // 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 interface{}, flags MapUpdateFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } valuePtr, err := m.marshalValue(value) if err != nil { return fmt.Errorf("can't marshal value: %w", err) } if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil { return fmt.Errorf("update failed: %w", 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) } if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil { return fmt.Errorf("delete failed: %w", 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 := internal.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 internal.Pointer) error { var ( keyPtr internal.Pointer err error ) if key != nil { keyPtr, err = m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } } if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil { return fmt.Errorf("next key failed: %w", err) } return nil } // 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(internal.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(internal.BPF_MAP_LOOKUP_AND_DELETE_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) } func (m *Map) batchLookup(cmd internal.BPFCmd, 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 := internal.NewSlicePointer(keyBuf) valueBuf := make([]byte, count*int(m.fullValueSize)) valuePtr := internal.NewSlicePointer(valueBuf) var ( startPtr internal.Pointer err error retErr error ) if startKey != nil { startPtr, err = marshalPtr(startKey, int(m.keySize)) if err != nil { return 0, err } } nextPtr, nextBuf := makeBuffer(nextKeyOut, int(m.keySize)) ct, err := bpfMapBatch(cmd, m.fd, startPtr, nextPtr, keyPtr, valuePtr, uint32(count), opts) if err != nil { if !errors.Is(err, ErrKeyNotExist) { return 0, err } retErr = ErrKeyNotExist } 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 { retErr = err } return int(ct), retErr } // 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 internal.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 } var nilPtr internal.Pointer ct, err := bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, valuePtr, uint32(count), opts) return int(ct), err } // 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) } var nilPtr internal.Pointer ct, err := bpfMapBatch(internal.BPF_MAP_DELETE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, nilPtr, uint32(count), opts) return int(ct), err } // 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 removes a Map 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 { fd, err := m.fd.Value() if err != nil { // Best effort: -1 is the number most likely to be an // invalid file descriptor. return -1 } return int(fd) } // 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/k8s-doc/admin/#admin-mount-bpffs 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) } if err := bpfMapFreeze(m.fd); 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{}) (internal.Pointer, error) { if data == nil { if m.keySize == 0 { // Queues have a key length of zero, so passing nil here is valid. return internal.NewPointer(nil), nil } return internal.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{}) (internal.Pointer, error) { if m.typ.hasPerCPUValue() { return marshalPerCPUValue(data, int(m.valueSize)) } var ( buf []byte err error ) switch value := data.(type) { case *Map: if !m.typ.canStoreMap() { return internal.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 internal.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 internal.Pointer{}, err } return internal.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 := internal.BPFObjGet(fileName, 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) } fd, err := m.fd.Value() if err != nil { return nil, err } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, fd) return buf, nil } func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) error { replaced := make(map[string]bool) replace := func(name string, offset, size int, replacement interface{}) error { if offset+size > len(value) { return fmt.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) } buf, err := marshalBytes(replacement, size) if err != nil { return fmt.Errorf("marshal %s: %w", name, err) } copy(value[offset:offset+size], buf) replaced[name] = true return nil } switch parent := typ.(type) { case *btf.Datasec: for _, secinfo := range parent.Vars { name := string(secinfo.Type.(*btf.Var).Name) replacement, ok := replacements[name] if !ok { continue } err := replace(name, int(secinfo.Offset), int(secinfo.Size), replacement) if err != nil { return err } } default: return fmt.Errorf("patching %T is not supported", typ) } if len(replaced) == len(replacements) { return nil } var missing []string for name := range replacements { if !replaced[name] { missing = append(missing, name) } } if len(missing) == 1 { return fmt.Errorf("unknown field: %s", missing[0]) } return fmt.Errorf("unknown fields: %s", strings.Join(missing, ",")) } // MapIterator iterates a Map. // // See Map.Iterate. type MapIterator struct { target *Map prevKey interface{} prevBytes []byte count, maxEntries uint32 done bool err error } func newMapIterator(target *Map) *MapIterator { return &MapIterator{ target: target, maxEntries: target.maxEntries, prevBytes: make([]byte, target.keySize), } } // 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 nextBytes []byte nextBytes, mi.err = mi.target.NextKeyBytes(mi.prevKey) if mi.err != nil { return false } if nextBytes == nil { mi.done = true return false } // The user can get access to nextBytes since unmarshalBytes // does not copy when unmarshaling into a []byte. // Make a copy to prevent accidental corruption of // iterator state. copy(mi.prevBytes, nextBytes) mi.prevKey = mi.prevBytes mi.count++ mi.err = mi.target.Lookup(nextBytes, 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 { return false } mi.err = mi.target.unmarshalKey(keyOut, nextBytes) 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) { id, err := objGetNextID(internal.BPF_MAP_GET_NEXT_ID, uint32(startID)) return MapID(id), err } // 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 := internal.BPFObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, err } return newMapFromFD(fd) } // ID returns the systemwide unique ID of the map. // // Deprecated: use MapInfo.ID() instead. func (m *Map) ID() (MapID, error) { info, err := bpfGetMapInfoByFD(m.fd) if err != nil { return MapID(0), err } return MapID(info.id), nil } golang-github-cilium-ebpf-0.7.0/map_test.go000066400000000000000000001125521414524555700206240ustar00rootroot00000000000000package 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/btf" "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, } ) 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, internal.ErrClosedFd) { t.Fatal("Put doesn't check for closed fd", err) } if _, err := m.LookupBytes(uint32(0)); !errors.Is(err, internal.ErrClosedFd) { t.Fatal("Get doesn't check for closed fd", 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 { t.Fatal(err) } pinned := m.IsPinned() c.Assert(pinned, qt.Equals, true) m.Close() m, err := LoadPinnedMap(path, nil) 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) 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) { 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.Equals, true) newPath := filepath.Join(tmp, "bar") err = m1.Pin(newPath) 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) 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.Equals, true) 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.Equals, true) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) 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.Equals, true) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) c.Assert(err, qt.IsNil) defer m2.Close() pinned = m2.IsPinned() c.Assert(pinned, qt.Equals, true) } 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.Equals, true) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) 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.14", "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(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 TestNotExist(t *testing.T) { hash := createHash() defer hash.Close() 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") } 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") } } func TestExist(t *testing.T) { hash := createHash() defer hash.Close() 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() ) defer m.Close() if !entries.Next(&key, &m) { t.Fatal("Iterator encountered error:", entries.Err()) } 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 == 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 := internal.BPFProgAttachAttr{ TargetFd: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), AttachFlags: 0, ReplaceBpfFd: 0, } err = internal.BPFProgAttach(&progAttachAttrs) if err != nil { t.Fatal(err) } defer func() { attr := internal.BPFProgDetachAttr{ TargetFd: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), } if err := internal.BPFProgDetach(&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() info, err := bpfGetMapInfoByFD(m.fd) if err != nil { t.Fatal(err) } if name := internal.CString(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(m.FD()) if err != nil { t.Fatal(err) } // Both m and m2 refer to the same fd now. Closing either of them will // release the fd to the OS, which then might re-use that fd for another // test. Once we close the second map we might close the re-used fd // inadvertently, leading to spurious test failures. // To avoid this we have to "leak" one of the maps. m2.fd.Forget() 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 hash := createHash() defer hash.Close() 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, 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) { testutils.SkipOnOldKernel(t, "4.13", "bpf_map_get_fd_by_id") hash := createHash() defer hash.Close() var next MapID var err error next, err = hash.ID() if err != nil { t.Fatal("Could not get ID of map:", err) } if _, err = NewMapFromID(next); err != nil { t.Fatalf("Can't get map for ID %d: %v", uint32(next), err) } // 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, 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.Equals, true) if err := m1.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't write value:", err) } // This is a terrible hack: if loading a pinned map tries to load BTF, // it will get a nil *btf.Spec from this *btf.Map. This is turn will make // btf.NewHandle fail. spec.BTF = new(btf.Map) m2, err := NewMapWithOptions(spec, MapOptions{PinPath: tmp}) if err != nil { t.Fatal("Can't create map:", err) } defer m2.Close() 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 } func BenchmarkMarshalling(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.Run("reflection", 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("custom", 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("unsafe", 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.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) } 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) } } }) } // 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) } 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 := createHash() 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 createHash() *Map { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } return hash } func ExampleMap_NextKey() { hash := createHash() 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 := createHash() 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.7.0/marshaler_example_test.go000066400000000000000000000017441414524555700235400ustar00rootroot00000000000000package 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 := createHash() 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.7.0/marshalers.go000066400000000000000000000151721414524555700211510ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding" "encoding/binary" "errors" "fmt" "reflect" "runtime" "sync" "unsafe" "github.com/cilium/ebpf/internal" ) // 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) (internal.Pointer, error) { if ptr, ok := data.(unsafe.Pointer); ok { return internal.NewPointer(ptr), nil } buf, err := marshalBytes(data, length) if err != nil { return internal.Pointer{}, err } return internal.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: var wr bytes.Buffer 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) (internal.Pointer, []byte) { if ptr, ok := dst.(unsafe.Pointer); ok { return internal.NewPointer(ptr), nil } buf := make([]byte, length) return internal.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: var dst []byte // Use unsafe.Slice when we drop support for pre1.17 (https://github.com/golang/go/issues/19367) // We could opt for removing unsafe.Pointer support in the lib as well sh := (*reflect.SliceHeader)(unsafe.Pointer(&dst)) sh.Data = uintptr(value) sh.Len = len(buf) sh.Cap = 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) (internal.Pointer, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return internal.Pointer{}, errors.New("per-CPU value requires slice") } possibleCPUs, err := internal.PossibleCPUs() if err != nil { return internal.Pointer{}, err } sliceValue := reflect.ValueOf(slice) sliceLen := sliceValue.Len() if sliceLen > possibleCPUs { return internal.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 internal.Pointer{}, err } offset := i * alignedElemLength copy(buf[offset:offset+elemLength], elemBytes) } return internal.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.7.0/perf/000077500000000000000000000000001414524555700174075ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/perf/doc.go000066400000000000000000000004061414524555700205030ustar00rootroot00000000000000// 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.7.0/perf/reader.go000066400000000000000000000261261414524555700212070ustar00rootroot00000000000000package perf import ( "encoding/binary" "errors" "fmt" "io" "math" "runtime" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( ErrClosed = errors.New("perf reader was closed") errEOR = errors.New("end of ring") ) // perfEventHeader must match 'struct perf_event_header` in . type perfEventHeader struct { Type uint32 Misc uint16 Size uint16 } func addToEpoll(epollfd, fd int, cpu int) error { if int64(cpu) > math.MaxInt32 { return fmt.Errorf("unsupported CPU number: %d", cpu) } // The representation of EpollEvent isn't entirely accurate. // Pad is fully useable, not just padding. Hence we stuff the // CPU in there, which allows us to use a slice to access // the correct perf ring. event := unix.EpollEvent{ Events: unix.EPOLLIN, Fd: int32(fd), Pad: int32(cpu), } if err := unix.EpollCtl(epollfd, unix.EPOLL_CTL_ADD, fd, &event); err != nil { return fmt.Errorf("can't add fd to epoll: %v", err) } return nil } 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 } // NB: Has to be preceded by a call to ring.loadHead. func readRecordFromRing(ring *perfEventRing) (Record, error) { defer ring.writeTail() return readRecord(ring, ring.cpu) } func readRecord(rd io.Reader, cpu int) (Record, error) { var header perfEventHeader err := binary.Read(rd, internal.NativeEndian, &header) if err == io.EOF { return Record{}, errEOR } if err != nil { return Record{}, fmt.Errorf("can't read event header: %v", err) } switch header.Type { case unix.PERF_RECORD_LOST: lost, err := readLostRecords(rd) return Record{CPU: cpu, LostSamples: lost}, err case unix.PERF_RECORD_SAMPLE: sample, err := readRawSample(rd) return Record{CPU: cpu, RawSample: sample}, err default: return Record{}, &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 } func readRawSample(rd io.Reader) ([]byte, error) { // This must match 'struct perf_event_sample in kernel sources. var size uint32 if err := binary.Read(rd, internal.NativeEndian, &size); err != nil { return nil, fmt.Errorf("can't read sample size: %v", err) } data := make([]byte, int(size)) if _, err := io.ReadFull(rd, data); err != nil { return nil, fmt.Errorf("can't read sample: %v", err) } return data, nil } // Reader allows reading bpf_perf_event_output // from user space. type Reader struct { // 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 epollFd int epollEvents []unix.EpollEvent epollRings []*perfEventRing // Eventfds for closing closeFd int // Ensure we only close once closeOnce sync.Once // 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 } // 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 } // 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") } epollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) if err != nil { return nil, fmt.Errorf("can't create epoll fd: %v", err) } var ( fds = []int{epollFd} nCPU = int(array.MaxEntries()) rings = make([]*perfEventRing, 0, nCPU) pauseFds = make([]int, 0, nCPU) ) defer func() { if err != nil { 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) 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 := addToEpoll(epollFd, ring.fd, len(rings)-1); err != nil { return nil, err } } closeFd, err := unix.Eventfd(0, unix.O_CLOEXEC|unix.O_NONBLOCK) if err != nil { return nil, err } fds = append(fds, closeFd) if err := addToEpoll(epollFd, closeFd, -1); err != nil { return nil, err } array, err = array.Clone() if err != nil { return nil, err } pr = &Reader{ array: array, rings: rings, epollFd: epollFd, // Allocate extra event for closeFd epollEvents: make([]unix.EpollEvent, len(rings)+1), epollRings: make([]*perfEventRing, 0, len(rings)), closeFd: closeFd, pauseFds: pauseFds, } 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 { var err error pr.closeOnce.Do(func() { runtime.SetFinalizer(pr, nil) // Interrupt Read() via the event fd. var value [8]byte internal.NativeEndian.PutUint64(value[:], 1) _, err = unix.Write(pr.closeFd, value[:]) if err != nil { err = fmt.Errorf("can't write event fd: %v", err) return } // Acquire the locks. This ensures that Read, Pause and Resume // aren't running. pr.mu.Lock() defer pr.mu.Unlock() pr.pauseMu.Lock() defer pr.pauseMu.Unlock() unix.Close(pr.epollFd) unix.Close(pr.closeFd) pr.epollFd, pr.closeFd = -1, -1 // Close rings for _, ring := range pr.rings { if ring != nil { ring.Close() } } pr.rings = nil pr.pauseFds = nil pr.array.Close() }) if err != nil { return fmt.Errorf("close PerfReader: %w", err) } return nil } // 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. func (pr *Reader) Read() (Record, error) { pr.mu.Lock() defer pr.mu.Unlock() if pr.epollFd == -1 { return Record{}, fmt.Errorf("%w", ErrClosed) } for { if len(pr.epollRings) == 0 { nEvents, err := unix.EpollWait(pr.epollFd, pr.epollEvents, -1) if temp, ok := err.(temporaryError); ok && temp.Temporary() { // Retry the syscall if we we're interrupted, see https://github.com/golang/go/issues/20400 continue } if err != nil { return Record{}, err } for _, event := range pr.epollEvents[:nEvents] { if int(event.Fd) == pr.closeFd { return Record{}, fmt.Errorf("%w", ErrClosed) } 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. record, err := readRecordFromRing(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 record, 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) } } 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) } } return nil } type temporaryError interface { Temporary() bool } // IsClosed returns true if the error occurred because // a Reader was closed. // // Deprecated: use errors.Is(err, ErrClosed) instead. func IsClosed(err error) bool { return errors.Is(err, ErrClosed) } 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.7.0/perf/reader_test.go000066400000000000000000000263261414524555700222500ustar00rootroot00000000000000package perf import ( "bytes" "encoding/binary" "errors" "fmt" "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/unix" qt "github.com/frankban/quicktest" ) var ( readTimeout = 250 * time.Millisecond ) func TestPerfReader(t *testing.T) { prog, events := mustOutputSamplesProg(t, 5) defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() ret, _, err := prog.Test(make([]byte, 14)) 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) } record, err := rd.Read() if err != nil { t.Fatal("Can't read samples:", err) } want := []byte{1, 2, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0} if !bytes.Equal(record.RawSample, want) { t.Log(record.RawSample) t.Error("Sample doesn't match expected output") } if record.CPU < 0 { t.Error("Record has invalid CPU number") } } func outputSamplesProg(sampleSizes ...int) (*ebpf.Program, *ebpf.Map, error) { const bpfFCurrentCPU = 0xffffffff events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.PerfEventArray, }) if err != nil { return nil, nil, err } var maxSampleSize int for _, sampleSize := range sampleSizes { if sampleSize > maxSampleSize { maxSampleSize = sampleSize } } // Fill a buffer on the stack, and stash context somewhere 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 _, sampleSize := range sampleSizes { insns = append(insns, asm.Mov.Reg(asm.R1, asm.R9), asm.LoadMapPtr(asm.R2, events.FD()), asm.LoadImm(asm.R3, bpfFCurrentCPU, asm.DWord), asm.Mov.Reg(asm.R4, asm.RFP), asm.Add.Imm(asm.R4, int32(bufDwords*-8)), 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 { events.Close() return nil, nil, err } return prog, events, nil } func mustOutputSamplesProg(tb testing.TB, sampleSizes ...int) (*ebpf.Program, *ebpf.Map) { tb.Helper() prog, events, err := outputSamplesProg(sampleSizes...) if err != nil { tb.Fatal(err) } return prog, events } 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 []int // 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) prog, events := mustOutputSamplesProg(t, sampleSizes...) defer prog.Close() defer events.Close() rd, err := NewReader(events, pageSize) if err != nil { t.Fatal(err) } defer rd.Close() ret, _, err := prog.Test(make([]byte, 14)) 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) } 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 TestPerfReaderClose(t *testing.T) { prog, events := mustOutputSamplesProg(t, 5) defer prog.Close() defer events.Close() 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) 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) } _, err = readRecord(&buf, 0) if !IsUnknownEvent(err) { t.Error("readRecord should return unknown event error, got", err) } } func TestPause(t *testing.T) { t.Parallel() prog, events := mustOutputSamplesProg(t, 5) defer prog.Close() defer events.Close() 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. ret, _, err := prog.Test(make([]byte, 14)) 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(make([]byte, 14)) 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(make([]byte, 14)) 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) { prog, events := mustOutputSamplesProg(b, 80) defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { b.Fatal(err) } defer rd.Close() buf := make([]byte, 14) 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) } } } // This exists just to make the example below nicer. func bpfPerfEventOutputProgram() (*ebpf.Program, *ebpf.Map) { prog, events, err := outputSamplesProg(5) if err != nil { panic(err) } return prog, events } // 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(make([]byte, 14)) 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) } golang-github-cilium-ebpf-0.7.0/perf/ring.go000066400000000000000000000072251414524555700207030ustar00rootroot00000000000000package 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) (*perfEventRing, error) { if watermark >= perCPUBuffer { return nil, errors.New("watermark must be smaller than perCPUBuffer") } fd, err := createPerfEvent(cpu, watermark) if err != nil { return nil, err } if err := unix.SetNonblock(fd, true); err != nil { unix.Close(fd) return nil, err } mmap, err := unix.Mmap(fd, 0, perfBufferSize(perCPUBuffer), unix.PROT_READ|unix.PROT_WRITE, 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])) ring := &perfEventRing{ fd: fd, cpu: cpu, mmap: mmap, ringReader: newRingReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]), } runtime.SetFinalizer(ring, (*perfEventRing).Close) return ring, nil } // mmapBufferSize 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) (int, error) { if watermark == 0 { watermark = 1 } attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_SOFTWARE, Config: unix.PERF_COUNT_SW_BPF_OUTPUT, Bits: unix.PerfBitWatermark, 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 struct { meta *unix.PerfEventMmapPage head, tail uint64 mask uint64 ring []byte } func newRingReader(meta *unix.PerfEventMmapPage, ring []byte) *ringReader { return &ringReader{ 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 *ringReader) loadHead() { rr.head = atomic.LoadUint64(&rr.meta.Data_head) } func (rr *ringReader) 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 *ringReader) 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 } golang-github-cilium-ebpf-0.7.0/perf/ring_test.go000066400000000000000000000045261414524555700217430ustar00rootroot00000000000000package perf import ( "bytes" "io" "os" "testing" "github.com/cilium/ebpf/internal/unix" ) 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) } // Wrapping read ring = makeRing(2, 1) n, err = io.ReadFull(ring, buf) if err != nil { t.Error("Error while reading:", err) } if n != 2 { t.Errorf("Expected to read 2 byte, got %d", n) } if !bytes.Equal(buf, []byte{1, 0}) { t.Error("Expected [1, 0], got", buf) } } func makeRing(size, offset int) *ringReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } ring := make([]byte, size) for i := range ring { ring[i] = byte(i) } meta := unix.PerfEventMmapPage{ Data_head: uint64(len(ring) + offset), Data_tail: uint64(offset), Data_size: uint64(len(ring)), } return newRingReader(&meta, ring) } func TestPerfEventRing(t *testing.T) { check := func(buffer, watermark int) { ring, err := newPerfEventRing(0, buffer, watermark) if err != nil { t.Fatal(err) } size := len(ring.ringReader.ring) // 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) if err == nil { t.Fatal("watermark > buffer allowed") } // watermark == buffer _, err = newPerfEventRing(0, 8192, 8192) if err == nil { t.Fatal("watermark == buffer allowed") } // buffer not a power of two, watermark < buffer check(8193, 8192) // large buffer not a multiple of page size at all (prime) check(65537, 8192) } golang-github-cilium-ebpf-0.7.0/prog.go000066400000000000000000000510251414524555700177540ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" "path/filepath" "strings" "time" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" ) // ErrNotSupported is returned whenever the kernel doesn't support a feature. var ErrNotSupported = internal.ErrNotSupported var errUnsatisfiedReference = errors.New("unsatisfied reference") // 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 // ProgramOptions control loading a program into the kernel. type ProgramOptions struct { // Controls the detail emitted by the kernel verifier. Set to non-zero // to enable logging. LogLevel uint32 // Controls the output buffer size for the verifier. Defaults to // DefaultVerifierLogSize. LogSize int // An ELF containing the target BTF for this program. It is used both to // find the correct function to trace and to apply 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. TargetBTF io.ReaderAt } // 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 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 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 BTF associated with this program. Changing Instructions // will most likely invalidate the contained data, and may // result in errors when attempting to load it into the kernel. BTF *btf.Program // 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) } // 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 *internal.FD name string pinnedPath string typ ProgramType } // NewProgram creates a new Program. // // Loading a program for the first time will perform // feature detection by loading small, temporary programs. 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. func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { handles := newHandleCache() defer handles.close() prog, err := newProgramWithOptions(spec, opts, handles) if errors.Is(err, errUnsatisfiedReference) { return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err) } return prog, err } func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) { if len(spec.Instructions) == 0 { return nil, errors.New("instructions cannot be empty") } if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian) } // 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 := &internal.BPFProgLoadAttr{ ProgType: uint32(spec.Type), ProgFlags: spec.Flags, ExpectedAttachType: uint32(spec.AttachType), License: internal.NewStringPointer(spec.License), KernelVersion: kv, } if haveObjName() == nil { attr.ProgName = internal.NewBPFObjName(spec.Name) } var err error var targetBTF *btf.Spec if opts.TargetBTF != nil { targetBTF, err = handles.btfSpec(opts.TargetBTF) if err != nil { return nil, fmt.Errorf("load target BTF: %w", err) } } var btfDisabled bool var core btf.COREFixups if spec.BTF != nil { core, err = spec.BTF.Fixups(targetBTF) if err != nil { return nil, fmt.Errorf("CO-RE relocations: %w", err) } handle, err := handles.btfHandle(spec.BTF.Spec()) btfDisabled = errors.Is(err, btf.ErrNotSupported) if err != nil && !btfDisabled { return nil, fmt.Errorf("load BTF: %w", err) } if handle != nil { attr.ProgBTFFd = uint32(handle.FD()) recSize, bytes, err := spec.BTF.LineInfos() if err != nil { return nil, fmt.Errorf("get BTF line infos: %w", err) } attr.LineInfoRecSize = recSize attr.LineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.LineInfo = internal.NewSlicePointer(bytes) recSize, bytes, err = spec.BTF.FuncInfos() if err != nil { return nil, fmt.Errorf("get BTF function infos: %w", err) } attr.FuncInfoRecSize = recSize attr.FuncInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.FuncInfo = internal.NewSlicePointer(bytes) } } insns, err := core.Apply(spec.Instructions) if err != nil { return nil, fmt.Errorf("CO-RE fixup: %w", err) } if err := fixupJumpsAndCalls(insns); err != nil { return nil, err } buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize)) err = insns.Marshal(buf, internal.NativeEndian) if err != nil { return nil, err } bytecode := buf.Bytes() attr.Instructions = internal.NewSlicePointer(bytecode) attr.InsCount = uint32(len(bytecode) / asm.InstructionSize) if spec.AttachTo != "" { if spec.AttachTarget != nil { info, err := spec.AttachTarget.Info() if err != nil { return nil, fmt.Errorf("load target BTF: %w", err) } btfID, ok := info.BTFID() if !ok { return nil, fmt.Errorf("load target BTF: no BTF info available") } btfHandle, err := btf.NewHandleFromID(btfID) if err != nil { return nil, fmt.Errorf("load target BTF: %w", err) } defer btfHandle.Close() targetBTF = btfHandle.Spec() if err != nil { return nil, fmt.Errorf("load target BTF: %w", err) } } target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType) if err != nil { return nil, err } if target != nil { attr.AttachBTFID = uint32(target.ID()) } if spec.AttachTarget != nil { attr.AttachProgFd = uint32(spec.AttachTarget.FD()) } } logSize := DefaultVerifierLogSize if opts.LogSize > 0 { logSize = opts.LogSize } var logBuf []byte if opts.LogLevel > 0 { logBuf = make([]byte, logSize) attr.LogLevel = opts.LogLevel attr.LogSize = uint32(len(logBuf)) attr.LogBuf = internal.NewSlicePointer(logBuf) } fd, err := internal.BPFProgLoad(attr) if err == nil { return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil } logErr := err if opts.LogLevel == 0 && opts.LogSize >= 0 { // Re-run with the verifier enabled to get better error messages. logBuf = make([]byte, logSize) attr.LogLevel = 1 attr.LogSize = uint32(len(logBuf)) attr.LogBuf = internal.NewSlicePointer(logBuf) fd, logErr = internal.BPFProgLoad(attr) if logErr == nil { fd.Close() } } if errors.Is(logErr, unix.EPERM) && 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 bay be too low, consider rlimit.RemoveMemlock)", logErr) } err = internal.ErrorWithLog(err, logBuf, logErr) if btfDisabled { return nil, fmt.Errorf("load program without BTF: %w", err) } return nil, fmt.Errorf("load program: %w", err) } // 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) { if fd < 0 { return nil, errors.New("invalid fd") } return newProgramFromFD(internal.NewFD(uint32(fd))) } // 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 := internal.BPFObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, fmt.Errorf("get program by id: %w", err) } return newProgramFromFD(fd) } func newProgramFromFD(fd *internal.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.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) } // 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 { fd, err := p.fd.Value() if err != nil { // Best effort: -1 is the number most likely to be an // invalid file descriptor. return -1 } return int(fd) } // 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/k8s-doc/admin/#admin-mount-bpffs 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 unloads the program from the kernel. func (p *Program) Close() error { if p == nil { return nil } return p.fd.Close() } // 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) { ret, out, _, err := p.testRun(in, 1, nil) if err != nil { return ret, nil, fmt.Errorf("can't test program: %w", err) } return ret, out, 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. // // Note: profiling a call to this function will skew it's results, see // https://github.com/cilium/ebpf/issues/24 // // This function requires at least Linux 4.12. func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { ret, _, total, err := p.testRun(in, repeat, reset) if err != nil { return ret, total, fmt.Errorf("can't benchmark program: %w", err) } return ret, total, nil } var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() error { prog, err := NewProgram(&ProgramSpec{ 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() // Programs require at least 14 bytes input in := make([]byte, 14) attr := bpfProgTestRunAttr{ fd: uint32(prog.FD()), dataSizeIn: uint32(len(in)), dataIn: internal.NewSlicePointer(in), } err = bpfProgTestRun(&attr) if errors.Is(err, unix.EINVAL) { // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. return internal.ErrNotSupported } if errors.Is(err, unix.EINTR) { // We know that PROG_TEST_RUN is supported if we get EINTR. return nil } return err }) func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) { if uint(repeat) > math.MaxUint32 { return 0, nil, 0, fmt.Errorf("repeat is too high") } if len(in) == 0 { return 0, nil, 0, fmt.Errorf("missing input") } if uint(len(in)) > math.MaxUint32 { return 0, nil, 0, fmt.Errorf("input is too long") } if err := haveProgTestRun(); err != nil { return 0, nil, 0, err } // 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/ out := make([]byte, len(in)+outputPad) fd, err := p.fd.Value() if err != nil { return 0, nil, 0, err } attr := bpfProgTestRunAttr{ fd: fd, dataSizeIn: uint32(len(in)), dataSizeOut: uint32(len(out)), dataIn: internal.NewSlicePointer(in), dataOut: internal.NewSlicePointer(out), repeat: uint32(repeat), } for { err = bpfProgTestRun(&attr) if err == nil { break } if errors.Is(err, unix.EINTR) { if reset != nil { reset() } continue } return 0, nil, 0, fmt.Errorf("can't run test: %w", err) } if int(attr.dataSizeOut) > cap(out) { // 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") } out = out[:int(attr.dataSizeOut)] total := time.Duration(attr.duration) * time.Nanosecond return attr.retval, out, 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) } value, err := p.fd.Value() if err != nil { return nil, err } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, value) return buf, nil } // Attach a Program. // // Deprecated: use link.RawAttachProgram instead. func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { if fd < 0 { return errors.New("invalid fd") } pfd, err := p.fd.Value() if err != nil { return err } attr := internal.BPFProgAttachAttr{ TargetFd: uint32(fd), AttachBpfFd: pfd, AttachType: uint32(typ), AttachFlags: uint32(flags), } return internal.BPFProgAttach(&attr) } // Detach a Program. // // Deprecated: use link.RawDetachProgram instead. func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { if fd < 0 { return errors.New("invalid fd") } if flags != 0 { return errors.New("flags must be zero") } pfd, err := p.fd.Value() if err != nil { return err } attr := internal.BPFProgDetachAttr{ TargetFd: uint32(fd), AttachBpfFd: pfd, AttachType: uint32(typ), } return internal.BPFProgDetach(&attr) } // LoadPinnedProgram loads a Program from a BPF file. // // Requires at least Linux 4.11. func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) { fd, err := internal.BPFObjGet(fileName, 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) } return &Program{"", fd, filepath.Base(fileName), 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) { id, err := objGetNextID(internal.BPF_PROG_GET_NEXT_ID, uint32(startID)) return ProgramID(id), err } // ID returns the systemwide unique ID of the program. // // Deprecated: use ProgramInfo.ID() instead. func (p *Program) ID() (ProgramID, error) { info, err := bpfGetProgInfoByFD(p.fd, nil) if err != nil { return ProgramID(0), err } return ProgramID(info.id), nil } func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) { type match struct { p ProgramType a AttachType } var typeName, featureName string switch (match{progType, attachType}) { case match{LSM, AttachLSMMac}: typeName = "bpf_lsm_" + name featureName = name + " LSM hook" case match{Tracing, AttachTraceIter}: typeName = "bpf_iter_" + name featureName = name + " iterator" case match{Extension, AttachNone}: typeName = name featureName = fmt.Sprintf("freplace %s", name) default: return nil, nil } if spec == nil { var err error spec, err = btf.LoadKernelSpec() if err != nil { return nil, fmt.Errorf("load kernel spec: %w", err) } } var target *btf.Func err := spec.FindType(typeName, &target) if errors.Is(err, btf.ErrNotFound) { return nil, &internal.UnsupportedFeatureError{ Name: featureName, } } if err != nil { return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err) } return target, nil } golang-github-cilium-ebpf-0.7.0/prog_test.go000066400000000000000000000406021414524555700210120ustar00rootroot00000000000000package 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/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestProgramRun(t *testing.T) { pat := []byte{0xDE, 0xAD, 0xBE, 0xEF} buf := make([]byte, 14) // 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).Sym("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 TestProgramBenchmark(t *testing.T) { prog := createSocketFilter(t) defer prog.Close() ret, duration, err := prog.Benchmark(make([]byte, 14), 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 := createSocketFilter(t) defer prog.Close() 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. _, _, _, err := prog.testRun(make([]byte, 14), math.MaxInt32, 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() }) 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 := createSocketFilter(t) if err := prog.Close(); err != nil { t.Fatal("Can't close program:", err) } } func TestProgramPin(t *testing.T) { prog := createSocketFilter(t) c := qt.New(t) defer prog.Close() 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.Equals, true) 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 !prog.IsPinned() { t.Error("Expected IsPinned to be true") } } func TestProgramUnpin(t *testing.T) { prog := createSocketFilter(t) c := qt.New(t) defer prog.Close() 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.Equals, true) 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 := createSocketFilter(t) defer prog.Close() 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") } if !strings.Contains(err.Error(), "exit") { t.Error("No verifier output in error message") } } 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: 2, }) 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: 2, }) 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") } } func TestProgramWithUnsatisfiedReference(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) if !errors.Is(err, errUnsatisfiedReference) { t.Fatal("Expected an error wrapping errUnsatisfiedReference, got", err) } t.Log(err) } func TestProgramName(t *testing.T) { if err := haveObjName(); err != nil { t.Skip(err) } prog := createSocketFilter(t) defer prog.Close() info, err := bpfGetProgInfoByFD(prog.fd, nil) if err != nil { t.Fatal(err) } if name := internal.CString(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, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() 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) } if prog2 == nil { t.Fatal("Unmarshalling set program to nil") } } func TestProgramFromFD(t *testing.T) { prog, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() // If you're thinking about copying this, don't. Use // Clone() instead. prog2, err := NewProgramFromFD(prog.FD()) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } // Both programs refer to the same fd now. Closing either of them will // release the fd to the OS, which then might re-use that fd for another // test. Once we close the second map we might close the re-used fd // inadvertently, leading to spurious test failures. // To avoid this we have to "leak" one of the programs. prog2.fd.Forget() } func TestHaveProgTestRun(t *testing.T) { testutils.CheckFeatureTest(t, haveProgTestRun) } func TestProgramGetNextID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_next_id") var next ProgramID prog, err := NewProgram(&ProgramSpec{ Type: SkSKB, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() if next, err = ProgramGetNextID(ProgramID(0)); err != nil { t.Fatal("Can't get next ID:", err) } if next == ProgramID(0) { t.Fatal("Expected next ID other than 0") } // 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 for { last := next if next, err = ProgramGetNextID(last); err != nil { if !errors.Is(err, 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 TestNewProgramFromID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_fd_by_id") prog, err := NewProgram(&ProgramSpec{ Type: SkSKB, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { t.Fatal(err) } defer prog.Close() var next ProgramID next, err = prog.ID() if err != nil { t.Fatal("Could not get ID of program:", err) } if _, err = NewProgramFromID(next); err != nil { t.Fatalf("Can't get FD for program ID %d: %v", uint32(next), err) } // 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, 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 TestProgramTypeLSM(t *testing.T) { lsmTests := []struct { attachFn string flags uint32 expectedErr bool }{ { attachFn: "task_getpgid", }, { attachFn: "task_setnice", flags: unix.BPF_F_SLEEPABLE, expectedErr: true, }, { attachFn: "file_open", flags: unix.BPF_F_SLEEPABLE, }, } for _, tt := range lsmTests { t.Run(tt.attachFn, func(t *testing.T) { prog, err := NewProgram(&ProgramSpec{ AttachTo: tt.attachFn, AttachType: AttachLSMMac, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", Type: LSM, Flags: tt.flags, }) testutils.SkipIfNotSupported(t, err) if tt.flags&unix.BPF_F_SLEEPABLE != 0 { testutils.SkipOnOldKernel(t, "5.11", "BPF_F_SLEEPABLE for LSM progs") } if tt.expectedErr && err == nil { t.Errorf("Test case '%s': expected error", tt.attachFn) } if !tt.expectedErr && err != nil { t.Errorf("Test case '%s': expected success", tt.attachFn) } prog.Close() }) } } func TestProgramTargetBTF(t *testing.T) { // Load a file that contains valid BTF, but doesn't contain the types // we need for bpf_iter. fh, err := os.Open("testdata/invalid_btf_map_init-el.elf") if err != nil { t.Fatal(err) } defer fh.Close() reader := &testReaderAt{file: fh} 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{ TargetBTF: reader, }) if err == nil { prog.Close() } if !errors.Is(err, ErrNotSupported) { t.Error("Expected ErrNotSupported, got", err) } if !reader.read { t.Error("TargetBTF is not read") } } type testReaderAt struct { file *os.File read bool } func (ra *testReaderAt) ReadAt(p []byte, off int64) (int, error) { ra.read = true return ra.file.ReadAt(p, off) } 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 createSocketFilter(t *testing.T) *Program { t.Helper() prog, err := NewProgram(socketFilterSpec) if err != nil { t.Fatal(err) } return prog } // 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 ExampleNewProgramWithOptions() { spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } prog, err := NewProgramWithOptions(spec, ProgramOptions{ LogLevel: 2, 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) } } golang-github-cilium-ebpf-0.7.0/ringbuf/000077500000000000000000000000001414524555700201075ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/ringbuf/doc.go000066400000000000000000000004231414524555700212020ustar00rootroot00000000000000// 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.7.0/ringbuf/reader.go000066400000000000000000000132111414524555700216760ustar00rootroot00000000000000package ringbuf import ( "encoding/binary" "errors" "fmt" "io" "runtime" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( ErrClosed = errors.New("ringbuf reader was closed") errDiscard = errors.New("sample discarded") errBusy = errors.New("sample not committed yet") ) func addToEpoll(epollfd, fd int) error { event := unix.EpollEvent{ Events: unix.EPOLLIN, Fd: int32(fd), } if err := unix.EpollCtl(epollfd, unix.EPOLL_CTL_ADD, fd, &event); err != nil { return fmt.Errorf("can't add fd to epoll: %w", err) } return nil } // 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 } func readRecord(rd *ringbufEventRing) (r Record, err error) { rd.loadConsumer() var header ringbufHeader err = binary.Read(rd, internal.NativeEndian, &header) if err == io.EOF { return Record{}, err } if err != nil { return Record{}, fmt.Errorf("can't read event header: %w", err) } 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 Record{}, fmt.Errorf("%w", 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 Record{}, fmt.Errorf("%w", errDiscard) } data := make([]byte, dataLenAligned) if _, err := io.ReadFull(rd, data); err != nil { return Record{}, fmt.Errorf("can't read sample: %w", err) } rd.storeConsumer() return Record{RawSample: data[:header.dataLen()]}, nil } // Reader allows reading bpf_ringbuf_output // from user space. type Reader struct { // mu protects read/write access to the Reader structure mu sync.Mutex ring *ringbufEventRing epollFd int epollEvents []unix.EpollEvent closeFd int // Ensure we only close once closeOnce sync.Once } // NewReader creates a new BPF ringbuf reader. func NewReader(ringbufMap *ebpf.Map) (r *Reader, err 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) } epollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) if err != nil { return nil, fmt.Errorf("can't create epoll fd: %w", err) } var ( fds = []int{epollFd} ring *ringbufEventRing ) defer func() { if err != nil { // close epollFd and closeFd for _, fd := range fds { unix.Close(fd) } if ring != nil { ring.Close() } } }() ring, err = newRingBufEventRing(ringbufMap.FD(), maxEntries) if err != nil { return nil, fmt.Errorf("failed to create ringbuf ring: %w", err) } if err := addToEpoll(epollFd, ringbufMap.FD()); err != nil { return nil, err } closeFd, err := unix.Eventfd(0, unix.O_CLOEXEC|unix.O_NONBLOCK) if err != nil { return nil, err } fds = append(fds, closeFd) if err := addToEpoll(epollFd, closeFd); err != nil { return nil, err } r = &Reader{ ring: ring, epollFd: epollFd, // Allocate extra event for closeFd epollEvents: make([]unix.EpollEvent, 2), closeFd: closeFd, } runtime.SetFinalizer(r, (*Reader).Close) return r, nil } // Close frees resources used by the reader. // // It interrupts calls to Read. func (r *Reader) Close() error { var err error r.closeOnce.Do(func() { runtime.SetFinalizer(r, nil) // Interrupt Read() via the closeFd event fd. var value [8]byte internal.NativeEndian.PutUint64(value[:], 1) if _, err = unix.Write(r.closeFd, value[:]); err != nil { err = fmt.Errorf("can't write event fd: %w", err) return } // Acquire the lock. This ensures that Read isn't running. r.mu.Lock() defer r.mu.Unlock() unix.Close(r.epollFd) unix.Close(r.closeFd) r.epollFd, r.closeFd = -1, -1 if r.ring != nil { r.ring.Close() } r.ring = nil }) if err != nil { return fmt.Errorf("close Reader: %w", err) } return nil } // Read the next record from the BPF ringbuf. // // Calling Close interrupts the function. func (r *Reader) Read() (Record, error) { r.mu.Lock() defer r.mu.Unlock() if r.epollFd == -1 { return Record{}, fmt.Errorf("%w", ErrClosed) } for { nEvents, err := unix.EpollWait(r.epollFd, r.epollEvents, -1) if temp, ok := err.(temporaryError); ok && temp.Temporary() { // Retry the syscall if we we're interrupted, see https://github.com/golang/go/issues/20400 continue } if err != nil { return Record{}, err } for _, event := range r.epollEvents[:nEvents] { if int(event.Fd) == r.closeFd { return Record{}, fmt.Errorf("%w", ErrClosed) } } record, err := readRecord(r.ring) if errors.Is(err, errBusy) || errors.Is(err, errDiscard) { continue } return record, err } } type temporaryError interface { Temporary() bool } golang-github-cilium-ebpf-0.7.0/ringbuf/reader_test.go000066400000000000000000000124161414524555700227430ustar00rootroot00000000000000package ringbuf import ( "errors" "syscall" "testing" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" "github.com/google/go-cmp/cmp" ) 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(make([]byte, 14)) 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)).Sym("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 TestRingbufReaderClose(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() errs := make(chan error, 1) waiting := make(chan struct{}) go func() { close(waiting) _, err := rd.Read() errs <- err }() <-waiting // 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("Read from RingbufReader that got closed does return ErrClosed") } 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 BenchmarkReader(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF ring buffer") readerBenchmarks := []struct { name string flags int32 }{ { name: "normal epoll with timeout -1", }, } b.ResetTimer() b.ReportAllocs() 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() for i := 0; i < b.N; i++ { ret, _, err := prog.Benchmark(make([]byte, 14), 1, nil) 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) } } }) } } golang-github-cilium-ebpf-0.7.0/ringbuf/ring.go000066400000000000000000000045461414524555700214060ustar00rootroot00000000000000package 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.7.0/ringbuf/ring_test.go000066400000000000000000000024271414524555700224410ustar00rootroot00000000000000package 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.7.0/rlimit/000077500000000000000000000000001414524555700177535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/rlimit/rlimit.go000066400000000000000000000071031414524555700216030ustar00rootroot00000000000000// 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/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 { // Reduce the limit to zero and store the previous limit. This should always succeed. var oldLimit unix.Rlimit 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 := internal.BPFMapCreateAttr{ 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 := internal.BPFMapCreate(&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.7.0/rlimit/rlimit_test.go000066400000000000000000000020141414524555700226360ustar00rootroot00000000000000package 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.7.0/run-tests.sh000077500000000000000000000062771414524555700207720ustar00rootroot00000000000000#!/bin/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 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 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\" \"$script\" --exec-test $cmd" \ --kopt possible_cpus=2; then # need at least two CPUs for some tests exit 23 fi if [[ ! -e "${output}/success" ]]; then exit 42 fi $sudo rm -r "$output" exit 0 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 dmesg -C if ! "$@"; then dmesg exit 1 # this return code is "swallowed" by qemu fi touch "/run/output/success" exit 0 fi readonly kernel_version="${1:-}" if [[ -z "${kernel_version}" ]]; then echo "Expecting kernel version as first argument" exit 1 fi shift readonly kernel="linux-${kernel_version}.bz" readonly selftests="linux-${kernel_version}-selftests-bpf.bz" readonly input="$(mktemp -d)" readonly tmp_dir="${TMPDIR:-/tmp}" readonly branch="${BRANCH:-master}" fetch() { echo Fetching "${1}" wget -nv -N -P "${tmp_dir}" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}" } fetch "${kernel}" cp "${tmp_dir}/${kernel}" "${input}/bzImage" if fetch "${selftests}"; then mkdir "${input}/bpf" tar --strip-components=4 -xjf "${tmp_dir}/${selftests}" -C "${input}/bpf" else echo "No selftests found, disabling" fi 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_version}" go test -exec "$script --exec-vm $input" "${args[@]}" echo "Test successful on ${kernel_version}" rm -r "${input}" golang-github-cilium-ebpf-0.7.0/syscalls.go000066400000000000000000000267131414524555700206500ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "os" "unsafe" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) // ErrNotExist is returned when loading a non-existing map or program. // // Deprecated: use os.ErrNotExist instead. var ErrNotExist = os.ErrNotExist // 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 } } type bpfMapOpAttr struct { mapFd uint32 padding uint32 key internal.Pointer value internal.Pointer flags uint64 } type bpfBatchMapOpAttr struct { inBatch internal.Pointer outBatch internal.Pointer keys internal.Pointer values internal.Pointer count uint32 mapFd uint32 elemFlags uint64 flags uint64 } type bpfMapInfo struct { map_type uint32 // since 4.12 1e2709769086 id uint32 key_size uint32 value_size uint32 max_entries uint32 map_flags uint32 name internal.BPFObjName // since 4.15 ad5b177bd73f ifindex uint32 // since 4.16 52775b33bb50 btf_vmlinux_value_type_id uint32 // since 5.6 85d33df357b6 netns_dev uint64 // since 4.16 52775b33bb50 netns_ino uint64 btf_id uint32 // since 4.18 78958fca7ead btf_key_type_id uint32 // since 4.18 9b2cf328b2ec btf_value_type_id uint32 } type bpfProgInfo struct { prog_type uint32 id uint32 tag [unix.BPF_TAG_SIZE]byte jited_prog_len uint32 xlated_prog_len uint32 jited_prog_insns internal.Pointer xlated_prog_insns internal.Pointer load_time uint64 // since 4.15 cb4d2b3f03d8 created_by_uid uint32 nr_map_ids uint32 // since 4.15 cb4d2b3f03d8 map_ids internal.Pointer name internal.BPFObjName // since 4.15 067cae47771c ifindex uint32 gpl_compatible uint32 netns_dev uint64 netns_ino uint64 nr_jited_ksyms uint32 nr_jited_func_lens uint32 jited_ksyms internal.Pointer jited_func_lens internal.Pointer btf_id uint32 func_info_rec_size uint32 func_info internal.Pointer nr_func_info uint32 nr_line_info uint32 line_info internal.Pointer jited_line_info internal.Pointer nr_jited_line_info uint32 line_info_rec_size uint32 jited_line_info_rec_size uint32 nr_prog_tags uint32 prog_tags internal.Pointer run_time_ns uint64 run_cnt uint64 } type bpfProgTestRunAttr struct { fd uint32 retval uint32 dataSizeIn uint32 dataSizeOut uint32 dataIn internal.Pointer dataOut internal.Pointer repeat uint32 duration uint32 } type bpfMapFreezeAttr struct { mapFd uint32 } type bpfObjGetNextIDAttr struct { startID uint32 nextID uint32 openFlags uint32 } func bpfProgTestRun(attr *bpfProgTestRunAttr) error { _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error { _, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ MapType: uint32(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.FeatureTest("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 := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ MapType: uint32(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, Flags: unix.BPF_F_RDONLY_PROG, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) var haveMmapableMaps = internal.FeatureTest("mmapable maps", "5.5", func() error { // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ MapType: uint32(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, Flags: unix.BPF_F_MMAPABLE, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) var haveInnerMaps = internal.FeatureTest("inner maps", "5.10", func() error { // This checks BPF_F_INNER_MAP, which appeared in 5.10. m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ MapType: uint32(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, Flags: unix.BPF_F_INNER_MAP, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }) func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapOpAttr{ mapFd: fd, key: key, value: valueOut, } _, err = internal.BPF(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapOpAttr{ mapFd: fd, key: key, value: valueOut, } _, err = internal.BPF(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapOpAttr{ mapFd: fd, key: key, value: valueOut, flags: flags, } _, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapOpAttr{ mapFd: fd, key: key, } _, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapOpAttr{ mapFd: fd, key: key, value: nextKeyOut, } _, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return wrapMapError(err) } func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) { attr := bpfObjGetNextIDAttr{ startID: start, } _, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return attr.nextID, err } func bpfMapBatch(cmd internal.BPFCmd, m *internal.FD, inBatch, outBatch, keys, values internal.Pointer, count uint32, opts *BatchOptions) (uint32, error) { fd, err := m.Value() if err != nil { return 0, err } attr := bpfBatchMapOpAttr{ inBatch: inBatch, outBatch: outBatch, keys: keys, values: values, count: count, mapFd: fd, } if opts != nil { attr.elemFlags = opts.ElemFlags attr.flags = opts.Flags } _, err = internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) // always return count even on an error, as things like update might partially be fulfilled. return attr.count, wrapMapError(err) } func wrapMapError(err error) error { if err == nil { return nil } if errors.Is(err, unix.ENOENT) { return internal.SyscallError(ErrKeyNotExist, unix.ENOENT) } if errors.Is(err, unix.EEXIST) { return internal.SyscallError(ErrKeyExist, unix.EEXIST) } if errors.Is(err, unix.ENOTSUPP) { return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP) } if errors.Is(err, unix.E2BIG) { return fmt.Errorf("key too big for map: %w", err) } return err } func bpfMapFreeze(m *internal.FD) error { fd, err := m.Value() if err != nil { return err } attr := bpfMapFreezeAttr{ mapFd: fd, } _, err = internal.BPF(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } func bpfGetProgInfoByFD(fd *internal.FD, ids []MapID) (*bpfProgInfo, error) { var info bpfProgInfo if len(ids) > 0 { info.nr_map_ids = uint32(len(ids)) info.map_ids = internal.NewPointer(unsafe.Pointer(&ids[0])) } if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { return nil, fmt.Errorf("can't get program info: %w", err) } return &info, nil } func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) { var info bpfMapInfo err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) if err != nil { return nil, fmt.Errorf("can't get map info: %w", err) } return &info, nil } var haveObjName = internal.FeatureTest("object names", "4.15", func() error { attr := internal.BPFMapCreateAttr{ MapType: uint32(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: internal.NewBPFObjName("feature_test"), } fd, err := internal.BPFMapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() error { if err := haveObjName(); err != nil { return err } attr := internal.BPFMapCreateAttr{ MapType: uint32(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: internal.NewBPFObjName(".test"), } fd, err := internal.BPFMapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error { var maxEntries uint32 = 2 attr := internal.BPFMapCreateAttr{ MapType: uint32(Hash), KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, } fd, err := internal.BPFMapCreate(&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) nilPtr := internal.NewPointer(nil) _, err = bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, fd, nilPtr, nilPtr, kp, vp, maxEntries, nil) if err != nil { return internal.ErrNotSupported } return nil }) var haveProbeReadKernel = internal.FeatureTest("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(), } buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize)) if err := insns.Marshal(buf, internal.NativeEndian); err != nil { return err } bytecode := buf.Bytes() fd, err := internal.BPFProgLoad(&internal.BPFProgLoadAttr{ ProgType: uint32(Kprobe), License: internal.NewStringPointer("GPL"), Instructions: internal.NewSlicePointer(bytecode), InsCount: uint32(len(bytecode) / asm.InstructionSize), }) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }) golang-github-cilium-ebpf-0.7.0/syscalls_test.go000066400000000000000000000023011414524555700216720ustar00rootroot00000000000000package 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) } golang-github-cilium-ebpf-0.7.0/testdata/000077500000000000000000000000001414524555700202645ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/testdata/btf_map_init-eb.elf000066400000000000000000000135701414524555700240010ustar00rootroot00000000000000ELF@@* 0 Q%U4I?: ;  : ;  I: ; 8 II!I7 $> $ > !I I  I: ; 4I: ; I'I.@B: ; I?.@B: ; 'I?: ; I! Skm?  }t y   0  *}"+,-.3/ ' > C "}#x$%&} C' "Z 0Z0Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/btf_map_init.c.prog_array_inittypeint__ARRAY_SIZE_TYPE__key_sizemax_entriesvaluesouter_map_initvalue_sizekeyunsigned intuint32_tvalueinner_maptail_calllong inttail_1tail_mainctx    @'3 : JS  '@`dj  '@t3     ( 0int__ARRAY_SIZE_TYPE__typekey_sizemax_entriesvaluesprog_array_inituint32_tunsigned intkeyvalueinner_mapvalue_sizeouter_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 p x | 0n; testdatabtf_map_init.ccommon.h    L  h( 2H0"(0  p      & + 7 D P \ h          H T ` l               ,<Phx , 0H]  .debug_abbrev.text.rel.BTF.extprog_array_initouter_map_init.rel.maps.rel.debug_ranges.debug_strinner_map.rel.debug_infotail_main.relsocket/mainsocket/tail.llvm_addrsig.rel.debug_line.rel.debug_frame.rel.debug_locbtf_map_init.c.strtab.symtab.rel.BTFtail_1@@P0 (ExA 8 3 X+v%r hP O?0K @ ]0o"  0  (P @ x@ Hr  oL h golang-github-cilium-ebpf-0.7.0/testdata/btf_map_init-el.elf000066400000000000000000000135701414524555700240130ustar00rootroot00000000000000ELF@@* Q%U4I?: ;  : ;  I: ; 8 II!I7 $> $ > !I I  I: ; 4I: ; I'I.@B: ; I?.@B: ; 'I?: ; I! Skm?  }t y   0  *}"+,-.3/ ' > C "}#x$%&} C' "Z 0Z0Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/btf_map_init.c.prog_array_inittypeint__ARRAY_SIZE_TYPE__key_sizemax_entriesvaluesouter_map_initvalue_sizekeyunsigned intuint32_tvalueinner_maptail_calllong inttail_1tail_mainctx     @'3 : JS  '@`dj  '@t3     ( 0int__ARRAY_SIZE_TYPE__typekey_sizemax_entriesvaluesprog_array_inituint32_tunsigned intkeyvalueinner_mapvalue_sizeouter_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 p x | 0n; testdatabtf_map_init.ccommon.h    L  h( 2H0"(0  p      & + 7 D P \ h          H T ` l               ,<Phx , 0H]  .debug_abbrev.text.rel.BTF.extprog_array_initouter_map_init.rel.maps.rel.debug_ranges.debug_strinner_map.rel.debug_infotail_main.relsocket/mainsocket/tail.llvm_addrsig.rel.debug_line.rel.debug_frame.rel.debug_locbtf_map_init.c.strtab.symtab.rel.BTFtail_1@@P0 ( ExA 8 3 X +v%r h P O?0K @ ]0o"  0  (P @ x@H r  Lo h golang-github-cilium-ebpf-0.7.0/testdata/btf_map_init.c000066400000000000000000000025651414524555700230730ustar00rootroot00000000000000/* 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); __uint(key_size, sizeof(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); __uint(key_size, sizeof(uint32_t)); __uint(value_size, sizeof(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.7.0/testdata/common.h000066400000000000000000000022511414524555700217250ustar00rootroot00000000000000#pragma once typedef unsigned int uint32_t; typedef unsigned long uint64_t; #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 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 (*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; golang-github-cilium-ebpf-0.7.0/testdata/docker/000077500000000000000000000000001414524555700215335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/testdata/docker/Dockerfile000066400000000000000000000012601414524555700235240ustar00rootroot00000000000000# This Dockerfile generates a build environment for generating ELFs # of testdata programs. Run `make build` in this directory to build it. FROM debian: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 \ clang-7 llvm-7 \ clang-9 llvm-9 \ clang-12 llvm-12 && \ rm -rf /var/lib/apt/lists/* golang-github-cilium-ebpf-0.7.0/testdata/docker/IMAGE000066400000000000000000000000341414524555700222750ustar00rootroot00000000000000quay.io/cilium/ebpf-builder golang-github-cilium-ebpf-0.7.0/testdata/docker/Makefile000066400000000000000000000005501414524555700231730ustar00rootroot00000000000000# Makefile to build and push the `cilium/ebpf` llvm builder Docker image. IMAGE := $(shell cat IMAGE) EPOCH := $(shell date +'%s') ifndef IMAGE $(error IMAGE file not present in Makefile directory) endif .PHONY: build push build: docker build --no-cache . -t "$(IMAGE):$(EPOCH)" echo $(EPOCH) > VERSION push: docker push "$(IMAGE):$(shell cat VERSION)" golang-github-cilium-ebpf-0.7.0/testdata/docker/README.md000066400000000000000000000022271414524555700230150ustar00rootroot00000000000000# `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.7.0/testdata/docker/VERSION000066400000000000000000000000131414524555700225750ustar00rootroot000000000000001619686610 golang-github-cilium-ebpf-0.7.0/testdata/docker/llvm-snapshot.gpg.key000066400000000000000000000061111414524555700256270ustar00rootroot00000000000000-----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.7.0/testdata/docker/llvm.list000066400000000000000000000012361414524555700234040ustar00rootroot00000000000000# 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-11 main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main deb http://apt.llvm.org/buster/ llvm-toolchain-buster-12 main deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-12 main golang-github-cilium-ebpf-0.7.0/testdata/freplace-eb.elf000066400000000000000000000114001414524555700231150ustar00rootroot00000000000000ELF @@@ca MIT%U4I?: ; I!I7 $> $ > .@B: ; I?4: ; I .@B: ; 'I? : ; I 1 .@B: ; I? 5II : ;  I: ; 8 I: ;   Sgi? KRsx Z  Z  Y Z R  Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/freplace.c.__licensechar__ARRAY_SIZE_TYPE__subprogintsched_process_execreplacementretctxargslong unsigned intuint64_tbpf_args?   w@      ( -7intsubprog.text./testdata/freplace.c__attribute__((noinline)) int subprog() { volatile int ret = 0; return ret;bpf_argsargsuint64_tlong unsigned int__ARRAY_SIZE_TYPE__ctxsched_process_execraw_tracepoint/sched_process_exec return subprog();replacementfreplace/subprog return 0;char__licenselicense 44|     ),S0j4D D T |  7 testdatafreplace.ccommon.h   !!       z"d      & + 7 L S Zh v           (( ,<L`p , 0D HD^y .debug_abbrev.text.rel.BTF.extreplacement.rel.debug_ranges.debug_str.rel.debug_infofreplace/subprog.llvm_addrsig__license.rel.debug_line.rel.debug_frame.relraw_tracepoint/sched_process_execfreplace.c.strtab.symtab.rel.BTF K@ ` ([p|OwK 82@. ` @0o   (X `H 0loL HP golang-github-cilium-ebpf-0.7.0/testdata/freplace-el.elf000066400000000000000000000114001414524555700231270ustar00rootroot00000000000000ELF@ @@caMIT%U4I?: ; I!I7 $> $ > .@B: ; I?4: ; I .@B: ; 'I? : ; I 1 .@B: ; I? 5II : ;  I: ; 8 I: ;  Sgi? KRsx Z  Z  Y Z R  Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/freplace.c.__licensechar__ARRAY_SIZE_TYPE__subprogintsched_process_execreplacementretctxargslong unsigned intuint64_tbpf_args?   w@     ( -7intsubprog.text./testdata/freplace.c__attribute__((noinline)) int subprog() { volatile int ret = 0; return ret;bpf_argsargsuint64_tlong unsigned int__ARRAY_SIZE_TYPE__ctxsched_process_execraw_tracepoint/sched_process_exec return subprog();replacementfreplace/subprog return 0;char__licenselicense 44|    ),S0j4 DD T |  7 testdatafreplace.ccommon.h   !!       z"d      & + 7 L S Zh v           (( ,<L`p , 0D HD^y .debug_abbrev.text.rel.BTF.extreplacement.rel.debug_ranges.debug_str.rel.debug_infofreplace/subprog.llvm_addrsig__license.rel.debug_line.rel.debug_frame.relraw_tracepoint/sched_process_execfreplace.c.strtab.symtab.rel.BTFK @ ` ( [p|OwK 8 2@. ` @0o    ( X `H  0lLoH P golang-github-cilium-ebpf-0.7.0/testdata/freplace.c000066400000000000000000000006331414524555700222130ustar00rootroot00000000000000// /* 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.7.0/testdata/invalid_btf_map_init-eb.elf000066400000000000000000000047001414524555700255020ustar00rootroot00000000000000ELF@@ %4I?: ;  : ;  I: ; 8 II!I7 $> $ > I: ;  Ssu3  ~h h my   Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_btf_map_init.c.hash_maptypeint__ARRAY_SIZE_TYPE__keyunsigned intuint32_tvaluelong unsigned intuint64_tmax_entriest  "  / 8@ JO@SYe n int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inttypekeyvaluemax_entrieshash_map.mapsIC testdatainvalid_btf_map_init.ccommon.hY &       +8 D P \ z      .debug_abbrev.text.maps.debug_strhash_map.rel.debug_info.llvm_addrsig.debug_lineinvalid_btf_map_init.c.strtab.symtab.rel.BTFp@@ `u3/  0m  MM?oL x@golang-github-cilium-ebpf-0.7.0/testdata/invalid_btf_map_init-el.elf000066400000000000000000000047001414524555700255140ustar00rootroot00000000000000ELF@@ %4I?: ;  : ;  I: ; 8 II!I7 $> $ > I: ;  Ssu3  ~h h my   Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_btf_map_init.c.hash_maptypeint__ARRAY_SIZE_TYPE__keyunsigned intuint32_tvaluelong unsigned intuint64_tmax_entriest  "  / 8@ JO@SYe n int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inttypekeyvaluemax_entrieshash_map.mapsIC testdatainvalid_btf_map_init.ccommon.hY &       +8 D P \ z      .debug_abbrev.text.maps.debug_strhash_map.rel.debug_info.llvm_addrsig.debug_lineinvalid_btf_map_init.c.strtab.symtab.rel.BTFp@@ `u3/  0m  MM?Lo x@golang-github-cilium-ebpf-0.7.0/testdata/invalid_btf_map_init.c000066400000000000000000000006431414524555700245740ustar00rootroot00000000000000/* 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.7.0/testdata/invalid_map-eb.elf000066400000000000000000000053201414524555700236230ustar00rootroot00000000000000ELFP@@MIT%4I?: ; I!I7 $> $ >  : ;  I: ; 8  : ; I: ;  Sjl3 ?Fv{b      Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_map.c.__licensechar__ARRAY_SIZE_TYPE__invalid_mapdeftypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defdummyuint32_t $(.:? H@S`_i v char__ARRAY_SIZE_TYPE____licensedefdummybpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intuint32_tinvalid_maplicensemaps@: testdatainvalid_map.ccommon.he O%      +@ G N Zg s          .debug_abbrev.textmaps.debug_strinvalid_map.rel.debug_info.llvm_addrsig__license.debug_lineinvalid_map.c.strtab.symtab.rel.BTFs@Q@D\{51 PP 0  YdDAoL {golang-github-cilium-ebpf-0.7.0/testdata/invalid_map-el.elf000066400000000000000000000053201414524555700236350ustar00rootroot00000000000000ELFP@@MIT%4I?: ; I!I7 $> $ >  : ;  I: ; 8  : ; I: ;  Sjl3 ?Fv{b      Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_map.c.__licensechar__ARRAY_SIZE_TYPE__invalid_mapdeftypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defdummyuint32_t $(.:? H@S`_i v char__ARRAY_SIZE_TYPE____licensedefdummybpf_map_deftypekey_sizevalue_sizemax_entriesmap_flagsunsigned intuint32_tinvalid_maplicensemaps@: testdatainvalid_map.ccommon.he O%      +@ G N Zg s          .debug_abbrev.textmaps.debug_strinvalid_map.rel.debug_info.llvm_addrsig__license.debug_lineinvalid_map.c.strtab.symtab.rel.BTFs@Q@D\{51 PP 0  YdDALo {golang-github-cilium-ebpf-0.7.0/testdata/invalid_map.c000066400000000000000000000005551414524555700227200ustar00rootroot00000000000000/* 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.7.0/testdata/invalid_map_static-eb.elf000066400000000000000000000103201414524555700251660ustar00rootroot00000000000000ELF @@c* U0`z@`Q%4I?: ;  : ;  I: ; 8 $> 4I: ; 4I: ; I I' I  & .@B: ; I?4: ; II: ;  Sq`s? y ~?   `Z8Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_map_static.c.dummytypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defhash_mapmap_lookup_elemxdp_progintkeyuint32_tp    @` 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 | `xA testdatacommon.hinvalid_map_static.c  !i .hJ  .X@ Y`      + 7 @ H T ` l x            ,@P`p P dummy.debug_abbrev.text.rel.BTF.extmaps.debug_str.relxdphash_map.rel.debug_infoxdp_prog.llvm_addrsig.rel.debug_line.rel.debug_frame.debug_locinvalid_map_static.c.strtab.symtab.rel.BTFLBB0_2 @<@`8 H(([#MI X-0   p h( t|p boL 8 golang-github-cilium-ebpf-0.7.0/testdata/invalid_map_static-el.elf000066400000000000000000000103201414524555700252000ustar00rootroot00000000000000ELF @@cU0`z@`Q%4I?: ;  : ;  I: ; 8 $> 4I: ; 4I: ; I I' I  & .@B: ; I?4: ; II: ;  Sq`s? y ~?   `Z8Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/invalid_map_static.c.dummytypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defhash_mapmap_lookup_elemxdp_progintkeyuint32_tp    @` 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 | `xA testdatacommon.hinvalid_map_static.c  !i .hJ  .X@ Y`      + 7 @ H T ` l x             ,@P`p P dummy.debug_abbrev.text.rel.BTF.extmaps.debug_str.relxdphash_map.rel.debug_infoxdp_prog.llvm_addrsig.rel.debug_line.rel.debug_frame.debug_locinvalid_map_static.c.strtab.symtab.rel.BTFLBB0_2 @<@`8 H(([#MI X-0    p h( t|p bLo 8 golang-github-cilium-ebpf-0.7.0/testdata/invalid_map_static.c000066400000000000000000000012411414524555700242600ustar00rootroot00000000000000/* 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.7.0/testdata/iproute2_map_compat-eb.elf000066400000000000000000000044701414524555700253160ustar00rootroot00000000000000ELF@@ %4I?: ;  : ;  I: ; 8 $>  Srt3 $ }    Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/iproute2_map_compat.c.hash_maptypeunsigned intsize_keysize_valuemax_elemflagsidpinninginner_idinner_idxbpf_elf_mapn $  @&`/58@IS `i$bpf_elf_maptypesize_keysize_valuemax_elemflagsidpinninginner_idinner_idxunsigned inthash_mapmaps<6 testdataiproute2_map_compat.cX %$      +4 < H T ` l x     .debug_abbrev.textmaps.debug_strhash_map.rel.debug_info.llvm_addrsig.debug_lineiproute2_map_compat.c.strtab.symtab.rel.BTFnq@@$dH2. @ 0\86~ ` Ln@>oLp vgolang-github-cilium-ebpf-0.7.0/testdata/iproute2_map_compat-el.elf000066400000000000000000000044701414524555700253300ustar00rootroot00000000000000ELF@@ %4I?: ;  : ;  I: ; 8 $>  Srt3 $ }    Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/iproute2_map_compat.c.hash_maptypeunsigned intsize_keysize_valuemax_elemflagsidpinninginner_idinner_idxbpf_elf_mapn $  @&`/58@IS `i$bpf_elf_maptypesize_keysize_valuemax_elemflagsidpinninginner_idinner_idxunsigned inthash_mapmaps<6 testdataiproute2_map_compat.cX %$      +4 < H T ` l x     .debug_abbrev.textmaps.debug_strhash_map.rel.debug_info.llvm_addrsig.debug_lineiproute2_map_compat.c.strtab.symtab.rel.BTFnq@@$dH2. @  0\86~ ` Ln@>Lop vgolang-github-cilium-ebpf-0.7.0/testdata/iproute2_map_compat.c000066400000000000000000000014131414524555700244000ustar00rootroot00000000000000/* 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.7.0/testdata/loader-clang-12-eb.elf000066400000000000000000000373401414524555700241170ustar00rootroot00000000000000ELF5@@%ap   pa`aa ]a ]a ]a MIT QPQPQP Q xVP%U4I?: ; I!I7 $> $ >  : ;  I: ; 8 &I 5I  : ; I I: ; !I!I74I: ; 4I: ; I'I&.@B: ; 'I: ; I.@B: ; 'I?1.@B: ; I?.@B: ; I?4: ; I Seg? KRqvnX  q       )  ( j {   jj o R    $-  j  R  R  R74  (j{ j?j G/  *+,j-U.  R R \ "j#{$ %j&  h3<  234j5U6 6j6j6{6 {6R g{A  >?@  Rr     011 6o p   Z^ ^Zb FbZf f`Zj j(@`Zu Z Z  Z xDebian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__array_of_hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defkey3unegneginthash_mapkeyuint32_tvaluelong unsigned intuint64_thash_map2btf_pinpinningbtf_outer_mapvaluesinner_map_tbtf_outer_map_anonperf_event_arrayargmap_lookup_elemkey1key2static_unegstatic_negstatic_fnglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsmy_const#  "  / 8@(JO@SYeo  Jx @Y(JO@SY  JO@SYJx @Y  JY@OSJx @Y!"%J Y$@&  ( x *  , A .  0 { 2  4  689Jx @Y`e; > =>> C B= G F=F@A ( (#'?DEHIJ:<int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inttypekeyvaluemax_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);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)}char__licensebpf_map_defarray_of_hash_mapkey3key1key2unegnegstatic_unegstatic_neg.bss.data.maps.rodatalicensemaps ||)+/-1357xg| g|  K  0HP,`*h0 0(P%xRR RRR(, P3T03T@R`hR`xblblx | `B5 testdataloader.ccommon.h  !   ! ' !.<, *.   !   YYY .<.     /  !X~  X~  X#~f.q %  !0  pH(I `(( ,   8" X# $(%8P%`&x ! *@'x N         & + 7L S Z fo w       & * '  !$. : F R ^     %      ,9 E Q ] i v            +8 D P \ l x    )    8 DM Yb nw                 $ 3 @ M W e t            ( 0 8 @ H P X `hpx<Tl$x%)&  * '   ,, < D T d t     0 @ P ` p           0 H `p  , 0 D H \ ` t x    B ^     #+( &$%*')perf_event_array.debug_abbrev.rel.text.rel.BTF.extsocket.bss.mapsdata_sections.rel.debug_ranges.debug_strother.relxdpbtf_outer_maparray_of_hash_map.rel.debug_infobtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__license.rel.debug_line.rel.debug_frame.rel.debug_locstaticloader.c.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5key3global_fn3.relsocket/3key2hash_map2global_fn2.relsocket/2key1z3$@x $0$jt~z $$7 %p$  %@$ 2DC>_K[ %P$eT &@$[ :W -P$i0 ( W .P$.I(* /P$NxJ 2 $>HF: 3 p$! oL3$  golang-github-cilium-ebpf-0.7.0/testdata/loader-clang-12-el.elf000066400000000000000000000373401414524555700241310ustar00rootroot00000000000000ELF5@@%axaaqaq`a]!a]!a]!a!MIT QPQPQP Q xVP%U4I?: ; I!I7 $> $ >  : ;  I: ; 8 &I 5I  : ; I I: ; !I!I74I: ; 4I: ; I'I&.@B: ; 'I: ; I.@B: ; 'I?1.@B: ; I?.@B: ; I?4: ; I Seg? KRqvnX  q      )  ( j {   jj o R    $-  j  R  R  R74  (j{ j?j G/  *+,j-U.  R R \ "j#{$ %j& h3<  234j5U6 6j6j6{6 {6R g{A  >?@  Rr    011 6o p   Z^ ^Zb FbZf f`Zj j(@`Zu Z Z  Z xDebian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__array_of_hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defkey3unegneginthash_mapkeyuint32_tvaluelong unsigned intuint64_thash_map2btf_pinpinningbtf_outer_mapvaluesinner_map_tbtf_outer_map_anonperf_event_arrayargmap_lookup_elemkey1key2static_unegstatic_negstatic_fnglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsmy_const#  "  / 8@(JO@SYeo  Jx @Y(JO@SY  JO@SYJx @Y  JY@OSJx @Y!"%J Y$@&  ( x *  , A .  0 { 2  4  689Jx @Y`e; > =>> C B= G F=F@A ( (#'?DEHIJ:<int__ARRAY_SIZE_TYPE__uint32_tunsigned intuint64_tlong unsigned inttypekeyvaluemax_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);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)}char__licensebpf_map_defarray_of_hash_mapkey3key1key2unegnegstatic_unegstatic_neg.bss.data.maps.rodatalicensemaps ||)+/-1357xg |g|  K  0HP,`*h0 0(P%xRR RRR(, P3T03T@R`hR`xblblx | `B5 testdataloader.ccommon.h  !   ! ' !.<, *.   !   YYY .<.     /  !X~  X~  X#~f.q %  !0  pH(I `(( ,   8 "X #$(%8P%`&x !*@'x N        & + 7L S Z fo w       & * '  !$. : F R ^     %      ,9 E Q ] i v            +8 D P \ l x    )    8 DM Yb nw                 $ 3 @ M W e t             ( 0 8 @ H P X `hpx<Tl$x%) &  * '  ,, < D T d t     0 @ P ` p           0 H `p  , 0 D H \ ` t x   B ^     #+( &$%*')perf_event_array.debug_abbrev.rel.text.rel.BTF.extsocket.bss.mapsdata_sections.rel.debug_ranges.debug_strother.relxdpbtf_outer_maparray_of_hash_map.rel.debug_infobtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__license.rel.debug_line.rel.debug_frame.rel.debug_locstaticloader.c.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5key3global_fn3.relsocket/3key2hash_map2global_fn2.relsocket/2key1z3$@x $0$jt~z $$7 p%$  %@$ 2DC>_K[ %P$eT &@$[: W P-$i0 (W  P.$.I(* P/$NxJ 2$>HF: 3p$! Lo3$ golang-github-cilium-ebpf-0.7.0/testdata/loader-clang-7-eb.elf000066400000000000000000000223601414524555700240370ustar00rootroot00000000000000ELF@@!apccc* * * `MIT clang version 7.1.0-svn353565-1~exp1~20190407073854.4 (branches/release_70)testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defhash_map2perf_event_arrayarray_of_hash_mapmap_lookup_elemstatic_fnintglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsarguint32_tkey1key2key3my_constQPQPQP Q xV(0P%BU4I?: ; I!I7 $> $ >  : ;  I: ; 8 4I: ; I I' I &.@: ; 'I: ; I.@: ; 'I?.@: ; I?4: ; I4: ; I.@: ; I?I: ;  ? KRnC  nK nR nX       Z^r^yZbr6byZfrlfy`ZjrjyZurwxy"zyZrZrGrZrx*__licenseYhash_mapxdp_progglobal_fnYdata_sectionsperf_event_arrayno_relocation0asm_relocationhash_map2array_of_hash_mapIglobal_fn2rglobal_fn3map_lookup_elem static_fnMnbpf_map_defunsigned intyuint32_trintKchar | `25 testdataloader.ccommon.h  !   ! ' !.<, *.   !   "//  %E&zJ' =<.     /  L^`jo*5?HVesw  a  < 6 j ~  ( $ X7 @8h99$ 5 0$$$$6#>#I#Q#l%t%%%####&&&&"&*&G(O(+   /  &- + 71L S Z f8o w  9 ; 2 !$/ :* > J#X c* g s% *  # *  &  *  *  *   '& 1(? J* N! Z)h s ~ $$## #(#0%8%@&H&P'X'`(h(p)x), , . $,. 0#D. H%\. `#t. x&. '. (. )B$^#%&'(!)"7<:34189;2perf_event_array.debug_abbrev.rel.textsocketmapsdata_sections.rel.debug_pubtypes.rel.debug_pubnames.rel.debug_ranges.debug_strother.relxdparray_of_hash_map.debug_macinfo.rel.debug_infono_relocationasm_relocationglobal_fnstatic_fnxdp_prog.llvm_addrsig__license.rel.debug_line.rel.debug_frame.rel.debug_locstatic.strtab.symtabMY_CONSTglobal_fn3socket/3hash_map2global_fn2.relsocket/2Q $@x  J P *  u1P~0T?j;   V(~  p l p  \ X p H QD  . *   6 p oL Y 0golang-github-cilium-ebpf-0.7.0/testdata/loader-clang-7-el.elf000066400000000000000000000223601414524555700240510ustar00rootroot00000000000000ELF@@!axaccc`MIT clang version 7.1.0-svn353565-1~exp1~20190407073854.4 (branches/release_70)testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defhash_map2perf_event_arrayarray_of_hash_mapmap_lookup_elemstatic_fnintglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsarguint32_tkey1key2key3my_constQPQPQP Q xV(0P%BU4I?: ; I!I7 $> $ >  : ;  I: ; 8 4I: ; I I' I &.@: ; 'I: ; I.@: ; 'I?.@: ; I?4: ; I4: ; I.@: ; I?I: ;  ? KRnC  nK nR nX       Z^r^yZbr6byZfrlfy`ZjrjyZurwxy"zyZrZrGrZrx*__licenseYhash_mapxdp_progglobal_fnYdata_sectionsperf_event_arrayno_relocation0asm_relocationhash_map2array_of_hash_mapIglobal_fn2rglobal_fn3map_lookup_elem static_fnMnbpf_map_defunsigned intyuint32_trintKchar | `25 testdataloader.ccommon.h  !   ! ' !.<, *.   !   "//  %E&zJ' =<.     /  L^`jo*5?HVesw  a  < 6 j ~  ( $X 7@8h99 $ 50$$$$6#>#I#Q#l%t%%%####&&&&"&*&G(O( +   / & -+ 71L S Z f8o w  9 ; 2 !$/ : *> J#X c *g s%  * #  * &   *  *  *  '& 1(? J *N !Z)h s ~ $$## #(#0%8%@&H&P'X'`(h(p)x) , , .$, .0#D .H%\ .`#t .x& .' .( .)B$^#%&'(!)"7<:34189;2perf_event_array.debug_abbrev.rel.textsocketmapsdata_sections.rel.debug_pubtypes.rel.debug_pubnames.rel.debug_ranges.debug_strother.relxdparray_of_hash_map.debug_macinfo.rel.debug_infono_relocationasm_relocationglobal_fnstatic_fnxdp_prog.llvm_addrsig__license.rel.debug_line.rel.debug_frame.rel.debug_locstatic.strtab.symtabMY_CONSTglobal_fn3socket/3hash_map2global_fn2.relsocket/2Q $@x  J P *  u1P~0T?j;  V(~  p l p  \ X p H QD  . *   6 p Lo Y 0golang-github-cilium-ebpf-0.7.0/testdata/loader-clang-9-eb.elf000066400000000000000000000412601414524555700240410ustar00rootroot00000000000000ELF90@@&ap   pa`aa ]a ]a ]a MIT clang version 9.0.1-+20210312025454+c1a0a213378a-1~exp1~20210312140025.114 testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__array_of_hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defkey3unegneginthash_mapkeyuint32_tvaluelong unsigned intuint64_thash_map2btf_pinpinningbtf_outer_mapvaluesinner_map_tbtf_outer_map_anonperf_event_arrayargmap_lookup_elemkey1key2static_unegstatic_negstatic_fnglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsmy_constQPQPQP Q xVP%U4I?: ; I!I7 $> $ >  : ;  I: ; 8 &I 5I  : ; I I: ; !I!I74I: ; 4I: ; I'I&.@: ; 'I: ; I.@: ; 'I?.@: ; I?.@: ; I?4: ; I ? KRnX  q       )  ( j {  jj o R      j  R  R  R4  (j{jj /  *+,j-.  R R  "j#{$%j&  3<  234j56 6j6j6{6{6R gA  >?@  Rr     011 6o p   Z^ ^Zb 6bZf lf`Zj jZu Z Z  Z x(@ & * >G  T ]@o x@ (@ x@  @ x@"! @  %@&$  ( x *  , A .  0 { 2  4  689x @`; > >> B A E =D?@ ( (#'CFG:<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);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)}char__licensebpf_map_defarray_of_hash_mapkey1key2static_unegstatic_neg.bss.data.maps.rodatalicensemaps(||)+/-1357xg| g|  K  0HP,`*h0 0(P%xRR RRR(, P3T03T@R`hR`xblblx | `B5 testdataloader.ccommon.h  !   ! ' !.<, *.   !   YYY .<.     /  !X~  X~  X#~f.L^`jo &08@NUat   4(   "?  pH(I `(( ; / 9 XQ R@(S8APS`Tx?9 O GX@Ux??999968>8I8Q8l:t:::8888==C   F  &D + 7HL S Z fIo w   T X U  !R. : F R ^     S      ,M9 E Q ] i v K          +L8 D P \ l x   ! W  " ?# 8$ D@M% YAb& n?w' ?9( B " 8) B " :* B " 8+  B $" 0;>, I<W- b=p. {B 0 >/ 9988 8(80:8:@;H;P<X<`=h=p>x> @$A<RHSTM`KlLxW? ? ? HI49D8L8\:l;|<=>9998888(888H8X8h8x8:::;;;;;;(;8;P<h=x=>>>>>>>>> >E 9,E 08DE H:\E `8tE x;E <E =E >B9^8:;<= >5QYVJNHITR3S42XU76MKLWperf_event_array.debug_abbrev.rel.text.rel.BTF.extsocket.bss.mapsdata_sections.rel.debug_ranges.debug_strother.relxdpbtf_outer_maparray_of_hash_map.debug_macinfo.rel.debug_infobtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__license.rel.debug_line.rel.debug_frame.rel.debug_locstaticloader.c.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5key3global_fn3.relsocket/3key2hash_map2global_fn2.relsocket/2key17& $@x ' %yt~z (%7 (%  (@% ADC>i0!nj ( %N *%[ W 1%BC  2%.80* 2%]hY 5% M8FI 6p%"oL7%pGgolang-github-cilium-ebpf-0.7.0/testdata/loader-clang-9-el.elf000066400000000000000000000412601414524555700240530ustar00rootroot00000000000000ELF09@@&axaaqaq`a]!a]!a]!a!MIT clang version 9.0.1-+20210312025454+c1a0a213378a-1~exp1~20210312140025.114 testdata/loader.c.__licensechar__ARRAY_SIZE_TYPE__array_of_hash_maptypeunsigned intkey_sizevalue_sizemax_entriesmap_flagsbpf_map_defkey3unegneginthash_mapkeyuint32_tvaluelong unsigned intuint64_thash_map2btf_pinpinningbtf_outer_mapvaluesinner_map_tbtf_outer_map_anonperf_event_arrayargmap_lookup_elemkey1key2static_unegstatic_negstatic_fnglobal_fn2global_fn3global_fnxdp_progno_relocationasm_relocationdata_sectionsmy_constQPQPQP Q xVP%U4I?: ; I!I7 $> $ >  : ;  I: ; 8 &I 5I  : ; I I: ; !I!I74I: ; 4I: ; I'I&.@: ; 'I: ; I.@: ; 'I?.@: ; I?.@: ; I?4: ; I ? KRnX  q      )  ( j {  jj o R      j  R  R  R4  (j{jj /  *+,j-.  R R  "j#{$%j& 3<  234j56 6j6j6{6{6R gA  >?@  Rr    011 6o p   Z^ ^Zb 6bZf lf`Zj jZu Z Z  Z x(@ & * >G  T ]@o x@ (@ x@  @ x@"! @  %@&$  ( x *  , A .  0 { 2  4  689x @`; > >> B A E =D?@ ( (#'CFG:<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);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)}char__licensebpf_map_defarray_of_hash_mapkey1key2static_unegstatic_neg.bss.data.maps.rodatalicensemaps(||)+/-1357xg |g|  K  0HP,`*h0 0(P%xRR RRR(, P3T03T@R`hR`xblblx | `B5 testdataloader.ccommon.h  !   ! ' !.<, *.   !   YYY .<.     /  !X~  X~  X#~f.L^`jo &08@NUat  4(   "?  pH(I `(( ; / 9X QR@(S8APS`Tx? 9 OGX@Ux??999968>8I8Q8l:t:::8888== C   F & D+ 7HL S Z fIo w  T X U  !R. : F R ^     S     ,M9 E Q ] i v K           +L8 D P \ l x    !W   "? #8 $D@M %YAb &n?w '?9 ( B "8 ) B ": * B "8 +  B$ "0;> ,I<W -b=p .{ B 0> /9988 8(80:8:@;H;P<X<`=h=p>x> @$A<RHSTM`KlLxW ? ? ?HI49D8L8\:l;|<=>9998888(888H8X8h8x8:::;;;;;;(;8;P<h=x=>>>>>>>>> > E9, E08D EH:\ E`8t Ex; E< E= E>B9^8:;<= >5QYVJNHITR3S42XU76MKLWperf_event_array.debug_abbrev.rel.text.rel.BTF.extsocket.bss.mapsdata_sections.rel.debug_ranges.debug_strother.relxdpbtf_outer_maparray_of_hash_map.debug_macinfo.rel.debug_infobtf_outer_map_anonno_relocationasm_relocationbtf_pinglobal_fnstatic_fnargxdp_prog.llvm_addrsigstatic_unegstatic_neg__license.rel.debug_line.rel.debug_frame.rel.debug_locstaticloader.c.strtab.symtab.rodata.dataMY_CONST.rel.BTFLBB7_5key3global_fn3.relsocket/3key2hash_map2global_fn2.relsocket/2key1&7 $@x ' %yt~z (%7 (%  (@% ADC>i0!nj ( %N *%[ W 1%BC  2%.80* 2%]hY 5% M8FI 6p%"Lo7%pGgolang-github-cilium-ebpf-0.7.0/testdata/loader-eb.elf000077700000000000000000000000001414524555700264252loader-clang-12-eb.elfustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/testdata/loader-el.elf000077700000000000000000000000001414524555700264512loader-clang-12-el.elfustar00rootroot00000000000000golang-github-cilium-ebpf-0.7.0/testdata/loader.c000066400000000000000000000102051414524555700216740ustar00rootroot00000000000000/* 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, rewritten by loader #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; #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); } // 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 golang-github-cilium-ebpf-0.7.0/testdata/raw_tracepoint-eb.elf000066400000000000000000000064001414524555700243610ustar00rootroot00000000000000ELF@@@MIT%4I?: ; I!I7 $> $ > .@B: ; 'I?: ; I I  : ; I: ; 8 I: ;  Smo? KRy~Z ~   R Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/raw_tracepoint.c.__licensechar__ARRAY_SIZE_TYPE__sched_process_execintctxargslong unsigned intuint64_tbpf_args @* >B F    bpf_argsargsuint64_tlong unsigned int__ARRAY_SIZE_TYPE__ctxintsched_process_execraw_tracepoint/sched_process_exec./testdata/raw_tracepoint.c return 0;char__licenselicense 0Y Y{0 | Z= testdataraw_tracepoint.ccommon.h    K     + 7L S Zh s      ,@ J.debug_abbrev.text.rel.BTF.ext.debug_str.rel.debug_info.llvm_addrsig__license.rel.debug_line.rel.debug_frameraw_tracepoint/sched_process_execraw_tracepoint.c.strtab.symtab.rel.BTFz@v@MPT1-  "0  CP (  i(e H  Y^U h=oLx golang-github-cilium-ebpf-0.7.0/testdata/raw_tracepoint-el.elf000066400000000000000000000064001414524555700243730ustar00rootroot00000000000000ELF@@@MIT%4I?: ; I!I7 $> $ > .@B: ; 'I?: ; I I  : ; I: ; 8 I: ;  Smo? KRy~Z ~   R Debian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/raw_tracepoint.c.__licensechar__ARRAY_SIZE_TYPE__sched_process_execintctxargslong unsigned intuint64_tbpf_args @*  >B F    bpf_argsargsuint64_tlong unsigned int__ARRAY_SIZE_TYPE__ctxintsched_process_execraw_tracepoint/sched_process_exec./testdata/raw_tracepoint.c return 0;char__licenselicense 0Y Y{0 | Z= testdataraw_tracepoint.ccommon.h    K     + 7L S Zh s      ,@ J.debug_abbrev.text.rel.BTF.ext.debug_str.rel.debug_info.llvm_addrsig__license.rel.debug_line.rel.debug_frameraw_tracepoint/sched_process_execraw_tracepoint.c.strtab.symtab.rel.BTFz@v@MPT1-  "0  CP (  i(e H  Y^U h=Lox golang-github-cilium-ebpf-0.7.0/testdata/raw_tracepoint.c000066400000000000000000000004051414524555700234500ustar00rootroot00000000000000/* 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.7.0/testdata/strings-eb.elf000066400000000000000000000067101414524555700230350ustar00rootroot00000000000000ELF@@ 00 MITThis string is allocated in the string section This one too %4I?: ; I!I7 $> $ > 4I: ; I I' I  &I I: ; .@B: ; I? SfPh? KRrwd i z  K PZDebian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/strings.c.__licensechar__ARRAY_SIZE_TYPE__trace_printklong intunsigned intuint32_tfilterint   y~ intfiltersocket./testdata/strings.c trace_printk(STR1, sizeof(STR1)); trace_printk(STR2, sizeof(STR2)); return 0;char__ARRAY_SIZE_TYPE____licenselicense <P  ($ K(@n, | PS6 testdatastrings.ccommon.h  KK ]8P      + 7L S Z {     ,@P` C .debug_abbrev.text.rel.BTF.ext.relsocket.debug_strfilter.rel.debug_info.llvm_addrsig__license.rel.debug_line.rel.debug_framestrings.c.strtab.symtab.rel.BTF.rodata.str1.1@&@P"  _2>Cr? 8-00D H 6p X@ {(w  kWg OoL(golang-github-cilium-ebpf-0.7.0/testdata/strings-el.elf000066400000000000000000000067101414524555700230470ustar00rootroot00000000000000ELF@@00MITThis string is allocated in the string section This one too %4I?: ; I!I7 $> $ > 4I: ; I I' I  &I I: ; .@B: ; I? SfPh? KRrwd i z  K PZDebian clang version 12.0.1-++20210423083014+072c90a863aa-1~exp1~20210423063719.80testdata/strings.c.__licensechar__ARRAY_SIZE_TYPE__trace_printklong intunsigned intuint32_tfilterint   y~ intfiltersocket./testdata/strings.c trace_printk(STR1, sizeof(STR1)); trace_printk(STR2, sizeof(STR2)); return 0;char__ARRAY_SIZE_TYPE____licenselicense <P  ($ K(@n, | PS6 testdatastrings.ccommon.h  KK ]8P      + 7L S Z {     ,@P` C .debug_abbrev.text.rel.BTF.ext.relsocket.debug_strfilter.rel.debug_info.llvm_addrsig__license.rel.debug_line.rel.debug_framestrings.c.strtab.symtab.rel.BTF.rodata.str1.1@&@P"  _2>Cr? 8-00D H 6p X@ {(w  kWg OLo(golang-github-cilium-ebpf-0.7.0/testdata/strings.c000066400000000000000000000004321414524555700221200ustar00rootroot00000000000000#include "common.h" char __license[] __section("license") = "MIT"; #define STR1 "This string is allocated in the string section\n" #define STR2 "This one too\n" __section("socket") int filter() { trace_printk(STR1, sizeof(STR1)); trace_printk(STR2, sizeof(STR2)); return 0; } golang-github-cilium-ebpf-0.7.0/types.go000066400000000000000000000202471414524555700201530ustar00rootroot00000000000000package ebpf import ( "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 // Max returns the latest supported MapType. func (_ MapType) Max() MapType { return maxMapType - 1 } // 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 // maxMapType - Bound enum of MapTypes, has to be last in enum. maxMapType ) // Deprecated: StructOpts was a typo, use StructOpsMap instead. // // Declared as a variable to prevent stringer from picking it up // as an enum value. var StructOpts MapType = StructOpsMap // 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 // Max return the latest supported ProgramType. func (_ ProgramType) Max() ProgramType { return maxProgramType - 1 } // 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 maxProgramType ) // 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 ) // 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 } golang-github-cilium-ebpf-0.7.0/types_string.go000066400000000000000000000073701414524555700215430ustar00rootroot00000000000000// 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] _ = x[maxMapType-30] } const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStoragemaxMapType" 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, 300} 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[maxProgramType-31] } const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupmaxProgramType" 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, 308} 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]] }