From 505e5c6de7909457a3b621e7bb148999453325bf Mon Sep 17 00:00:00 2001 From: Audrey Dutcher Date: Fri, 23 Feb 2018 05:30:52 -0800 Subject: [PATCH] Choose members of the dynamic tag enum based on the current machine (#183) * Only use processor/os specific dynamic tags if that processor or os is in use * Add testcase for machine-specific dynamic tags * Clarify layout of ENUM_D_TAG --- elftools/elf/elffile.py | 5 +- elftools/elf/enums.py | 71 ++++++++++++------ elftools/elf/structs.py | 14 +++- test/test_dynamic.py | 20 +++++ .../android_dyntags.elf | Bin 0 -> 54808 bytes 5 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 test/testfiles_for_unittests/android_dyntags.elf diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index a1509ee..289a5f9 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -71,7 +71,10 @@ class ELFFile(object): self.structs.create_basic_structs() self.header = self._parse_elf_header() - self.structs.create_advanced_structs(self['e_type'], self['e_machine']) + self.structs.create_advanced_structs( + self['e_type'], + self['e_machine'], + self['e_ident']['EI_OSABI']) self.stream.seek(0) self.e_ident_raw = self.stream.read(16) diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 48ef99a..e37effa 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -468,7 +468,7 @@ ENUM_ST_SHNDX = dict( ) # d_tag -ENUM_D_TAG = dict( +ENUM_D_TAG_COMMON = dict( DT_NULL=0, DT_NEEDED=1, DT_PLTRELSZ=2, @@ -505,24 +505,6 @@ ENUM_D_TAG = dict( DT_PREINIT_ARRAYSZ=33, DT_NUM=34, DT_LOOS=0x6000000d, - DT_SUNW_AUXILIARY=0x6000000d, - DT_SUNW_RTLDINF=0x6000000e, - DT_SUNW_FILTER=0x6000000f, - DT_SUNW_CAP=0x60000010, - DT_SUNW_SYMTAB=0x60000011, - DT_SUNW_SYMSZ=0x60000012, - DT_SUNW_ENCODING=0x60000013, - DT_SUNW_SORTENT=0x60000013, - DT_SUNW_SYMSORT=0x60000014, - DT_SUNW_SYMSORTSZ=0x60000015, - DT_SUNW_TLSSORT=0x60000016, - DT_SUNW_TLSSORTSZ=0x60000017, - DT_SUNW_CAPINFO=0x60000018, - DT_SUNW_STRPAD=0x60000019, - DT_SUNW_CAPCHAIN=0x6000001a, - DT_SUNW_LDMACH=0x6000001b, - DT_SUNW_CAPCHAINENT=0x6000001d, - DT_SUNW_CAPCHAINSZ=0x6000001f, DT_HIOS=0x6ffff000, DT_LOPROC=0x70000000, DT_HIPROC=0x7fffffff, @@ -556,6 +538,36 @@ ENUM_D_TAG = dict( DT_VERDEFNUM=0x6ffffffd, DT_VERNEED=0x6ffffffe, DT_VERNEEDNUM=0x6fffffff, + DT_AUXILIARY=0x7ffffffd, + DT_FILTER=0x7fffffff, + _default_=Pass, +) + +# Above are the dynamic tags which are valid always. +# Below are the dynamic tags which are only valid in certain contexts. + +ENUM_D_TAG_SOLARIS = dict( + DT_SUNW_AUXILIARY=0x6000000d, + DT_SUNW_RTLDINF=0x6000000e, + DT_SUNW_FILTER=0x6000000f, + DT_SUNW_CAP=0x60000010, + DT_SUNW_SYMTAB=0x60000011, + DT_SUNW_SYMSZ=0x60000012, + DT_SUNW_ENCODING=0x60000013, + DT_SUNW_SORTENT=0x60000013, + DT_SUNW_SYMSORT=0x60000014, + DT_SUNW_SYMSORTSZ=0x60000015, + DT_SUNW_TLSSORT=0x60000016, + DT_SUNW_TLSSORTSZ=0x60000017, + DT_SUNW_CAPINFO=0x60000018, + DT_SUNW_STRPAD=0x60000019, + DT_SUNW_CAPCHAIN=0x6000001a, + DT_SUNW_LDMACH=0x6000001b, + DT_SUNW_CAPCHAINENT=0x6000001d, + DT_SUNW_CAPCHAINSZ=0x6000001f, +) + +ENUM_D_TAG_MIPS = dict( DT_MIPS_RLD_VERSION=0x70000001, DT_MIPS_TIME_STAMP=0x70000002, DT_MIPS_ICHECKSUM=0x70000003, @@ -573,11 +585,26 @@ ENUM_D_TAG = dict( DT_MIPS_HIPAGENO=0x70000014, DT_MIPS_RLD_MAP=0x70000016, DT_MIPS_RLD_MAP_REL=0x70000035, - DT_AUXILIARY=0x7ffffffd, - DT_FILTER=0x7fffffff, - _default_=Pass, ) +# Here is the mapping from e_machine enum to the extra dynamic tags which it +# validates. Solaris is missing from this list because its inclusion is not +# controlled by e_machine but rather e_ident[EI_OSABI]. +# TODO: add the rest of the machine-specific dynamic tags, not just mips and +# solaris + +ENUMMAP_EXTRA_D_TAG_MACHINE = dict( + EM_MIPS=ENUM_D_TAG_MIPS, + EM_MIPS_RS3_LE=ENUM_D_TAG_MIPS, +) + +# Here is the full combined mapping from tag name to value + +ENUM_D_TAG = dict(ENUM_D_TAG_COMMON) +ENUM_D_TAG.update(ENUM_D_TAG_SOLARIS) +for k in ENUMMAP_EXTRA_D_TAG_MACHINE: + ENUM_D_TAG.update(ENUMMAP_EXTRA_D_TAG_MACHINE[k]) + ENUM_RELOC_TYPE_MIPS = dict( R_MIPS_NONE=0, R_MIPS_16=1, diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index a89bfed..660f687 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -72,7 +72,7 @@ class ELFStructs(object): self._create_leb128() self._create_ntbs() - def create_advanced_structs(self, e_type=None, e_machine=None): + def create_advanced_structs(self, e_type=None, e_machine=None, e_ident_osabi=None): """ Create all ELF structs except the ehdr. They may possibly depend on provided e_type and/or e_machine parsed from ehdr. """ @@ -81,7 +81,7 @@ class ELFStructs(object): self._create_chdr() self._create_sym() self._create_rel() - self._create_dyn() + self._create_dyn(e_machine, e_ident_osabi) self._create_sunw_syminfo() self._create_gnu_verneed() self._create_gnu_verdef() @@ -225,9 +225,15 @@ class ELFStructs(object): self.Elf_sxword('r_addend'), ) - def _create_dyn(self): + def _create_dyn(self, e_machine=None, e_ident_osabi=None): + d_tag_dict = dict(ENUM_D_TAG_COMMON) + if e_machine in ENUMMAP_EXTRA_D_TAG_MACHINE: + d_tag_dict.update(ENUMMAP_EXTRA_D_TAG_MACHINE[e_machine]) + elif e_ident_osabi == 'ELFOSABI_SOLARIS': + d_tag_dict.update(ENUM_D_TAG_SOLARIS) + self.Elf_Dyn = Struct('Elf_Dyn', - Enum(self.Elf_sxword('d_tag'), **ENUM_D_TAG), + Enum(self.Elf_sxword('d_tag'), **d_tag_dict), self.Elf_xword('d_val'), Value('d_ptr', lambda ctx: ctx['d_val']), ) diff --git a/test/test_dynamic.py b/test/test_dynamic.py index 9b242f0..1ef0080 100644 --- a/test/test_dynamic.py +++ b/test/test_dynamic.py @@ -68,6 +68,26 @@ class TestDynamic(unittest.TestCase): exp = [b'', b'__libc_start_main', b'__gmon_start__', b'abort'] self.assertEqual(symbol_names, exp) + def test_sunw_tags(self): + def extract_sunw(filename): + with open(filename, 'rb') as f: + elf = ELFFile(f) + dyn = elf.get_section_by_name('.dynamic') + + seen = set() + for tag in dyn.iter_tags(): + if type(tag.entry.d_tag) is str and \ + tag.entry.d_tag.startswith("DT_SUNW"): + seen.add(tag.entry.d_tag) + + return seen + + f1 = extract_sunw(os.path.join('test', 'testfiles_for_unittests', + 'exe_solaris32_cc.sparc.elf')) + f2 = extract_sunw(os.path.join('test', 'testfiles_for_unittests', + 'android_dyntags.elf')) + self.assertEqual(f1, {'DT_SUNW_STRPAD', 'DT_SUNW_LDMACH'}) + self.assertEqual(f2, set()) if __name__ == '__main__': unittest.main() diff --git a/test/testfiles_for_unittests/android_dyntags.elf b/test/testfiles_for_unittests/android_dyntags.elf new file mode 100644 index 0000000000000000000000000000000000000000..f93d7f41fe1a66ada0e871f6e6deeaa04790395f GIT binary patch literal 54808 zcmeIb4_s7L`agbW7?G5Kii(VCtjVP!10toO78MAMYEm*Q3y?p8A`lFUM#VHp&_GdZ zWv#Vrx@E1ke9JW|x1GdQD=IQGx3!inbH}8ja$R<_=J$S|d+*Gh%M75|@AG+mUfp_l z?)#kQJm)#j`S;v2cg8z2vNH_^gQQ*|Qi!CYWF)q|E9WFjX6YCyPC8ABl|qrkyyPgE z%qT!IA&nFQ$^p(fPNoxpNz6mCATUWnc~aXNnMNsTl&N0_>XxbDEJ<1gV!f0%f_1(L z+AmWY3Xqtu+E4;qg8E4*NGGXz*3WE(E$x!j(jk)x8G;msWXwUwXYtw zk(i$bx}bV#wXJB;1@lVFFDNT5UszNnNnuE5{cPKm^1`Z$(n9HEC5>(f0bOzRb&dS8XbO5hkv0_$nw9`;Y=OAL5COV z@JbzyWoIcD_1~bw4LZDhM9`jEdtq3dFqQk$`;os`;Q##zv zj#Dn`@6_Sh$Edk-{abaoPluq!&=^OzBwZ53sLir@YjU$1ZUE>Bw)D>pmX&Rc1-D z-YZEjD)QyyC28dYSVOT6F#iu&AJR%BX$@EwX*gPr-cOSuRQMDEVif z3H_hR>n`=*hWxjiB`HXW&n_z8&TtDsIj&{y~edj9W--Lb7Kp*`<`RUMqoW`Hykm0sromJ%9u(`awMBaZW ze<%8Po<{$1W=YC`|5bfYVSIg3?`zLi^w%=zhnT3Z0Q;#_>(gh1J<{JLX)YMsGYxD>5Y{>`R{)* zp-s_pwC`WtGL3_vCBT0!!XZ;IMUy^40 zPQduu3H#?M^RXQdsSeq{?2q4}Khw~ks=ZFwJ4rJh$HI{-(f^l0AL~02 z^Z6k9bBv<@0`zw*`hyqyo8z`kQ~qhp-+{X&savtP1qD~s_|~gi;cwUbl7uc2?c0R$ zn~w3zdBOU!#!Auz)Q2G|a5Cn{>FB&Wp^x@|g7I^@-RF;F-T2E#|F)rVW|XJ=3iwaL z{8jt^Bg_x!Z?FN3{yhkPtcO3;`P+#8ZmNjCHilT zX8bM2_^|+=tH`IKzv9qes{ek#P1<5HB!&L>EU@Ks-+FQo<+S_7X!PeY^e0Rb^1p`u ztD#?=FEenn4)&<~@tN>%9{igCdA4s3=5xeDK7ZYU`lr@N(zS}dYcYRf(4VMA;`(v| z?A`LPZ#=yPebNV#RH*1{1GZwkss6}|f$a^x_ST$<_MyC*f13sE$6!2HsV^x?k~X4$ zRr{;3-ad-;)}i$8yRh#|jeQf)Kbzoxb$qXezM0UcmX~sK3u^Ooi%ZM%%Sx9ON!in< zPLs+?=M_w>t{|_js3|YBRh7=4UsS~;Sv=oXR9k8Ts4ALYif6g1+_jMq1azzVNo@VU#j(mBC9hlF-hyubU8O?wyzKT@-v_?CufLE zZ`S0ga(}1}R{a~W3x!Z|RZ$VuFHXoUPfW`7Y6@UsL4FxLLE)0}#7RSn3Yp|#M1s|o zAUbk5ixjCLP4UVEH$@f+VoG94MfuF4f{OX&rMA+F@{GkC_%pBM*p`R*Y3D@ehVEiIp)I7v9ct2ofIk(huvQczV|X{)Hp zkT6@86)dVuoaFC3bulO{_g@S!?zA0wv<>(6-bh!#q@?*pHm@(zeGL;Umrq?FM+{k3 zZm3Ve2wOU&&R|140mpk-!P85N3YFoqM6BMErz=al7}`Tl`NWaw2x37}GQ6l=4t*Pi z{|=B?SX7)}Q)W}f`BnMl`Pl8SSkzRP6%|!-45?$!J0hngVk6<2H8@LIb-fwEwyVU% z{AzC}>t>YJMdeEtXzNk;pJ~cor<$gl{E21Qm@)Hf%8IHptQRI$bEnN!cJkasA|ux~ zXRLm8=yY9FU1Xb*x?BB_4@Me#_vbd>i7Lk|WWRFx}h9Dfwzx>q^Q&#B2w^V8bdj#Aid*`0bqAbc!zVG7HmuPLf`}(xDC_0G z@M;wg4V73Aau=6Y*=q9r0)2IbPdwb6-CHoJx}>PAY+A*lMF^rZN=r*;VrWg8T~vVG zolgMXWf<#-cJw7)G<{}KHI{O8b5ZGHEaHRWCwXN}ys)68s9@o&nt26f`PEn;{T~Xv zh9n{|%`a0{W4~R)E0R=&NX=HHmGN${>XJ3|20BPQ@xoGNp_o#JR_OX$3B?l=DltH{ z)v8N~T5ED)&7w+W$qM2;?YLEorP!+S%d7JXY#9rLZzoyvs|pZ6T4yEZ`t1^Gfkf;z z%v@X=HgjNxwygqx)|H*U_2EU`iccsE-%5uNDVQJyp>gy=Zw(rNeNTs zz~1k{nUBrGXz-77k64ZGaU}6VzbEmbhq*dDb%p#RcTGSwI9m3vEzn7UcPb5Ect~T1 z4V3k!45`f<3c{*NZ!{o|K=gWoAAStqqLHq*{=?wt>Gd@;d1|FL>PsAY>s1yQgw%d3 ztdg8~@sg@iTanjoITZ_w%H>tYw+(3Lrc&-AQH=9lEHvJcp=4jo0~;v4P!~ok+4>+p zQQxHD0vMgVU#RAt2|A&~Nrx5Zc$=igKgrcN!VB!g1l5UXuV194dU>)Ckb?(hQ_=3M z>PoH;-1RLutk}@o0L6%8_KALHfam>IyAiB&z6DBcU9#Bjy<;fDDneDIog6%RQxYf9 z7C(3R4NleNBYlX|&AB6JlXuYgFB$r$q(sg0#-Kd^q9e9lb3~}?Z@+_?i>D&I7RRlq zIQZbv9!CzPPPb1e%Y*j3(!V*hMXP&ou5W?z&l~iVrSajQJe|qgUAF}n@=dHj zeHCE23?!zjEnw69MoM6N)Md=?Abn_2{pvBKw70E6q?EN}B-IC5c7{~!tv;ZC)W+*u zG?ef#8iN=aSfe8BTM5t>zm-7wWYvFv3@+pghJqbkkL+nDSm}{J@deWmXd&`OaDyIb zf@>MF9m72X9-&%a&+8usgDX6Yr@|qWjPN*47qI9*Bl__bcs-DGq5q!Y{eT7!j6;3m z5VTytWBP!Th6jgN<3KCmL%9Ef7u3if^$`o&;5SEoZ9nXDoFAdd*A+qDsZbsvG*Nd@ z=j*nTA?1dzSBWt-i&_v1_0U_WRZ)Ft14!wEK0RB8PNF`Rmzy+Jhx-1vr* z9=RQ|dY>IZoEx-(L(31ZH^@F1Yz}J?n#p@ zkG_7)8p#kQMcMIMnn|jko|bR6M9*k?UNT3uWH%fWCb`TpUt2G-^t_gDGHtu(`S#;) zkj@NCHcR1VDf&c1n7Q8EGH253Gun!dIniu+nzr!D23-TPf@Xqv<;YB$t?=u?=YVbm z-2|EoQm>nFa|D0{e@R-CA`KbEd(tB;T22oRf)SQ5MFx| z*JAv|D}B;Z&}|^TyLboaP7tqp5MF^4R|Ebo2Q|vj?=N1%l$w+jyy7U~wLx*+r||p1 z9{@cFdPqql!|QwE{n1C2v{m4bfgV@zZ^54ctx+)9??6w1)`Fe}snN)JL1ay7KLvGe*^TUg1Z&|HuyWB9?-j> z?V$5+d^!8Wza@3=y8P=a?D^&dT9?#bpY)4ozBkUG~G%#)XE<_SelX z|JGS`;agq*_-gHEp5m5|dKUlHlQ`zDVHr!LvkJys(DH|wSKYPliJZ?mUzxM=wBJ=f zHu~kHonP!SPWr6vq}7-0PpP(@_ssWwV=uia?SZGiaNb^i;a_i^6Z=E$dFDS}_+D4{ z=BKV2-MR6LkH&}ZNqT7Ox2~=aYp$z${yqb+r$098)-?+ny5FyR?EMF?GX4FEYtIgAO`Y8P-IOW&>z=Oq*174SOLrZ2 z+h0Aauc-RucM%t~Z2xmk(`O^*e-hEtYRQ0^1i`LDt zrY-IIO={;i>*i0(c)#z=`^zuvx^l+K^vB(gymd+bed{lH=KE>VNvoH&{;B%m`|f*InzI=lv>nTHE9UUte<9$DfofyyUCsna347Mt!X?Ssr>{EL`OBo|`+hU<)gSg;S@p?_3l1gn zTZr<17%;euf_#5PqDYnRWC`B0)K7I8q@Vu_?19K7of2LZ7Z+ZU78fN?#9fqv3y#Ih z;piO|PqEHQS|567Wyq)yQ`j!-HmN3)+0=cE$^Y6Ds^T7_sc*CjOr~9<)cbzJ$TC)p zF-Yyp8qfWO!G8JfKi^qz=rn9LG#J_pyTErF2Mj9>YfUQ+?ILw0QX7oj$n6MeGpzMW zHW+sVNP|k3v}@{7R=MdYt4uK1HwREzZy1o+?0RDpT%c|K4rxGYLyF7L1Fm(#?w~nM z27`Tzv~pU50sRvu{@-P+H{fmn-vjPMYR4!6P59;tyKrrY0{0O5l~6^{!xZ$X>5y;x z29;nP1FYjPWWx;hZPMBdm!bZ6m%$Yw({Tp-8`6p^U537p&4$g!mN72Ffsh8{wlJnN z7<&{Bv2LjyWrr3s8SHOU?1cKL`Y4p?kv31hF(vHj_vUd%g{h_pLsM5QKF8%S)*}X$6|77UWdxp;5J47~m)-Cm-VLKuZgtUbB zg{%M_W3b<2*mZ43NWHOV?Aj2QVWs$sW%fI#z-wo?W+t;W9*L`+ODy;8}>}FKVj&ZV_#!v8*P8a zu)Eg2&d?NNf6lPuw8rNRd-ClY3>aiD8`jL>nH2&PYH-EmnK40r(R`{q38L8^;@@rV|?XP;{?hTXncma)&qWHPkhJ&L;L5 zIx{()I?re@9xJU2wSQ@F-(v4KgxkL{^h|6#U}%_WA29TtVgIk8H`jj1(7V+BgJD;c zz23O4+y0L<{UrM;W6uKnii|_EdLkOv z8Da5rMwi!OWIu1LzcayPlvah>JB@ue*k7j1M&qt!ST&5UGla)n#uX88k+dVEag)(q zW#3{%8@EyL4dbdBRWL+qD7JSS_e9#?Hf}4m_t2Of#=eLK7&B0A-$i3SH1^d&?ReaN zY+PR$hl-mnX#CjNeXV^D^?hy}uzB^tyT-LOw0-00jeRWsrE%p=?4kpq_I|2AXxz0# zxMaZC7VR>Omdy4;#-3vv?+WRiVQ&a&8f|Y1Su?Tmo)Al8Tga|k?GJ>Y(29`F`RIRF zeB+7`RQY(w#;fZwL|qu9MyaRP-X5|h%DyH<+2wy`|5rSa^MxdhHsD#D96|M5a&&VZ zxe3qPo!t$??6z9pw1_+b(hhwY$krm!w|uGw?k@ax=aU zPCg#r`y@Y8lKRQdlB5IV7JMI*{A@`&L_Ptd<4f>g;5&charka3`8kplPJS-pW%Bdz zy<_rtd_R!beh2!W{5teM`IG2>avVa%k{6=?$y?F?3`a^ShUN8Z9R);BRp4C{W zBq)=leZ3xBtD}gSy{|`LKK1fj^3pz+z%j)9v#&$oSYk6VPH|A4w!fGaizdm0Hl&M;yi&VT9QR<6&Mp-DzOr$3CyA;bBGfJP9@GGHVd3aTtXZra5`}% zu_Q26m(&vX|A2(m*~In4y#lkUk|yFFfpdu4h+P7+>XMbj9RklKZYORRIFEQOaht#x z&{9bUalODat)!E zHxTy|O9D3$4-of%&-S+v<0J#^7r2c$jJQYO6~s}*E`e7P#}Ic2yo%UN+%9lCaU5}* zz-x#Ti0cJjOPoSnDeyYtG~zsgJBYK0tpcwnwi2fa+(|r#I6>fz#CgPKfnCJ>`L2CY z0&ga+B$foGYfEa0`wy}G-Ng08y#mwKB~8RV0`DMhBX$W)*O#m$?htr4aXWFlz`ew4 ziQ5F;L)<}JFEG2Nq?5Q(;67p(ah|~Js*-KQR)G%?_YkKE%&sfhMVuh;A>v+Qv%nH= zOWed!0wX#`{}W3BhY=4D_kYLsBl1Q68-aTTjw0sI1mY}WtH8;`R^l{)Q;6pfCkUKMoJVXH zIE}c3I7;Aj;!0vk;4I==;{N}N`A=L=+$*q^xQVz&VEz(aNgJ_C;5o!Ai8}jf?$?j)`hxQy6EoF{N4@it(vCh!{K1mb#u*Ak}?R|>q2IE^?@;11#}VynRGiLJzG0(TP6Ax;o@BXJ(FSzs4& z32~Ibn~5ulC4sjQ*An*+i1|-kPuweT4{;N5kH9;K+lXBP?;>7F+#&F8;&$S8fqRM9 z61NGwhq!~dUSKzICvl~~eZ(%}Jc0X(w-H+fK0w?J0>==?5O)a7U+60_6SoU& zCXOR+6WBtWKwK|y9B~S9rNHsTX~cN~ClF^5TLtDXv9=mfS8H743csBh}s&1EX3U z@y|kt2E4GDN|P<_=V3srLztM33T>=#mr`M|P!)aCEsCa_g{HR=%CtJNy_z}|O(jEU ziq&a4L1=0anrvQ8eTt^jhtRbB0Gmgfu512mR%@Z9&%FiCvv*Av%0xrp*Iz>nnb9}h zOgY>QIBpM4wu-wZi1XM z$6|IGm%D7oHRqs{qC%z~p!RRRr9WbB>S`16FAMqeg3E96m%m5If7b~8=@v7prOxM6 z8Jgk@3z>3&&Dr-RWZUF1HA@uR%wjf+xiwQ1TY_R7Qg@2t>>~g2uldTKAj&tOeCrEh z(4*KK6myCxf<}&S{S_yWUsFdzKr^o#vFpTO#=J_A?@U`wmQir5`!q<>lC#0(!6JKS z6+&@)*Re`#+0+w+1l*_0;AKK$KOSeA618c*aQBdZY31|k4Y@KbU<*Z=lH&8G;MwavMy2;GSjHXlzZylyjVRmS1Tbo>y z!xi70X>z`@o;F+&E5@Yk=|vR3LJTrCvmDT|#lqWfzh;ZY?05QgUM!S2$}Fa+ea9nv%` zGD~{vl4vTt6$+gh65N!`mTw2deVp@7^Ky@EymPeE8Ux?OLWJ2bAe#-xs)z{?>)B2f zR|}isvBY6^EA_pGDBIqZG`K#4e|_@?*B7g)Z(5-GKJKSe{p-6{)VIkosD1ZCSZ?1W zHsBjTwC`NGeP^+K92;u;-lMcw!TnlTHmJr}_ummm+q)ZEbttb;THEoVOGsq&6AfO#EQaw zCN!k-0KJ=stmXm7=2-f7lS$e-8pBc2rm$Ae{s+1+ZyHr*NpM~#Ijf?CkL8)+{(@%Jp?5J2ksbqsJUd|xe4C2?JsD|Y`EZ6h zvkn-(<^WhmWiSV0^F(Jhva>at%41(Z9|suhRs>@F1nMXN$2NGiL^e!@Pn-XOD&r8| zVC`P+ifp(H`#jBE#_Cg1kH4CKK@B_>PLF|yk*h+@@fObqK@Iq>Pna*aN^Y@xG>l=R z%PgsKk6~U!i=mw%vEBRF?z>Q>cbAa|zPgn@1)a^mXIBTUwn45ou-E3xnF)%=;{7}} zgPF?i7P-6wt7EHU?mASc@Agpmz&#hz`9b7m~IcsMm4Yq!|)!^Ogib>K|6Rat7(_Dm4%c^F5}Vy(-xpzZEubicB-dpGjs zNc3-D7O%B&gRHe7zErK1*TYoz62$8EE_WTwt+QC=)!+R$2x4-J zN&Q<)YK+Txj?2|d-vUp};Xnq@0XH>csocjLv9Ea7oKPe=M`JAc&$|ouL2@#e2_w81 ztD6w{C5MuwzRMa>ah8)`)E{2BR7yGurbm49c;@W92?h6gGxf z#fabP#-4aEa`_irWf*5H-yYe}3xHZJtu~Bpj7z5~eFG}OdL4)Mur>}@_t|IwSD0+9 zaRz4^SD$F-$yBqKYOsv9PURDTZ1hZZVTph$l!?_Zb+oQI+=4bldwTFUUbu$sRZB+0 zWMsrCON#&M`R{*|e!K+OG$QnzNB z=^)Y4$c9t71$rM3V$cKFAR-&0k=&Y%_(54qy`A?DXi(yWcnlhKp>;n^!!Y)8uq|_* z#5uE=XKBskh)cm#NzqM}yHNsJspzpN8Wd>wL0z0YZ!Dg7G15{n3ZjG_6l}(jp9v*M zS9d)BO|9lLk*V%@!d7oX<{&djM_z_3Wr1o&6mbWjE~YZ?8^C6yuAqAmN7}ottA!`) z5G~3N5^=2j3O;_FC$^?Ic!OILwD>1fh9GQQ{P{A_U@FI9Co+dC#=fb7&;B&y^z;nPY;8t)m zcs6(}coujJcsh7Acp7*Vcq%w|$gtL59pWmKBQK|+?Dv6V?yz#;VI#(nE9MTIZb<_t zP6y|%lGT(s1}wWNb1ay(DKi`_rz!JTusKbc5nywhG9wdRj?INQcj&}A?b-h=8kyx@ zi>T1vb&A|bE5>^cMocv}hlN!oXv| zL&2lLP2f@D`3(!KXqvnWayK+#Ub!!X_i0tuO^Q(m+T1T7qO-qtcMaTb-wZ>=F^TuY zX8;4#-c`z6SGxmi1t2)^Cx86FVnEHnGfMB@f4r7Y1zN_SVcOHKzG5==L$xUr)V%;awx| zP&?P3X19GaZ4?i7V&zwCEobK8)D-cDhWw{gw|E6Ughn{e-Ju()L1?$ta3Q;c~L_ZkOo zrihZYj-_~r6xr|}rR3|Hl5fe`kQRF_UIJ0~THXHjcNl^vA55osp&IU((ku2^5u3r* z2dGNk|Kw;=jgFRnEHc!+&tLIeX3DL?Gz~m9IhOTfa}Vd2=;zOQhx$JuM?ce}euobG zreS__j3Yy$NU}3M&i3Ua^O7A2< zkJ>vH+-rJg6pc~3;1p(Mut$WC-+?Bq^o(cxU2z;-q4)7eNJA{6`gkH$1@iHUABl{> zJ|5WZwJoTR@hq?P@g*E_ZU2xx+eEJQtk^XIdHOOql_fJ-0_#Xkr#dVZ7Yd(1pauf_ zf9;JM&qwrb?|Qrfu14I578c?$yTmatD)R1q^nZ(E)w6I;^eLWCc!W;JVgRN=NrNkL z`PUE;CvD!)$R`skWNf|8Q1raV8pSO}Ig|1U{Tm~>!DV|zHMW%@ol?o0$iq`r4pO)o zQwc*;?!9Fglx`>T11fz&D2;5GCyzdPMV8OOg!NU(M}^`LjIB8y7$a=Cl_L!4s3Z;* z*J0hqffWo$@EV{V<_H7EpqxA=A{(mde?Cg8bMN~PX`MKF)HrUmV%pXp!~jke!wbFQ z`MVfx7=$Sz%7;~le z;%3#x8)O?_LONPmiB?9T)n(8tAH>4hjxFMmqXMH`?Y=;Lbq1=_^_6+wHPDM$>TTq> z!HqmwX(VGf#q$VzP8BUNHopst;zP0y-~rUVh)o8R9d0{~S%{<=Q7fPSU`RCmRTe@f z7fe1W_^*ibDIh!=*>DQoq3yC6$lMo#RJDa7R~c;|!2|oAN2VO%Y{c2R@?`lrcbBf4 z%ES~h!+|&pl4lB5yv&bLp*X<_mrrn%7+-#Fi+4XO3ZE_)PTR_pHTGA!IM1qt_hJ#= zJ2!h^Djpr^7tC_c@$wmBtT;X7W3}e=Fr7^d6)o33a4*^g6%(O47Fj3wwRYur_E4Q+;XVNNAe%vAaTy)uva zj&2lcMbsMEnpM_(O|-+6+KgL0USF1j3rfIHw$4siabn&D{Rb8KjE{Q9_E1GT zjhl<|mgj=tAK#?jcR>|M*}`#kgtW|``tKXAtRJk$$wR1d9@vqtEl zhfq@Qp?U0KpNC$c!ayE+^t~ZGbUQPL@sK9;f`?8;J@n8C;3M(SH(1*3T?NHH4?TkA zO7&1H?lm5I73QcOx=YR)p@(cJsrOJCyr%Kc{ZtspL$%w7@X$@n9L7V49;9aZ;S?VF zkFrjD4L+oY9DS+o_ps{QyV8rqBE{pA9Bhqq90SJ420rXK210B(jsXKs#&RrqBJ{|? z!*vcq4~8|EZpvbDfNhn}iM!ND3by3$n^`^7r< zBjmMr8Rse6yc|P(#?lT%jj+la>-2K5bKp@ zjYat$gu~PiLlNuD5wQ+FXvF(JbcBDb)5>Mb7weRxbxN%B?OUv1q_Iv5_p7e&^A4}} zNdMaVQKb^=)Zmt@{Ox)xhKhAY3nOY3BL;bTP=C~8X;qf5QKFu~FDGeYou|3sbd6KA z)3_*%BJy1bn&rO0nA609jCI_R|3%K<&1WrSV;ZebP-xL zXS8RCLTxPMJ)@=SBRZq~J&X@>M%#|8A8|%oi~Y{tweJ>~xlSCks%Nw@n4MVq#2IZk zXKI5u!fi(Ey$B;h-GB|qYIa9%aI2>~X+GJY=QNsdisvihoOTx%8gUl(G4If0JIMo_ zd5Wmhg+<@ql?c1lsB=EV)rjFH+-o9+jTl3!!B%9c52dXQ;*dHiM}EMzh(^OB-ysbY_j717!Hu%8XziNT32W=Hmwv^Vu;TPFNKD zH@WseQZ?mqWT>V*Kp~j&UyLTzlzWi|QyBB|sI3@oN_1gJ$0I@7S4gj+nkJSM^XU%C zuMl2oHZK1naybJX49WP5w?$s@8c(x$D9{?6_Sz>Q=WJ^3d961DRfQ>4h*}kW`%)n& z&Q15F<4!sGoa??23xd6C&5iJ`4WBHgTWroUORb1cpMp({Pp@ZuIuT~nBSD0y*_JxQ zCi>H{YcZ}Io9D`B-`J+VA@6;_jDpMX`KJ;Xm&p?g z)ntq1Lwhooutlst>2QPO48<8bKRx9cy6^<1l<$Z(T5GFYUNX|zUys5IFwMl_rM6|8 zl8-WjE4>YsVt8dK!MFR$t-@v=8L8!0%H^{LMSyqy1*0X9(qcIS7KwKX{XYkoE@!6E z+(!`f`c33`WFjnfFdd!y5hnd*l8L_=hd+^-128k2?d#-N!PtxEgyaOOfyZ*-dVIDl z+%HbUy{EOJPhIi~lCAaKI;1T>SQE!)T)Z{%ih)+sofzfk3@Afg7^jShG|1AZZ5VN| zvGRM-U8VyVbE>Ouh6v3QU8oeBjx>l*QN*=b6Opy=J+xJR1lW2AGe>y_;Iy`PExcY# z9Q}FcY?K@78Q@Q%@I<+A&}V=?7(UWxfE)xv0mA9qyPzWQGr&A#1%C#xL7(>-Uxkv;=7VJg|X{*$9PVT84h^{>4fBU5<>xD~Z? zH(RQ=q6JMr&uSuV-+5<il!r2S`vq(w!ArTO@QRPDcHS+YbbuP|%H zyuYM8{+E(pd7-+f$}ImRtC{~YshO)qz{2M83Y)pIW*+_-&61+Rc=M8c{8PQ<^CwQ6 zC}mVtRaBYJE;JWZl$Yb*6hpGvR$&%)>6=0QXD6i8o_}^ivj3l?+Orb|6XlhEIC`S@ z*JAztk*(qKVu5XqsmxCw$PD>{9feaCqevl zR4O{mNNxZfgv{mO+InmnnYGA@!mK<)Cv%-5!IEHw?l5p(Y|{Zy zKhiE>0&I2|r$v}cBf_S1MVOe+w+SWa36`IXeA;rBw*1XL3WR>o;67OM5B}S@T-ZtG-)YF>qgq! zNUK!pSsh(#%ME6lwLiXBquiSsY-j1PaC-x?eETL)TmPbKE6b#z3{PsG4N_*cuFPD>*-)kzW%#Y|Ail7M zm<HKE+gD2JaC3h4W^Amv;rx8n%v8$h{( zhgly_01`%He&JALuVSZnJXIP$@EcIbH-qvZzXkFSD)JnoR{SWaI!M`H4Uh>(;PsFq zqn~3f{w@>qyAx$h_!L9kW1>*D8)-(QY4^Hbq#Z>4W<|GlFJKu7!pv9aJkz3))`v7z z57((U0Dg9QwAZP6=p7H6)%sIW*9xS?APqlP6?iOWMsygAYX+~05NG)vDBtoU%10jq zB{%?W1PeG9MEy$w)}MtUIVhir@{VA0DHFe5YkVbQqhT6vRs@+&;y3l%q3aGDBHuZr zu3xLV?g^+X&`yDgx03CH;~&Ra`XxiG%f`Uv^8(gov<6PzB-RdPF>ehhbGt@_<8T#h zOT{N~>exs?S_eQ9(%8=d*61qTRFSy0?LnDKP=;@q1?*461+@{Wj1F!HVAwP)ZDKEp z$BhM_A6*GdJ>I!*#-!E+adF6=1KD)Qs&TJRwn{51*6Rky&V=kdMX!GT&(SrCzUf4n zdX!Op!?bRsEk>HUCeUBKNV6erK@i<}0dz-)OVTqs-El~3*XU*&Q<3&K(yml=``YO5 z5874&*+1#Z*CA~q(#{=JesEm47P2qv%5Oy4f{h-}j6vm%Q?*WJ{cgy%pgh6>aWU-> z(&{uinHF`dB=K!0EFt3Je6}F19BKH`+`!{U%;$P_4&D`D3~`&2sH+mX((%5*(a^OK zy2gF{<8`&buCNHi?ayY&($*pEOr#yo-0wx1b5Z8U&i%MZ?2G>xeynp|rti> zWmMnLU)zvYj5O`s-;K0Fq%A;xfVI*XXzoWr*`qq${3ZzB^Hz1UjVVaG7ilAz`-PBw zR#(0jY3q=7u40>S?HD}w*Fg3eUHML=b>gFn8H36Xp8I$^TyidXWreosRheXQzXtdxVaIY>L&vC=x|eFS>d`Z*6=NMqmd#lC=h zn}}7`28>naunami3@>Sh1$!0|Yum5Xwe5+3b3I$hwkjNIY1>@rUjM1bb9b;cMd;od zP-Pv;uR?i#7wCfEu}P(IR>0zc*Q9_?O;g6+7Ev}WqS7k<8Xp%-#MrSXNYd~B>GO92 zY)U}d6G&tG0?es^@kb%bJcBYnb`4qwJuC3)%y4s#^K%zu*F$zBYmg~gk~Ts1aMqv{ zl<7wqbu4mDWg~4b($sZ={>nqz9;7Y6eE@$2T!U7i%(#92y4NBt3Tditws9lU_-*jv z{6X8?kUd{l{t(jQk#??P+fZvz+=_k+j(5$Ch};V1AEa4UE=_#E&&@Fwtj@J{e{@V|k( zz`q3F0eT@%5h!Jh%mvi|M;Aw8$1CY*I2&sc&37Z$S@ub`Na%7v5qA=Wo6}0B!}}{4RVB91^0b>MFBo#4^`#yxlrcprEP z_z&Q9;GsM4(JFY{`yS73@EJRCKLA@lK%2o+@a~!EKky594EP=3+29=?qF(Sr;4bh< zcoypeUkT3N`+NdC8GJo>7WnCiwd=s+z}vtt0bc{Y9J~j7CHNljP2l`})E(gboz=bI zh2UR;*MdhN&Rz)~1HKO24DJGd2|VTydqx;x_Pb?uXxdJePikJ_64K zUj=>_cn|pcy(s?!`tqL`pWv5(SAwqsUjzOmcqe!#csKZ&ZpdPu#)C(LUk)Az-VB}t zz6!hqd<*yraIp~l{O4y6{Op0BJ@B&!e)ho69{4})0aFp4;R_^bLLts|Kre$1=Q3%8 zUoOV^B4`;%e<8H-keEIn`A-Fq(U%zs#$(ioE<6HwNFx$H3?cJj$R3F(!a@(}NXm|+ zjCmwNKT_tWY=dQlw(W)wNh1^=vDirK*DE|)_8dnwO8FaLL$aNUU7sJl4!yO+`JLDBLWUib^yYw`IVj~jypWc5)T7R&{{Mdc*aO-D( z4LA0*ef4AQ0ieh=>-f?9~d>p=1E!ye!=$cc0HV2%os9vvh-Sn~hh?GI*$ znr>2WhT-h9;rfj#=^KU(&T zw0`!}aC1i6KO?n|<1ia9J9qB}2iZU-;=iFj&>Gy^K!uP?);j_Cf7(`ebsXh8~dQ5wN%Il}xJ8Ab%Z9nVyDcdlT^^-QJbCdT+^BQUW z`Z|x6J%>|2`+B%FOmFAm;=^qhZ3_PcbN(OTpk&ZyP93K5gMO68lx3YPnqc$NwGa{xQ=1>~Jj_Y5jV=N6Vg(*3W(#ZtiINXQcLV z{AYiL{eBNP$ObYIe~S7*YjAG^6+$jq?*!ogY5Pq9YzmY#(*2hHXxWSXACw+QkuGhd z_3Mg>z8tPU^<@HK+WS*pKjq#@yMJo?S;tS=hLNnFv_YMlyg!=PNbA?vd9>^~och_< zM`O(E+oHvX+b-G^{yFCSK5$SnXfr6=jkJGa{|9y8eidX=K<$wKzp(%FweYMj@SR6+ zHx{vz!8hi}>p5PFm+z~os9&r$d-3SX%3WeRUlI9~e|6-v-v&_Ph>jgoW{XaZ;wC=)arR18`SY6Lw5dJ?n&v<0*iv=?*`6nYcNgC>9` zfigj}LB*iOphnO`peI2aKwCgNL3=?5L7{U|9y9?o36u$%4JrmL1~q~n0zC=Z0NMiD z3EB%f2nxl3ISDiYGzpXmnhh!jEe17$9s)fH+5p-D+6meVItU8A1?52#K$Ae3pxK~e z&|**{=poRPpbelcpq-$-po5^$T$Be*08IjAf@Xt?L5o3+poc(Df;ND*fOdlRf)0X0 z^H3f%0W=Ae37QQm1}z3Pf*t}r3EBYK0@?}M3pxl2%}06A1kfZzAqs-mi*vZDOz zqH`d|`HH1Pa0pBDE6NJZi4zkOkychZPy9ZN@+&v^5gSQ*a8#~Cnpj;@ZL6~7&yyyW zmfMP|Dy50|<(Z<1>W{#b78aG;l(hNfH52F6l$I5qUs@V%y>yYxZB^1l=9c7F zm&kXEi>j(iE6V*qE=o~K$;(9{m`_FWO=THsvK7_hZ&gKMzAayxSX7c*T$R75$aj}p zQiyWJrRAl$`BhcKqdZ66g9}2=T%or6ALO9E%I+4 zop!#~_}s#xc{TIPO3N3*jU&FeZW};G5WjOJes@Jvnt8mq?&E9lLaqdI{4JIkW+)fS z^ZErym8*wbJ><;HP%id8uS-Czo8`EFv_mfKSb6`ztr#0z$ESiqK$PRY!tWw-Ur{g0 z^1>1>;r06p%5lG`#Y2hePftWlM#C>>Uy1Xwl zpD_wK_um!}<+=ZMU~lL^TY5G6=sU{Z4^rj$eq7rue7FB1Bq|r};q@?xa>Rv?fSU3B zQ6?xCb@BQQNR>PEDB61ube<@(#2;1eX^mV<2kdQ!J!&TM)Y}(vj~GMrG57QOb-p|# zd*!-t&%K{xf%|*|oq}+@8M|Yo2u^zXoIdSIr}* QZR(W=x%1UbB&xUnABW