123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452 |
- /* Pointer Bounds Checker insrumentation pass.
- Copyright (C) 2014-2015 Free Software Foundation, Inc.
- Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "hash-set.h"
- #include "machmode.h"
- #include "vec.h"
- #include "double-int.h"
- #include "input.h"
- #include "alias.h"
- #include "symtab.h"
- #include "options.h"
- #include "wide-int.h"
- #include "inchash.h"
- #include "tree.h"
- #include "fold-const.h"
- #include "stor-layout.h"
- #include "varasm.h"
- #include "target.h"
- #include "tree-iterator.h"
- #include "tree-cfg.h"
- #include "langhooks.h"
- #include "tree-pass.h"
- #include "diagnostic.h"
- #include "ggc.h"
- #include "is-a.h"
- #include "cfgloop.h"
- #include "stringpool.h"
- #include "tree-ssa-alias.h"
- #include "tree-ssanames.h"
- #include "tree-ssa-operands.h"
- #include "tree-ssa-address.h"
- #include "tree-ssa.h"
- #include "predict.h"
- #include "dominance.h"
- #include "cfg.h"
- #include "basic-block.h"
- #include "tree-ssa-loop-niter.h"
- #include "gimple-expr.h"
- #include "gimple.h"
- #include "tree-phinodes.h"
- #include "gimple-ssa.h"
- #include "ssa-iterators.h"
- #include "gimple-pretty-print.h"
- #include "gimple-iterator.h"
- #include "gimplify.h"
- #include "gimplify-me.h"
- #include "print-tree.h"
- #include "hashtab.h"
- #include "tm.h"
- #include "hard-reg-set.h"
- #include "function.h"
- #include "rtl.h"
- #include "flags.h"
- #include "statistics.h"
- #include "real.h"
- #include "fixed-value.h"
- #include "insn-config.h"
- #include "expmed.h"
- #include "dojump.h"
- #include "explow.h"
- #include "calls.h"
- #include "emit-rtl.h"
- #include "stmt.h"
- #include "expr.h"
- #include "tree-ssa-propagate.h"
- #include "gimple-fold.h"
- #include "tree-chkp.h"
- #include "gimple-walk.h"
- #include "rtl.h" /* For MEM_P, assign_temp. */
- #include "tree-dfa.h"
- #include "ipa-ref.h"
- #include "lto-streamer.h"
- #include "cgraph.h"
- #include "ipa-chkp.h"
- #include "params.h"
- /* Pointer Bounds Checker instruments code with memory checks to find
- out-of-bounds memory accesses. Checks are performed by computing
- bounds for each pointer and then comparing address of accessed
- memory before pointer dereferencing.
- 1. Function clones.
- See ipa-chkp.c.
- 2. Instrumentation.
- There are few things to instrument:
- a) Memory accesses - add checker calls to check address of accessed memory
- against bounds of dereferenced pointer. Obviously safe memory
- accesses like static variable access does not have to be instrumented
- with checks.
- Example:
- val_2 = *p_1;
- with 4 bytes access is transformed into:
- __builtin___chkp_bndcl (__bound_tmp.1_3, p_1);
- D.1_4 = p_1 + 3;
- __builtin___chkp_bndcu (__bound_tmp.1_3, D.1_4);
- val_2 = *p_1;
- where __bound_tmp.1_3 are bounds computed for pointer p_1,
- __builtin___chkp_bndcl is a lower bound check and
- __builtin___chkp_bndcu is an upper bound check.
- b) Pointer stores.
- When pointer is stored in memory we need to store its bounds. To
- achieve compatibility of instrumented code with regular codes
- we have to keep data layout and store bounds in special bound tables
- via special checker call. Implementation of bounds table may vary for
- different platforms. It has to associate pointer value and its
- location (it is required because we may have two equal pointers
- with different bounds stored in different places) with bounds.
- Another checker builtin allows to get bounds for specified pointer
- loaded from specified location.
- Example:
- buf1[i_1] = &buf2;
- is transformed into:
- buf1[i_1] = &buf2;
- D.1_2 = &buf1[i_1];
- __builtin___chkp_bndstx (D.1_2, &buf2, __bound_tmp.1_2);
- where __bound_tmp.1_2 are bounds of &buf2.
- c) Static initialization.
- The special case of pointer store is static pointer initialization.
- Bounds initialization is performed in a few steps:
- - register all static initializations in front-end using
- chkp_register_var_initializer
- - when file compilation finishes we create functions with special
- attribute 'chkp ctor' and put explicit initialization code
- (assignments) for all statically initialized pointers.
- - when checker constructor is compiled checker pass adds required
- bounds initialization for all statically initialized pointers
- - since we do not actually need excess pointers initialization
- in checker constructor we remove such assignments from them
- d) Calls.
- For each call in the code we add additional arguments to pass
- bounds for pointer arguments. We determine type of call arguments
- using arguments list from function declaration; if function
- declaration is not available we use function type; otherwise
- (e.g. for unnamed arguments) we use type of passed value. Function
- declaration/type is replaced with the instrumented one.
- Example:
- val_1 = foo (&buf1, &buf2, &buf1, 0);
- is translated into:
- val_1 = foo.chkp (&buf1, __bound_tmp.1_2, &buf2, __bound_tmp.1_3,
- &buf1, __bound_tmp.1_2, 0);
- e) Returns.
- If function returns a pointer value we have to return bounds also.
- A new operand was added for return statement to hold returned bounds.
- Example:
- return &_buf1;
- is transformed into
- return &_buf1, __bound_tmp.1_1;
- 3. Bounds computation.
- Compiler is fully responsible for computing bounds to be used for each
- memory access. The first step for bounds computation is to find the
- origin of pointer dereferenced for memory access. Basing on pointer
- origin we define a way to compute its bounds. There are just few
- possible cases:
- a) Pointer is returned by call.
- In this case we use corresponding checker builtin method to obtain returned
- bounds.
- Example:
- buf_1 = malloc (size_2);
- foo (buf_1);
- is translated into:
- buf_1 = malloc (size_2);
- __bound_tmp.1_3 = __builtin___chkp_bndret (buf_1);
- foo (buf_1, __bound_tmp.1_3);
- b) Pointer is an address of an object.
- In this case compiler tries to compute objects size and create corresponding
- bounds. If object has incomplete type then special checker builtin is used to
- obtain its size at runtime.
- Example:
- foo ()
- {
- <unnamed type> __bound_tmp.3;
- static int buf[100];
- <bb 3>:
- __bound_tmp.3_2 = __builtin___chkp_bndmk (&buf, 400);
- <bb 2>:
- return &buf, __bound_tmp.3_2;
- }
- Example:
- Address of an object 'extern int buf[]' with incomplete type is
- returned.
- foo ()
- {
- <unnamed type> __bound_tmp.4;
- long unsigned int __size_tmp.3;
- <bb 3>:
- __size_tmp.3_4 = __builtin_ia32_sizeof (buf);
- __bound_tmp.4_3 = __builtin_ia32_bndmk (&buf, __size_tmp.3_4);
- <bb 2>:
- return &buf, __bound_tmp.4_3;
- }
- c) Pointer is the result of object narrowing.
- It happens when we use pointer to an object to compute pointer to a part
- of an object. E.g. we take pointer to a field of a structure. In this
- case we perform bounds intersection using bounds of original object and
- bounds of object's part (which are computed basing on its type).
- There may be some debatable questions about when narrowing should occur
- and when it should not. To avoid false bound violations in correct
- programs we do not perform narrowing when address of an array element is
- obtained (it has address of the whole array) and when address of the first
- structure field is obtained (because it is guaranteed to be equal to
- address of the whole structure and it is legal to cast it back to structure).
- Default narrowing behavior may be changed using compiler flags.
- Example:
- In this example address of the second structure field is returned.
- foo (struct A * p, __bounds_type __bounds_of_p)
- {
- <unnamed type> __bound_tmp.3;
- int * _2;
- int * _5;
- <bb 2>:
- _5 = &p_1(D)->second_field;
- __bound_tmp.3_6 = __builtin___chkp_bndmk (_5, 4);
- __bound_tmp.3_8 = __builtin___chkp_intersect (__bound_tmp.3_6,
- __bounds_of_p_3(D));
- _2 = &p_1(D)->second_field;
- return _2, __bound_tmp.3_8;
- }
- Example:
- In this example address of the first field of array element is returned.
- foo (struct A * p, __bounds_type __bounds_of_p, int i)
- {
- long unsigned int _3;
- long unsigned int _4;
- struct A * _6;
- int * _7;
- <bb 2>:
- _3 = (long unsigned int) i_1(D);
- _4 = _3 * 8;
- _6 = p_5(D) + _4;
- _7 = &_6->first_field;
- return _7, __bounds_of_p_2(D);
- }
- d) Pointer is the result of pointer arithmetic or type cast.
- In this case bounds of the base pointer are used. In case of binary
- operation producing a pointer we are analyzing data flow further
- looking for operand's bounds. One operand is considered as a base
- if it has some valid bounds. If we fall into a case when none of
- operands (or both of them) has valid bounds, a default bounds value
- is used.
- Trying to find out bounds for binary operations we may fall into
- cyclic dependencies for pointers. To avoid infinite recursion all
- walked phi nodes instantly obtain corresponding bounds but created
- bounds are marked as incomplete. It helps us to stop DF walk during
- bounds search.
- When we reach pointer source, some args of incomplete bounds phi obtain
- valid bounds and those values are propagated further through phi nodes.
- If no valid bounds were found for phi node then we mark its result as
- invalid bounds. Process stops when all incomplete bounds become either
- valid or invalid and we are able to choose a pointer base.
- e) Pointer is loaded from the memory.
- In this case we just need to load bounds from the bounds table.
- Example:
- foo ()
- {
- <unnamed type> __bound_tmp.3;
- static int * buf;
- int * _2;
- <bb 2>:
- _2 = buf;
- __bound_tmp.3_4 = __builtin___chkp_bndldx (&buf, _2);
- return _2, __bound_tmp.3_4;
- }
- */
- typedef void (*assign_handler)(tree, tree, void *);
- static tree chkp_get_zero_bounds ();
- static tree chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter);
- static tree chkp_find_bounds_loaded (tree ptr, tree ptr_src,
- gimple_stmt_iterator *iter);
- static void chkp_parse_array_and_component_ref (tree node, tree *ptr,
- tree *elt, bool *safe,
- bool *bitfield,
- tree *bounds,
- gimple_stmt_iterator *iter,
- bool innermost_bounds);
- #define chkp_bndldx_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDLDX))
- #define chkp_bndstx_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDSTX))
- #define chkp_checkl_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL))
- #define chkp_checku_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU))
- #define chkp_bndmk_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK))
- #define chkp_ret_bnd_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET))
- #define chkp_intersect_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT))
- #define chkp_narrow_bounds_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_NARROW))
- #define chkp_sizeof_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_SIZEOF))
- #define chkp_extract_lower_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_LOWER))
- #define chkp_extract_upper_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER))
- static GTY (()) tree chkp_uintptr_type;
- static GTY (()) tree chkp_zero_bounds_var;
- static GTY (()) tree chkp_none_bounds_var;
- static GTY (()) basic_block entry_block;
- static GTY (()) tree zero_bounds;
- static GTY (()) tree none_bounds;
- static GTY (()) tree incomplete_bounds;
- static GTY (()) tree tmp_var;
- static GTY (()) tree size_tmp_var;
- static GTY (()) bitmap chkp_abnormal_copies;
- struct hash_set<tree> *chkp_invalid_bounds;
- struct hash_set<tree> *chkp_completed_bounds_set;
- struct hash_map<tree, tree> *chkp_reg_bounds;
- struct hash_map<tree, tree> *chkp_bound_vars;
- struct hash_map<tree, tree> *chkp_reg_addr_bounds;
- struct hash_map<tree, tree> *chkp_incomplete_bounds_map;
- struct hash_map<tree, tree> *chkp_bounds_map;
- struct hash_map<tree, tree> *chkp_static_var_bounds;
- static bool in_chkp_pass;
- #define CHKP_BOUND_TMP_NAME "__bound_tmp"
- #define CHKP_SIZE_TMP_NAME "__size_tmp"
- #define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_"
- #define CHKP_STRING_BOUNDS_PREFIX "__chkp_string_bounds_"
- #define CHKP_VAR_BOUNDS_PREFIX "__chkp_var_bounds_"
- #define CHKP_ZERO_BOUNDS_VAR_NAME "__chkp_zero_bounds"
- #define CHKP_NONE_BOUNDS_VAR_NAME "__chkp_none_bounds"
- /* Static checker constructors may become very large and their
- compilation with optimization may take too much time.
- Therefore we put a limit to number of statements in one
- constructor. Tests with 100 000 statically initialized
- pointers showed following compilation times on Sandy Bridge
- server (used -O2):
- limit 100 => ~18 sec.
- limit 300 => ~22 sec.
- limit 1000 => ~30 sec.
- limit 3000 => ~49 sec.
- limit 5000 => ~55 sec.
- limit 10000 => ~76 sec.
- limit 100000 => ~532 sec. */
- #define MAX_STMTS_IN_STATIC_CHKP_CTOR (PARAM_VALUE (PARAM_CHKP_MAX_CTOR_SIZE))
- struct chkp_ctor_stmt_list
- {
- tree stmts;
- int avail;
- };
- /* Return 1 if function FNDECL is instrumented by Pointer
- Bounds Checker. */
- bool
- chkp_function_instrumented_p (tree fndecl)
- {
- return fndecl
- && lookup_attribute ("chkp instrumented", DECL_ATTRIBUTES (fndecl));
- }
- /* Mark function FNDECL as instrumented. */
- void
- chkp_function_mark_instrumented (tree fndecl)
- {
- if (chkp_function_instrumented_p (fndecl))
- return;
- DECL_ATTRIBUTES (fndecl)
- = tree_cons (get_identifier ("chkp instrumented"), NULL,
- DECL_ATTRIBUTES (fndecl));
- }
- /* Return true when STMT is builtin call to instrumentation function
- corresponding to CODE. */
- bool
- chkp_gimple_call_builtin_p (gimple call,
- enum built_in_function code)
- {
- tree fndecl;
- if (is_gimple_call (call)
- && (fndecl = targetm.builtin_chkp_function (code))
- && gimple_call_fndecl (call) == fndecl)
- return true;
- return false;
- }
- /* Emit code to store zero bounds for PTR located at MEM. */
- void
- chkp_expand_bounds_reset_for_mem (tree mem, tree ptr)
- {
- tree zero_bnd, bnd, addr, bndstx;
- if (flag_chkp_use_static_const_bounds)
- zero_bnd = chkp_get_zero_bounds_var ();
- else
- zero_bnd = chkp_build_make_bounds_call (integer_zero_node,
- integer_zero_node);
- bnd = make_tree (pointer_bounds_type_node,
- assign_temp (pointer_bounds_type_node, 0, 1));
- addr = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (mem)), mem);
- bndstx = chkp_build_bndstx_call (addr, ptr, bnd);
- expand_assignment (bnd, zero_bnd, false);
- expand_normal (bndstx);
- }
- /* Build retbnd call for returned value RETVAL.
- If BNDVAL is not NULL then result is stored
- in it. Otherwise a temporary is created to
- hold returned value.
- GSI points to a position for a retbnd call
- and is set to created stmt.
- Cgraph edge is created for a new call if
- UPDATE_EDGE is 1.
- Obtained bounds are returned. */
- tree
- chkp_insert_retbnd_call (tree bndval, tree retval,
- gimple_stmt_iterator *gsi)
- {
- gimple call;
- if (!bndval)
- bndval = create_tmp_reg (pointer_bounds_type_node, "retbnd");
- call = gimple_build_call (chkp_ret_bnd_fndecl, 1, retval);
- gimple_call_set_lhs (call, bndval);
- gsi_insert_after (gsi, call, GSI_CONTINUE_LINKING);
- return bndval;
- }
- /* Build a GIMPLE_CALL identical to CALL but skipping bounds
- arguments. */
- gcall *
- chkp_copy_call_skip_bounds (gcall *call)
- {
- bitmap bounds;
- unsigned i;
- bitmap_obstack_initialize (NULL);
- bounds = BITMAP_ALLOC (NULL);
- for (i = 0; i < gimple_call_num_args (call); i++)
- if (POINTER_BOUNDS_P (gimple_call_arg (call, i)))
- bitmap_set_bit (bounds, i);
- if (!bitmap_empty_p (bounds))
- call = gimple_call_copy_skip_args (call, bounds);
- gimple_call_set_with_bounds (call, false);
- BITMAP_FREE (bounds);
- bitmap_obstack_release (NULL);
- return call;
- }
- /* Redirect edge E to the correct node according to call_stmt.
- Return 1 if bounds removal from call_stmt should be done
- instead of redirection. */
- bool
- chkp_redirect_edge (cgraph_edge *e)
- {
- bool instrumented = false;
- tree decl = e->callee->decl;
- if (e->callee->instrumentation_clone
- || chkp_function_instrumented_p (decl))
- instrumented = true;
- if (instrumented
- && !gimple_call_with_bounds_p (e->call_stmt))
- e->redirect_callee (cgraph_node::get_create (e->callee->orig_decl));
- else if (!instrumented
- && gimple_call_with_bounds_p (e->call_stmt)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDCL)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDCU)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDSTX))
- {
- if (e->callee->instrumented_version)
- e->redirect_callee (e->callee->instrumented_version);
- else
- {
- tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
- /* Avoid bounds removal if all args will be removed. */
- if (!args || TREE_VALUE (args) != void_type_node)
- return true;
- else
- gimple_call_set_with_bounds (e->call_stmt, false);
- }
- }
- return false;
- }
- /* Mark statement S to not be instrumented. */
- static void
- chkp_mark_stmt (gimple s)
- {
- gimple_set_plf (s, GF_PLF_1, true);
- }
- /* Mark statement S to be instrumented. */
- static void
- chkp_unmark_stmt (gimple s)
- {
- gimple_set_plf (s, GF_PLF_1, false);
- }
- /* Return 1 if statement S should not be instrumented. */
- static bool
- chkp_marked_stmt_p (gimple s)
- {
- return gimple_plf (s, GF_PLF_1);
- }
- /* Get var to be used for bound temps. */
- static tree
- chkp_get_tmp_var (void)
- {
- if (!tmp_var)
- tmp_var = create_tmp_reg (pointer_bounds_type_node, CHKP_BOUND_TMP_NAME);
- return tmp_var;
- }
- /* Get SSA_NAME to be used as temp. */
- static tree
- chkp_get_tmp_reg (gimple stmt)
- {
- if (in_chkp_pass)
- return make_ssa_name (chkp_get_tmp_var (), stmt);
- return make_temp_ssa_name (pointer_bounds_type_node, stmt,
- CHKP_BOUND_TMP_NAME);
- }
- /* Get var to be used for size temps. */
- static tree
- chkp_get_size_tmp_var (void)
- {
- if (!size_tmp_var)
- size_tmp_var = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME);
- return size_tmp_var;
- }
- /* Register bounds BND for address of OBJ. */
- static void
- chkp_register_addr_bounds (tree obj, tree bnd)
- {
- if (bnd == incomplete_bounds)
- return;
- chkp_reg_addr_bounds->put (obj, bnd);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered bound ");
- print_generic_expr (dump_file, bnd, 0);
- fprintf (dump_file, " for address of ");
- print_generic_expr (dump_file, obj, 0);
- fprintf (dump_file, "\n");
- }
- }
- /* Return bounds registered for address of OBJ. */
- static tree
- chkp_get_registered_addr_bounds (tree obj)
- {
- tree *slot = chkp_reg_addr_bounds->get (obj);
- return slot ? *slot : NULL_TREE;
- }
- /* Mark BOUNDS as completed. */
- static void
- chkp_mark_completed_bounds (tree bounds)
- {
- chkp_completed_bounds_set->add (bounds);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Marked bounds ");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, " as completed\n");
- }
- }
- /* Return 1 if BOUNDS were marked as completed and 0 otherwise. */
- static bool
- chkp_completed_bounds (tree bounds)
- {
- return chkp_completed_bounds_set->contains (bounds);
- }
- /* Clear comleted bound marks. */
- static void
- chkp_erase_completed_bounds (void)
- {
- delete chkp_completed_bounds_set;
- chkp_completed_bounds_set = new hash_set<tree>;
- }
- /* Mark BOUNDS associated with PTR as incomplete. */
- static void
- chkp_register_incomplete_bounds (tree bounds, tree ptr)
- {
- chkp_incomplete_bounds_map->put (bounds, ptr);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered incomplete bounds ");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, " for ");
- print_generic_expr (dump_file, ptr, 0);
- fprintf (dump_file, "\n");
- }
- }
- /* Return 1 if BOUNDS are incomplete and 0 otherwise. */
- static bool
- chkp_incomplete_bounds (tree bounds)
- {
- if (bounds == incomplete_bounds)
- return true;
- if (chkp_completed_bounds (bounds))
- return false;
- return chkp_incomplete_bounds_map->get (bounds) != NULL;
- }
- /* Clear incomleted bound marks. */
- static void
- chkp_erase_incomplete_bounds (void)
- {
- delete chkp_incomplete_bounds_map;
- chkp_incomplete_bounds_map = new hash_map<tree, tree>;
- }
- /* Build and return bndmk call which creates bounds for structure
- pointed by PTR. Structure should have complete type. */
- tree
- chkp_make_bounds_for_struct_addr (tree ptr)
- {
- tree type = TREE_TYPE (ptr);
- tree size;
- gcc_assert (POINTER_TYPE_P (type));
- size = TYPE_SIZE (TREE_TYPE (type));
- gcc_assert (size);
- return build_call_nary (pointer_bounds_type_node,
- build_fold_addr_expr (chkp_bndmk_fndecl),
- 2, ptr, size);
- }
- /* Traversal function for chkp_may_finish_incomplete_bounds.
- Set RES to 0 if at least one argument of phi statement
- defining bounds (passed in KEY arg) is unknown.
- Traversal stops when first unknown phi argument is found. */
- bool
- chkp_may_complete_phi_bounds (tree const &bounds, tree *slot ATTRIBUTE_UNUSED,
- bool *res)
- {
- gimple phi;
- unsigned i;
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
- phi = SSA_NAME_DEF_STMT (bounds);
- gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI);
- for (i = 0; i < gimple_phi_num_args (phi); i++)
- {
- tree phi_arg = gimple_phi_arg_def (phi, i);
- if (!phi_arg)
- {
- *res = false;
- /* Do not need to traverse further. */
- return false;
- }
- }
- return true;
- }
- /* Return 1 if all phi nodes created for bounds have their
- arguments computed. */
- static bool
- chkp_may_finish_incomplete_bounds (void)
- {
- bool res = true;
- chkp_incomplete_bounds_map
- ->traverse<bool *, chkp_may_complete_phi_bounds> (&res);
- return res;
- }
- /* Helper function for chkp_finish_incomplete_bounds.
- Recompute args for bounds phi node. */
- bool
- chkp_recompute_phi_bounds (tree const &bounds, tree *slot,
- void *res ATTRIBUTE_UNUSED)
- {
- tree ptr = *slot;
- gphi *bounds_phi;
- gphi *ptr_phi;
- unsigned i;
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
- gcc_assert (TREE_CODE (ptr) == SSA_NAME);
- bounds_phi = as_a <gphi *> (SSA_NAME_DEF_STMT (bounds));
- ptr_phi = as_a <gphi *> (SSA_NAME_DEF_STMT (ptr));
- for (i = 0; i < gimple_phi_num_args (bounds_phi); i++)
- {
- tree ptr_arg = gimple_phi_arg_def (ptr_phi, i);
- tree bound_arg = chkp_find_bounds (ptr_arg, NULL);
- add_phi_arg (bounds_phi, bound_arg,
- gimple_phi_arg_edge (ptr_phi, i),
- UNKNOWN_LOCATION);
- }
- return true;
- }
- /* Mark BOUNDS as invalid. */
- static void
- chkp_mark_invalid_bounds (tree bounds)
- {
- chkp_invalid_bounds->add (bounds);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Marked bounds ");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, " as invalid\n");
- }
- }
- /* Return 1 if BOUNDS were marked as invalid and 0 otherwise. */
- static bool
- chkp_valid_bounds (tree bounds)
- {
- if (bounds == zero_bounds || bounds == none_bounds)
- return false;
- return !chkp_invalid_bounds->contains (bounds);
- }
- /* Helper function for chkp_finish_incomplete_bounds.
- Check all arguments of phi nodes trying to find
- valid completed bounds. If there is at least one
- such arg then bounds produced by phi node are marked
- as valid completed bounds and all phi args are
- recomputed. */
- bool
- chkp_find_valid_phi_bounds (tree const &bounds, tree *slot, bool *res)
- {
- gimple phi;
- unsigned i;
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
- if (chkp_completed_bounds (bounds))
- return true;
- phi = SSA_NAME_DEF_STMT (bounds);
- gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI);
- for (i = 0; i < gimple_phi_num_args (phi); i++)
- {
- tree phi_arg = gimple_phi_arg_def (phi, i);
- gcc_assert (phi_arg);
- if (chkp_valid_bounds (phi_arg) && !chkp_incomplete_bounds (phi_arg))
- {
- *res = true;
- chkp_mark_completed_bounds (bounds);
- chkp_recompute_phi_bounds (bounds, slot, NULL);
- return true;
- }
- }
- return true;
- }
- /* Helper function for chkp_finish_incomplete_bounds.
- Marks all incompleted bounds as invalid. */
- bool
- chkp_mark_invalid_bounds_walker (tree const &bounds,
- tree *slot ATTRIBUTE_UNUSED,
- void *res ATTRIBUTE_UNUSED)
- {
- if (!chkp_completed_bounds (bounds))
- {
- chkp_mark_invalid_bounds (bounds);
- chkp_mark_completed_bounds (bounds);
- }
- return true;
- }
- /* When all bound phi nodes have all their args computed
- we have enough info to find valid bounds. We iterate
- through all incompleted bounds searching for valid
- bounds. Found valid bounds are marked as completed
- and all remaining incompleted bounds are recomputed.
- Process continues until no new valid bounds may be
- found. All remained incompleted bounds are marked as
- invalid (i.e. have no valid source of bounds). */
- static void
- chkp_finish_incomplete_bounds (void)
- {
- bool found_valid;
- while (found_valid)
- {
- found_valid = false;
- chkp_incomplete_bounds_map->
- traverse<bool *, chkp_find_valid_phi_bounds> (&found_valid);
- if (found_valid)
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_recompute_phi_bounds> (NULL);
- }
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_mark_invalid_bounds_walker> (NULL);
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_recompute_phi_bounds> (NULL);
- chkp_erase_completed_bounds ();
- chkp_erase_incomplete_bounds ();
- }
- /* Return 1 if type TYPE is a pointer type or a
- structure having a pointer type as one of its fields.
- Otherwise return 0. */
- bool
- chkp_type_has_pointer (const_tree type)
- {
- bool res = false;
- if (BOUNDED_TYPE_P (type))
- res = true;
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- res = res || chkp_type_has_pointer (TREE_TYPE (field));
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- res = chkp_type_has_pointer (TREE_TYPE (type));
- return res;
- }
- unsigned
- chkp_type_bounds_count (const_tree type)
- {
- unsigned res = 0;
- if (!type)
- res = 0;
- else if (BOUNDED_TYPE_P (type))
- res = 1;
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- bitmap have_bound;
- bitmap_obstack_initialize (NULL);
- have_bound = BITMAP_ALLOC (NULL);
- chkp_find_bound_slots (type, have_bound);
- res = bitmap_count_bits (have_bound);
- BITMAP_FREE (have_bound);
- bitmap_obstack_release (NULL);
- }
- return res;
- }
- /* Get bounds associated with NODE via
- chkp_set_bounds call. */
- tree
- chkp_get_bounds (tree node)
- {
- tree *slot;
- if (!chkp_bounds_map)
- return NULL_TREE;
- slot = chkp_bounds_map->get (node);
- return slot ? *slot : NULL_TREE;
- }
- /* Associate bounds VAL with NODE. */
- void
- chkp_set_bounds (tree node, tree val)
- {
- if (!chkp_bounds_map)
- chkp_bounds_map = new hash_map<tree, tree>;
- chkp_bounds_map->put (node, val);
- }
- /* Check if statically initialized variable VAR require
- static bounds initialization. If VAR is added into
- bounds initlization list then 1 is returned. Otherwise
- return 0. */
- extern bool
- chkp_register_var_initializer (tree var)
- {
- if (!flag_check_pointer_bounds
- || DECL_INITIAL (var) == error_mark_node)
- return false;
- gcc_assert (TREE_CODE (var) == VAR_DECL);
- gcc_assert (DECL_INITIAL (var));
- if (TREE_STATIC (var)
- && chkp_type_has_pointer (TREE_TYPE (var)))
- {
- varpool_node::get_create (var)->need_bounds_init = 1;
- return true;
- }
- return false;
- }
- /* Helper function for chkp_finish_file.
- Add new modification statement (RHS is assigned to LHS)
- into list of static initializer statementes (passed in ARG).
- If statements list becomes too big, emit checker constructor
- and start the new one. */
- static void
- chkp_add_modification_to_stmt_list (tree lhs,
- tree rhs,
- void *arg)
- {
- struct chkp_ctor_stmt_list *stmts = (struct chkp_ctor_stmt_list *)arg;
- tree modify;
- if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
- rhs = build1 (CONVERT_EXPR, TREE_TYPE (lhs), rhs);
- modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
- append_to_statement_list (modify, &stmts->stmts);
- stmts->avail--;
- }
- /* Build and return ADDR_EXPR for specified object OBJ. */
- static tree
- chkp_build_addr_expr (tree obj)
- {
- return TREE_CODE (obj) == TARGET_MEM_REF
- ? tree_mem_ref_addr (ptr_type_node, obj)
- : build_fold_addr_expr (obj);
- }
- /* Helper function for chkp_finish_file.
- Initialize bound variable BND_VAR with bounds of variable
- VAR to statements list STMTS. If statements list becomes
- too big, emit checker constructor and start the new one. */
- static void
- chkp_output_static_bounds (tree bnd_var, tree var,
- struct chkp_ctor_stmt_list *stmts)
- {
- tree lb, ub, size;
- if (TREE_CODE (var) == STRING_CST)
- {
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- size = build_int_cst (size_type_node, TREE_STRING_LENGTH (var) - 1);
- }
- else if (DECL_SIZE (var)
- && !chkp_variable_size_type (TREE_TYPE (var)))
- {
- /* Compute bounds using statically known size. */
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- size = size_binop (MINUS_EXPR, DECL_SIZE_UNIT (var), size_one_node);
- }
- else
- {
- /* Compute bounds using dynamic size. */
- tree call;
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- call = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_sizeof_fndecl)),
- chkp_sizeof_fndecl);
- size = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_sizeof_fndecl)),
- call, 1, var);
- if (flag_chkp_zero_dynamic_size_as_infinite)
- {
- tree max_size, cond;
- max_size = build2 (MINUS_EXPR, size_type_node, size_zero_node, lb);
- cond = build2 (NE_EXPR, boolean_type_node, size, size_zero_node);
- size = build3 (COND_EXPR, size_type_node, cond, size, max_size);
- }
- size = size_binop (MINUS_EXPR, size, size_one_node);
- }
- ub = size_binop (PLUS_EXPR, lb, size);
- stmts->avail -= targetm.chkp_initialize_bounds (bnd_var, lb, ub,
- &stmts->stmts);
- if (stmts->avail <= 0)
- {
- cgraph_build_static_cdtor ('B', stmts->stmts,
- MAX_RESERVED_INIT_PRIORITY + 2);
- stmts->avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts->stmts = NULL;
- }
- }
- /* Return entry block to be used for checker initilization code.
- Create new block if required. */
- static basic_block
- chkp_get_entry_block (void)
- {
- if (!entry_block)
- entry_block = split_block (ENTRY_BLOCK_PTR_FOR_FN (cfun), NULL)->dest;
- return entry_block;
- }
- /* Return a bounds var to be used for pointer var PTR_VAR. */
- static tree
- chkp_get_bounds_var (tree ptr_var)
- {
- tree bnd_var;
- tree *slot;
- slot = chkp_bound_vars->get (ptr_var);
- if (slot)
- bnd_var = *slot;
- else
- {
- bnd_var = create_tmp_reg (pointer_bounds_type_node,
- CHKP_BOUND_TMP_NAME);
- chkp_bound_vars->put (ptr_var, bnd_var);
- }
- return bnd_var;
- }
- /* Register bounds BND for object PTR in global bounds table.
- A copy of bounds may be created for abnormal ssa names.
- Returns bounds to use for PTR. */
- static tree
- chkp_maybe_copy_and_register_bounds (tree ptr, tree bnd)
- {
- bool abnormal_ptr;
- if (!chkp_reg_bounds)
- return bnd;
- /* Do nothing if bounds are incomplete_bounds
- because it means bounds will be recomputed. */
- if (bnd == incomplete_bounds)
- return bnd;
- abnormal_ptr = (TREE_CODE (ptr) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
- && gimple_code (SSA_NAME_DEF_STMT (ptr)) != GIMPLE_PHI);
- /* A single bounds value may be reused multiple times for
- different pointer values. It may cause coalescing issues
- for abnormal SSA names. To avoid it we create a bounds
- copy in case it is computed for abnormal SSA name.
- We also cannot reuse such created copies for other pointers */
- if (abnormal_ptr
- || bitmap_bit_p (chkp_abnormal_copies, SSA_NAME_VERSION (bnd)))
- {
- tree bnd_var = NULL_TREE;
- if (abnormal_ptr)
- {
- if (SSA_NAME_VAR (ptr))
- bnd_var = chkp_get_bounds_var (SSA_NAME_VAR (ptr));
- }
- else
- bnd_var = chkp_get_tmp_var ();
- /* For abnormal copies we may just find original
- bounds and use them. */
- if (!abnormal_ptr && !SSA_NAME_IS_DEFAULT_DEF (bnd))
- {
- gimple bnd_def = SSA_NAME_DEF_STMT (bnd);
- gcc_checking_assert (gimple_code (bnd_def) == GIMPLE_ASSIGN);
- bnd = gimple_assign_rhs1 (bnd_def);
- }
- /* For undefined values we usually use none bounds
- value but in case of abnormal edge it may cause
- coalescing failures. Use default definition of
- bounds variable instead to avoid it. */
- else if (SSA_NAME_IS_DEFAULT_DEF (ptr)
- && TREE_CODE (SSA_NAME_VAR (ptr)) != PARM_DECL)
- {
- bnd = get_or_create_ssa_default_def (cfun, bnd_var);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Using default def bounds ");
- print_generic_expr (dump_file, bnd, 0);
- fprintf (dump_file, " for abnormal default def SSA name ");
- print_generic_expr (dump_file, ptr, 0);
- fprintf (dump_file, "\n");
- }
- }
- else
- {
- tree copy;
- gimple def = SSA_NAME_DEF_STMT (ptr);
- gimple assign;
- gimple_stmt_iterator gsi;
- if (bnd_var)
- copy = make_ssa_name (bnd_var, gimple_build_nop ());
- else
- copy = make_temp_ssa_name (pointer_bounds_type_node,
- gimple_build_nop (),
- CHKP_BOUND_TMP_NAME);
- assign = gimple_build_assign (copy, bnd);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Creating a copy of bounds ");
- print_generic_expr (dump_file, bnd, 0);
- fprintf (dump_file, " for abnormal SSA name ");
- print_generic_expr (dump_file, ptr, 0);
- fprintf (dump_file, "\n");
- }
- if (gimple_code (def) == GIMPLE_NOP)
- {
- gsi = gsi_last_bb (chkp_get_entry_block ());
- if (!gsi_end_p (gsi) && is_ctrl_stmt (gsi_stmt (gsi)))
- gsi_insert_before (&gsi, assign, GSI_CONTINUE_LINKING);
- else
- gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING);
- }
- else
- {
- gimple bnd_def = SSA_NAME_DEF_STMT (bnd);
- /* Sometimes (e.g. when we load a pointer from a
- memory) bounds are produced later than a pointer.
- We need to insert bounds copy appropriately. */
- if (gimple_code (bnd_def) != GIMPLE_NOP
- && stmt_dominates_stmt_p (def, bnd_def))
- gsi = gsi_for_stmt (bnd_def);
- else
- gsi = gsi_for_stmt (def);
- gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING);
- }
- bnd = copy;
- }
- if (abnormal_ptr)
- bitmap_set_bit (chkp_abnormal_copies, SSA_NAME_VERSION (bnd));
- }
- chkp_reg_bounds->put (ptr, bnd);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered bound ");
- print_generic_expr (dump_file, bnd, 0);
- fprintf (dump_file, " for pointer ");
- print_generic_expr (dump_file, ptr, 0);
- fprintf (dump_file, "\n");
- }
- return bnd;
- }
- /* Get bounds registered for object PTR in global bounds table. */
- static tree
- chkp_get_registered_bounds (tree ptr)
- {
- tree *slot;
- if (!chkp_reg_bounds)
- return NULL_TREE;
- slot = chkp_reg_bounds->get (ptr);
- return slot ? *slot : NULL_TREE;
- }
- /* Add bound retvals to return statement pointed by GSI. */
- static void
- chkp_add_bounds_to_ret_stmt (gimple_stmt_iterator *gsi)
- {
- greturn *ret = as_a <greturn *> (gsi_stmt (*gsi));
- tree retval = gimple_return_retval (ret);
- tree ret_decl = DECL_RESULT (cfun->decl);
- tree bounds;
- if (!retval)
- return;
- if (BOUNDED_P (ret_decl))
- {
- bounds = chkp_find_bounds (retval, gsi);
- bounds = chkp_maybe_copy_and_register_bounds (ret_decl, bounds);
- gimple_return_set_retbnd (ret, bounds);
- }
- update_stmt (ret);
- }
- /* Force OP to be suitable for using as an argument for call.
- New statements (if any) go to SEQ. */
- static tree
- chkp_force_gimple_call_op (tree op, gimple_seq *seq)
- {
- gimple_seq stmts;
- gimple_stmt_iterator si;
- op = force_gimple_operand (unshare_expr (op), &stmts, true, NULL_TREE);
- for (si = gsi_start (stmts); !gsi_end_p (si); gsi_next (&si))
- chkp_mark_stmt (gsi_stmt (si));
- gimple_seq_add_seq (seq, stmts);
- return op;
- }
- /* Generate lower bound check for memory access by ADDR.
- Check is inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
- static void
- chkp_check_lower (tree addr, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
- {
- gimple_seq seq;
- gimple check;
- tree node;
- if (!chkp_function_instrumented_p (current_function_decl)
- && bounds == chkp_get_zero_bounds ())
- return;
- if (dirflag == integer_zero_node
- && !flag_chkp_check_read)
- return;
- if (dirflag == integer_one_node
- && !flag_chkp_check_write)
- return;
- seq = NULL;
- node = chkp_force_gimple_call_op (addr, &seq);
- check = gimple_build_call (chkp_checkl_fndecl, 2, node, bounds);
- chkp_mark_stmt (check);
- gimple_call_set_with_bounds (check, true);
- gimple_set_location (check, location);
- gimple_seq_add_stmt (&seq, check);
- gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- gimple before = gsi_stmt (iter);
- fprintf (dump_file, "Generated lower bound check for statement ");
- print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " ");
- print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- }
- /* Generate upper bound check for memory access by ADDR.
- Check is inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
- static void
- chkp_check_upper (tree addr, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
- {
- gimple_seq seq;
- gimple check;
- tree node;
- if (!chkp_function_instrumented_p (current_function_decl)
- && bounds == chkp_get_zero_bounds ())
- return;
- if (dirflag == integer_zero_node
- && !flag_chkp_check_read)
- return;
- if (dirflag == integer_one_node
- && !flag_chkp_check_write)
- return;
- seq = NULL;
- node = chkp_force_gimple_call_op (addr, &seq);
- check = gimple_build_call (chkp_checku_fndecl, 2, node, bounds);
- chkp_mark_stmt (check);
- gimple_call_set_with_bounds (check, true);
- gimple_set_location (check, location);
- gimple_seq_add_stmt (&seq, check);
- gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- gimple before = gsi_stmt (iter);
- fprintf (dump_file, "Generated upper bound check for statement ");
- print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " ");
- print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- }
- /* Generate lower and upper bound checks for memory access
- to memory slot [FIRST, LAST] againsr BOUNDS. Checks
- are inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
- void
- chkp_check_mem_access (tree first, tree last, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
- {
- chkp_check_lower (first, bounds, iter, location, dirflag);
- chkp_check_upper (last, bounds, iter, location, dirflag);
- }
- /* Replace call to _bnd_chk_* pointed by GSI with
- bndcu and bndcl calls. DIRFLAG determines whether
- check is for read or write. */
- void
- chkp_replace_address_check_builtin (gimple_stmt_iterator *gsi,
- tree dirflag)
- {
- gimple_stmt_iterator call_iter = *gsi;
- gimple call = gsi_stmt (*gsi);
- tree fndecl = gimple_call_fndecl (call);
- tree addr = gimple_call_arg (call, 0);
- tree bounds = chkp_find_bounds (addr, gsi);
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
- chkp_check_lower (addr, bounds, *gsi, gimple_location (call), dirflag);
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS)
- chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
- {
- tree size = gimple_call_arg (call, 1);
- addr = fold_build_pointer_plus (addr, size);
- addr = fold_build_pointer_plus_hwi (addr, -1);
- chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
- }
- gsi_remove (&call_iter, true);
- }
- /* Replace call to _bnd_get_ptr_* pointed by GSI with
- corresponding bounds extract call. */
- void
- chkp_replace_extract_builtin (gimple_stmt_iterator *gsi)
- {
- gimple call = gsi_stmt (*gsi);
- tree fndecl = gimple_call_fndecl (call);
- tree addr = gimple_call_arg (call, 0);
- tree bounds = chkp_find_bounds (addr, gsi);
- gimple extract;
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND)
- fndecl = chkp_extract_lower_fndecl;
- else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND)
- fndecl = chkp_extract_upper_fndecl;
- else
- gcc_unreachable ();
- extract = gimple_build_call (fndecl, 1, bounds);
- gimple_call_set_lhs (extract, gimple_call_lhs (call));
- chkp_mark_stmt (extract);
- gsi_replace (gsi, extract, false);
- }
- /* Return COMPONENT_REF accessing FIELD in OBJ. */
- static tree
- chkp_build_component_ref (tree obj, tree field)
- {
- tree res;
- /* If object is TMR then we do not use component_ref but
- add offset instead. We need it to be able to get addr
- of the reasult later. */
- if (TREE_CODE (obj) == TARGET_MEM_REF)
- {
- tree offs = TMR_OFFSET (obj);
- offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs),
- offs, DECL_FIELD_OFFSET (field));
- gcc_assert (offs);
- res = copy_node (obj);
- TREE_TYPE (res) = TREE_TYPE (field);
- TMR_OFFSET (res) = offs;
- }
- else
- res = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL_TREE);
- return res;
- }
- /* Return ARRAY_REF for array ARR and index IDX with
- specified element type ETYPE and element size ESIZE. */
- static tree
- chkp_build_array_ref (tree arr, tree etype, tree esize,
- unsigned HOST_WIDE_INT idx)
- {
- tree index = build_int_cst (size_type_node, idx);
- tree res;
- /* If object is TMR then we do not use array_ref but
- add offset instead. We need it to be able to get addr
- of the reasult later. */
- if (TREE_CODE (arr) == TARGET_MEM_REF)
- {
- tree offs = TMR_OFFSET (arr);
- esize = fold_binary_to_constant (MULT_EXPR, TREE_TYPE (esize),
- esize, index);
- gcc_assert(esize);
- offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs),
- offs, esize);
- gcc_assert (offs);
- res = copy_node (arr);
- TREE_TYPE (res) = etype;
- TMR_OFFSET (res) = offs;
- }
- else
- res = build4 (ARRAY_REF, etype, arr, index, NULL_TREE, NULL_TREE);
- return res;
- }
- /* Helper function for chkp_add_bounds_to_call_stmt.
- Fill ALL_BOUNDS output array with created bounds.
- OFFS is used for recursive calls and holds basic
- offset of TYPE in outer structure in bits.
- ITER points a position where bounds are searched.
- ALL_BOUNDS[i] is filled with elem bounds if there
- is a field in TYPE which has pointer type and offset
- equal to i * POINTER_SIZE in bits. */
- static void
- chkp_find_bounds_for_elem (tree elem, tree *all_bounds,
- HOST_WIDE_INT offs,
- gimple_stmt_iterator *iter)
- {
- tree type = TREE_TYPE (elem);
- if (BOUNDED_TYPE_P (type))
- {
- if (!all_bounds[offs / POINTER_SIZE])
- {
- tree temp = make_temp_ssa_name (type, gimple_build_nop (), "");
- gimple assign = gimple_build_assign (temp, elem);
- gimple_stmt_iterator gsi;
- gsi_insert_before (iter, assign, GSI_SAME_STMT);
- gsi = gsi_for_stmt (assign);
- all_bounds[offs / POINTER_SIZE] = chkp_find_bounds (temp, &gsi);
- }
- }
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- {
- tree base = unshare_expr (elem);
- tree field_ref = chkp_build_component_ref (base, field);
- HOST_WIDE_INT field_offs
- = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
- if (DECL_FIELD_OFFSET (field))
- field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
- chkp_find_bounds_for_elem (field_ref, all_bounds,
- offs + field_offs, iter);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
- unsigned HOST_WIDE_INT cur;
- if (!maxval || integer_minus_onep (maxval))
- return;
- for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
- {
- tree base = unshare_expr (elem);
- tree arr_elem = chkp_build_array_ref (base, etype,
- TYPE_SIZE (etype),
- cur);
- chkp_find_bounds_for_elem (arr_elem, all_bounds, offs + cur * esize,
- iter);
- }
- }
- }
- /* Fill HAVE_BOUND output bitmap with information about
- bounds requred for object of type TYPE.
- OFFS is used for recursive calls and holds basic
- offset of TYPE in outer structure in bits.
- HAVE_BOUND[i] is set to 1 if there is a field
- in TYPE which has pointer type and offset
- equal to i * POINTER_SIZE - OFFS in bits. */
- void
- chkp_find_bound_slots_1 (const_tree type, bitmap have_bound,
- HOST_WIDE_INT offs)
- {
- if (BOUNDED_TYPE_P (type))
- bitmap_set_bit (have_bound, offs / POINTER_SIZE);
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- {
- HOST_WIDE_INT field_offs
- = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
- if (DECL_FIELD_OFFSET (field))
- field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
- chkp_find_bound_slots_1 (TREE_TYPE (field), have_bound,
- offs + field_offs);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
- unsigned HOST_WIDE_INT cur;
- if (!maxval
- || TREE_CODE (maxval) != INTEGER_CST
- || integer_minus_onep (maxval))
- return;
- for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
- chkp_find_bound_slots_1 (etype, have_bound, offs + cur * esize);
- }
- }
- /* Fill bitmap RES with information about bounds for
- type TYPE. See chkp_find_bound_slots_1 for more
- details. */
- void
- chkp_find_bound_slots (const_tree type, bitmap res)
- {
- bitmap_clear (res);
- chkp_find_bound_slots_1 (type, res, 0);
- }
- /* Return 1 if call to FNDECL should be instrumented
- and 0 otherwise. */
- static bool
- chkp_instrument_normal_builtin (tree fndecl)
- {
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_STRLEN:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STPCPY:
- case BUILT_IN_STPNCPY:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMSET:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_BZERO:
- case BUILT_IN_STRCMP:
- case BUILT_IN_STRNCMP:
- case BUILT_IN_BCMP:
- case BUILT_IN_MEMCMP:
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- case BUILT_IN_STRCAT_CHK:
- case BUILT_IN_STRNCAT_CHK:
- case BUILT_IN_MALLOC:
- case BUILT_IN_CALLOC:
- case BUILT_IN_REALLOC:
- return 1;
- default:
- return 0;
- }
- }
- /* Add bound arguments to call statement pointed by GSI.
- Also performs a replacement of user checker builtins calls
- with internal ones. */
- static void
- chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
- {
- gcall *call = as_a <gcall *> (gsi_stmt (*gsi));
- unsigned arg_no = 0;
- tree fndecl = gimple_call_fndecl (call);
- tree fntype;
- tree first_formal_arg;
- tree arg;
- bool use_fntype = false;
- tree op;
- ssa_op_iter iter;
- gcall *new_call;
- /* Do nothing for internal functions. */
- if (gimple_call_internal_p (call))
- return;
- fntype = TREE_TYPE (TREE_TYPE (gimple_call_fn (call)));
- /* Do nothing if back-end builtin is called. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return;
- /* Do nothing for some middle-end builtins. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE)
- return;
- /* Do nothing for calls to not instrumentable functions. */
- if (fndecl && !chkp_instrumentable_p (fndecl))
- return;
- /* Ignore CHKP_INIT_PTR_BOUNDS, CHKP_NULL_PTR_BOUNDS
- and CHKP_COPY_PTR_BOUNDS. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS))
- return;
- /* Check user builtins are replaced with checks. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS))
- {
- chkp_replace_address_check_builtin (gsi, integer_minus_one_node);
- return;
- }
- /* Check user builtins are replaced with bound extract. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND))
- {
- chkp_replace_extract_builtin (gsi);
- return;
- }
- /* BUILT_IN_CHKP_NARROW_PTR_BOUNDS call is replaced with
- target narrow bounds call. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
- {
- tree arg = gimple_call_arg (call, 1);
- tree bounds = chkp_find_bounds (arg, gsi);
- gimple_call_set_fndecl (call, chkp_narrow_bounds_fndecl);
- gimple_call_set_arg (call, 1, bounds);
- update_stmt (call);
- return;
- }
- /* BUILT_IN_CHKP_STORE_PTR_BOUNDS call is replaced with
- bndstx call. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_STORE_PTR_BOUNDS)
- {
- tree addr = gimple_call_arg (call, 0);
- tree ptr = gimple_call_arg (call, 1);
- tree bounds = chkp_find_bounds (ptr, gsi);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- chkp_build_bndstx (addr, ptr, bounds, gsi);
- gsi_remove (&iter, true);
- return;
- }
- if (!flag_chkp_instrument_calls)
- return;
- /* We instrument only some subset of builtins. We also instrument
- builtin calls to be inlined. */
- if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && !chkp_instrument_normal_builtin (fndecl))
- {
- if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
- return;
- struct cgraph_node *clone = chkp_maybe_create_clone (fndecl);
- if (!clone
- || !gimple_has_body_p (clone->decl))
- return;
- }
- /* If function decl is available then use it for
- formal arguments list. Otherwise use function type. */
- if (fndecl && DECL_ARGUMENTS (fndecl))
- first_formal_arg = DECL_ARGUMENTS (fndecl);
- else
- {
- first_formal_arg = TYPE_ARG_TYPES (fntype);
- use_fntype = true;
- }
- /* Fill vector of new call args. */
- vec<tree> new_args = vNULL;
- new_args.create (gimple_call_num_args (call));
- arg = first_formal_arg;
- for (arg_no = 0; arg_no < gimple_call_num_args (call); arg_no++)
- {
- tree call_arg = gimple_call_arg (call, arg_no);
- tree type;
- /* Get arg type using formal argument description
- or actual argument type. */
- if (arg)
- if (use_fntype)
- if (TREE_VALUE (arg) != void_type_node)
- {
- type = TREE_VALUE (arg);
- arg = TREE_CHAIN (arg);
- }
- else
- type = TREE_TYPE (call_arg);
- else
- {
- type = TREE_TYPE (arg);
- arg = TREE_CHAIN (arg);
- }
- else
- type = TREE_TYPE (call_arg);
- new_args.safe_push (call_arg);
- if (BOUNDED_TYPE_P (type)
- || pass_by_reference (NULL, TYPE_MODE (type), type, true))
- new_args.safe_push (chkp_find_bounds (call_arg, gsi));
- else if (chkp_type_has_pointer (type))
- {
- HOST_WIDE_INT max_bounds
- = TREE_INT_CST_LOW (TYPE_SIZE (type)) / POINTER_SIZE;
- tree *all_bounds = (tree *)xmalloc (sizeof (tree) * max_bounds);
- HOST_WIDE_INT bnd_no;
- memset (all_bounds, 0, sizeof (tree) * max_bounds);
- chkp_find_bounds_for_elem (call_arg, all_bounds, 0, gsi);
- for (bnd_no = 0; bnd_no < max_bounds; bnd_no++)
- if (all_bounds[bnd_no])
- new_args.safe_push (all_bounds[bnd_no]);
- free (all_bounds);
- }
- }
- if (new_args.length () == gimple_call_num_args (call))
- new_call = call;
- else
- {
- new_call = gimple_build_call_vec (gimple_op (call, 1), new_args);
- gimple_call_set_lhs (new_call, gimple_call_lhs (call));
- gimple_call_copy_flags (new_call, call);
- gimple_call_set_chain (new_call, gimple_call_chain (call));
- }
- new_args.release ();
- /* For direct calls fndecl is replaced with instrumented version. */
- if (fndecl)
- {
- tree new_decl = chkp_maybe_create_clone (fndecl)->decl;
- gimple_call_set_fndecl (new_call, new_decl);
- gimple_call_set_fntype (new_call, TREE_TYPE (new_decl));
- }
- /* For indirect call we should fix function pointer type if
- pass some bounds. */
- else if (new_call != call)
- {
- tree type = gimple_call_fntype (call);
- type = chkp_copy_function_type_adding_bounds (type);
- gimple_call_set_fntype (new_call, type);
- }
- /* replace old call statement with the new one. */
- if (call != new_call)
- {
- FOR_EACH_SSA_TREE_OPERAND (op, call, iter, SSA_OP_ALL_DEFS)
- {
- SSA_NAME_DEF_STMT (op) = new_call;
- }
- gsi_replace (gsi, new_call, true);
- }
- else
- update_stmt (new_call);
- gimple_call_set_with_bounds (new_call, true);
- }
- /* Return constant static bounds var with specified bounds LB and UB.
- If such var does not exists then new var is created with specified NAME. */
- static tree
- chkp_make_static_const_bounds (HOST_WIDE_INT lb,
- HOST_WIDE_INT ub,
- const char *name)
- {
- tree id = get_identifier (name);
- tree var;
- varpool_node *node;
- symtab_node *snode;
- var = build_decl (UNKNOWN_LOCATION, VAR_DECL, id,
- pointer_bounds_type_node);
- TREE_STATIC (var) = 1;
- TREE_PUBLIC (var) = 1;
- /* With LTO we may have constant bounds already in varpool.
- Try to find it. */
- if ((snode = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (var))))
- {
- /* We don't allow this symbol usage for non bounds. */
- if (snode->type != SYMTAB_VARIABLE
- || !POINTER_BOUNDS_P (snode->decl))
- sorry ("-fcheck-pointer-bounds requires '%s' "
- "name for internal usage",
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (var)));
- return snode->decl;
- }
- TREE_USED (var) = 1;
- TREE_READONLY (var) = 1;
- TREE_ADDRESSABLE (var) = 0;
- DECL_ARTIFICIAL (var) = 1;
- DECL_READ_P (var) = 1;
- DECL_INITIAL (var) = targetm.chkp_make_bounds_constant (lb, ub);
- make_decl_one_only (var, DECL_ASSEMBLER_NAME (var));
- /* We may use this symbol during ctors generation in chkp_finish_file
- when all symbols are emitted. Force output to avoid undefined
- symbols in ctors. */
- node = varpool_node::get_create (var);
- node->force_output = 1;
- varpool_node::finalize_decl (var);
- return var;
- }
- /* Generate code to make bounds with specified lower bound LB and SIZE.
- if AFTER is 1 then code is inserted after position pointed by ITER
- otherwise code is inserted before position pointed by ITER.
- If ITER is NULL then code is added to entry block. */
- static tree
- chkp_make_bounds (tree lb, tree size, gimple_stmt_iterator *iter, bool after)
- {
- gimple_seq seq;
- gimple_stmt_iterator gsi;
- gimple stmt;
- tree bounds;
- if (iter)
- gsi = *iter;
- else
- gsi = gsi_start_bb (chkp_get_entry_block ());
- seq = NULL;
- lb = chkp_force_gimple_call_op (lb, &seq);
- size = chkp_force_gimple_call_op (size, &seq);
- stmt = gimple_build_call (chkp_bndmk_fndecl, 2, lb, size);
- chkp_mark_stmt (stmt);
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
- gimple_seq_add_stmt (&seq, stmt);
- if (iter && after)
- gsi_insert_seq_after (&gsi, seq, GSI_SAME_STMT);
- else
- gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Made bounds: ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- if (iter)
- {
- fprintf (dump_file, " inserted before statement: ");
- print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, TDF_VOPS|TDF_MEMSYMS);
- }
- else
- fprintf (dump_file, " at function entry\n");
- }
- /* update_stmt (stmt); */
- return bounds;
- }
- /* Return var holding zero bounds. */
- tree
- chkp_get_zero_bounds_var (void)
- {
- if (!chkp_zero_bounds_var)
- chkp_zero_bounds_var
- = chkp_make_static_const_bounds (0, -1,
- CHKP_ZERO_BOUNDS_VAR_NAME);
- return chkp_zero_bounds_var;
- }
- /* Return var holding none bounds. */
- tree
- chkp_get_none_bounds_var (void)
- {
- if (!chkp_none_bounds_var)
- chkp_none_bounds_var
- = chkp_make_static_const_bounds (-1, 0,
- CHKP_NONE_BOUNDS_VAR_NAME);
- return chkp_none_bounds_var;
- }
- /* Return SSA_NAME used to represent zero bounds. */
- static tree
- chkp_get_zero_bounds (void)
- {
- if (zero_bounds)
- return zero_bounds;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Creating zero bounds...");
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple stmt;
- zero_bounds = chkp_get_tmp_reg (gimple_build_nop ());
- stmt = gimple_build_assign (zero_bounds, chkp_get_zero_bounds_var ());
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- zero_bounds = chkp_make_bounds (integer_zero_node,
- integer_zero_node,
- NULL,
- false);
- return zero_bounds;
- }
- /* Return SSA_NAME used to represent none bounds. */
- static tree
- chkp_get_none_bounds (void)
- {
- if (none_bounds)
- return none_bounds;
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Creating none bounds...");
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple stmt;
- none_bounds = chkp_get_tmp_reg (gimple_build_nop ());
- stmt = gimple_build_assign (none_bounds, chkp_get_none_bounds_var ());
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- none_bounds = chkp_make_bounds (integer_minus_one_node,
- build_int_cst (size_type_node, 2),
- NULL,
- false);
- return none_bounds;
- }
- /* Return bounds to be used as a result of operation which
- should not create poiunter (e.g. MULT_EXPR). */
- static tree
- chkp_get_invalid_op_bounds (void)
- {
- return chkp_get_zero_bounds ();
- }
- /* Return bounds to be used for loads of non-pointer values. */
- static tree
- chkp_get_nonpointer_load_bounds (void)
- {
- return chkp_get_zero_bounds ();
- }
- /* Return 1 if may use bndret call to get bounds for pointer
- returned by CALL. */
- static bool
- chkp_call_returns_bounds_p (gcall *call)
- {
- if (gimple_call_internal_p (call))
- return false;
- if (gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
- || chkp_gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW))
- return true;
- if (gimple_call_with_bounds_p (call))
- return true;
- tree fndecl = gimple_call_fndecl (call);
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return false;
- if (fndecl && !chkp_instrumentable_p (fndecl))
- return false;
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
- {
- if (chkp_instrument_normal_builtin (fndecl))
- return true;
- if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
- return false;
- struct cgraph_node *clone = chkp_maybe_create_clone (fndecl);
- return (clone && gimple_has_body_p (clone->decl));
- }
- return true;
- }
- /* Build bounds returned by CALL. */
- static tree
- chkp_build_returned_bound (gcall *call)
- {
- gimple_stmt_iterator gsi;
- tree bounds;
- gimple stmt;
- tree fndecl = gimple_call_fndecl (call);
- unsigned int retflags;
- /* To avoid fixing alloca expands in targets we handle
- it separately. */
- if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN))
- {
- tree size = gimple_call_arg (call, 0);
- tree lb = gimple_call_lhs (call);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_make_bounds (lb, size, &iter, true);
- }
- /* We know bounds returned by set_bounds builtin call. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS)
- {
- tree lb = gimple_call_arg (call, 0);
- tree size = gimple_call_arg (call, 1);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_make_bounds (lb, size, &iter, true);
- }
- /* Detect bounds initialization calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS)
- bounds = chkp_get_zero_bounds ();
- /* Detect bounds nullification calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS)
- bounds = chkp_get_none_bounds ();
- /* Detect bounds copy calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS)
- {
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_find_bounds (gimple_call_arg (call, 1), &iter);
- }
- /* Do not use retbnd when returned bounds are equal to some
- of passed bounds. */
- else if (((retflags = gimple_call_return_flags (call)) & ERF_RETURNS_ARG)
- && (retflags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (call))
- {
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- unsigned int retarg = retflags & ERF_RETURN_ARG_MASK, argno;
- if (gimple_call_with_bounds_p (call))
- {
- for (argno = 0; argno < gimple_call_num_args (call); argno++)
- if (!POINTER_BOUNDS_P (gimple_call_arg (call, argno)))
- {
- if (retarg)
- retarg--;
- else
- break;
- }
- }
- else
- argno = retarg;
- bounds = chkp_find_bounds (gimple_call_arg (call, argno), &iter);
- }
- else if (chkp_call_returns_bounds_p (call))
- {
- gcc_assert (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME);
- /* In general case build checker builtin call to
- obtain returned bounds. */
- stmt = gimple_build_call (chkp_ret_bnd_fndecl, 1,
- gimple_call_lhs (call));
- chkp_mark_stmt (stmt);
- gsi = gsi_for_stmt (call);
- gsi_insert_after (&gsi, stmt, GSI_SAME_STMT);
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
- update_stmt (stmt);
- }
- else
- bounds = chkp_get_zero_bounds ();
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Built returned bounds (");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, ") for call: ");
- print_gimple_stmt (dump_file, call, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- bounds = chkp_maybe_copy_and_register_bounds (gimple_call_lhs (call), bounds);
- return bounds;
- }
- /* Return bounds used as returned by call
- which produced SSA name VAL. */
- gcall *
- chkp_retbnd_call_by_val (tree val)
- {
- if (TREE_CODE (val) != SSA_NAME)
- return NULL;
- gcc_assert (gimple_code (SSA_NAME_DEF_STMT (val)) == GIMPLE_CALL);
- imm_use_iterator use_iter;
- use_operand_p use_p;
- FOR_EACH_IMM_USE_FAST (use_p, use_iter, val)
- if (gimple_code (USE_STMT (use_p)) == GIMPLE_CALL
- && gimple_call_fndecl (USE_STMT (use_p)) == chkp_ret_bnd_fndecl)
- return as_a <gcall *> (USE_STMT (use_p));
- return NULL;
- }
- /* Check the next parameter for the given PARM is bounds
- and return it's default SSA_NAME (create if required). */
- static tree
- chkp_get_next_bounds_parm (tree parm)
- {
- tree bounds = TREE_CHAIN (parm);
- gcc_assert (POINTER_BOUNDS_P (bounds));
- bounds = ssa_default_def (cfun, bounds);
- if (!bounds)
- {
- bounds = make_ssa_name (TREE_CHAIN (parm), gimple_build_nop ());
- set_ssa_default_def (cfun, TREE_CHAIN (parm), bounds);
- }
- return bounds;
- }
- /* Return bounds to be used for input argument PARM. */
- static tree
- chkp_get_bound_for_parm (tree parm)
- {
- tree decl = SSA_NAME_VAR (parm);
- tree bounds;
- gcc_assert (TREE_CODE (decl) == PARM_DECL);
- bounds = chkp_get_registered_bounds (parm);
- if (!bounds)
- bounds = chkp_get_registered_bounds (decl);
- if (!bounds)
- {
- tree orig_decl = cgraph_node::get (cfun->decl)->orig_decl;
- /* For static chain param we return zero bounds
- because currently we do not check dereferences
- of this pointer. */
- if (cfun->static_chain_decl == decl)
- bounds = chkp_get_zero_bounds ();
- /* If non instrumented runtime is used then it may be useful
- to use zero bounds for input arguments of main
- function. */
- else if (flag_chkp_zero_input_bounds_for_main
- && strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (orig_decl)),
- "main") == 0)
- bounds = chkp_get_zero_bounds ();
- else if (BOUNDED_P (parm))
- {
- bounds = chkp_get_next_bounds_parm (decl);
- bounds = chkp_maybe_copy_and_register_bounds (decl, bounds);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Built arg bounds (");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, ") for arg: ");
- print_node (dump_file, "", decl, 0);
- }
- }
- else
- bounds = chkp_get_zero_bounds ();
- }
- if (!chkp_get_registered_bounds (parm))
- bounds = chkp_maybe_copy_and_register_bounds (parm, bounds);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Using bounds ");
- print_generic_expr (dump_file, bounds, 0);
- fprintf (dump_file, " for parm ");
- print_generic_expr (dump_file, parm, 0);
- fprintf (dump_file, " of type ");
- print_generic_expr (dump_file, TREE_TYPE (parm), 0);
- fprintf (dump_file, ".\n");
- }
- return bounds;
- }
- /* Build and return CALL_EXPR for bndstx builtin with specified
- arguments. */
- tree
- chkp_build_bndldx_call (tree addr, tree ptr)
- {
- tree fn = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndldx_fndecl)),
- chkp_bndldx_fndecl);
- tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndldx_fndecl)),
- fn, 2, addr, ptr);
- CALL_WITH_BOUNDS_P (call) = true;
- return call;
- }
- /* Insert code to load bounds for PTR located by ADDR.
- Code is inserted after position pointed by GSI.
- Loaded bounds are returned. */
- static tree
- chkp_build_bndldx (tree addr, tree ptr, gimple_stmt_iterator *gsi)
- {
- gimple_seq seq;
- gimple stmt;
- tree bounds;
- seq = NULL;
- addr = chkp_force_gimple_call_op (addr, &seq);
- ptr = chkp_force_gimple_call_op (ptr, &seq);
- stmt = gimple_build_call (chkp_bndldx_fndecl, 2, addr, ptr);
- chkp_mark_stmt (stmt);
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
- gimple_seq_add_stmt (&seq, stmt);
- gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generated bndldx for pointer ");
- print_generic_expr (dump_file, ptr, 0);
- fprintf (dump_file, ": ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- return bounds;
- }
- /* Build and return CALL_EXPR for bndstx builtin with specified
- arguments. */
- tree
- chkp_build_bndstx_call (tree addr, tree ptr, tree bounds)
- {
- tree fn = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndstx_fndecl)),
- chkp_bndstx_fndecl);
- tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndstx_fndecl)),
- fn, 3, ptr, bounds, addr);
- CALL_WITH_BOUNDS_P (call) = true;
- return call;
- }
- /* Insert code to store BOUNDS for PTR stored by ADDR.
- New statements are inserted after position pointed
- by GSI. */
- void
- chkp_build_bndstx (tree addr, tree ptr, tree bounds,
- gimple_stmt_iterator *gsi)
- {
- gimple_seq seq;
- gimple stmt;
- seq = NULL;
- addr = chkp_force_gimple_call_op (addr, &seq);
- ptr = chkp_force_gimple_call_op (ptr, &seq);
- stmt = gimple_build_call (chkp_bndstx_fndecl, 3, ptr, bounds, addr);
- chkp_mark_stmt (stmt);
- gimple_call_set_with_bounds (stmt, true);
- gimple_seq_add_stmt (&seq, stmt);
- gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generated bndstx for pointer store ");
- print_gimple_stmt (dump_file, gsi_stmt (*gsi), 0, TDF_VOPS|TDF_MEMSYMS);
- print_gimple_stmt (dump_file, stmt, 2, TDF_VOPS|TDF_MEMSYMS);
- }
- }
- /* Compute bounds for pointer NODE which was assigned in
- assignment statement ASSIGN. Return computed bounds. */
- static tree
- chkp_compute_bounds_for_assignment (tree node, gimple assign)
- {
- enum tree_code rhs_code = gimple_assign_rhs_code (assign);
- tree rhs1 = gimple_assign_rhs1 (assign);
- tree bounds = NULL_TREE;
- gimple_stmt_iterator iter = gsi_for_stmt (assign);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Computing bounds for assignment: ");
- print_gimple_stmt (dump_file, assign, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- switch (rhs_code)
- {
- case MEM_REF:
- case TARGET_MEM_REF:
- case COMPONENT_REF:
- case ARRAY_REF:
- /* We need to load bounds from the bounds table. */
- bounds = chkp_find_bounds_loaded (node, rhs1, &iter);
- break;
- case VAR_DECL:
- case SSA_NAME:
- case ADDR_EXPR:
- case POINTER_PLUS_EXPR:
- case NOP_EXPR:
- case CONVERT_EXPR:
- case INTEGER_CST:
- /* Bounds are just propagated from RHS. */
- bounds = chkp_find_bounds (rhs1, &iter);
- break;
- case VIEW_CONVERT_EXPR:
- /* Bounds are just propagated from RHS. */
- bounds = chkp_find_bounds (TREE_OPERAND (rhs1, 0), &iter);
- break;
- case PARM_DECL:
- if (BOUNDED_P (rhs1))
- {
- /* We need to load bounds from the bounds table. */
- bounds = chkp_build_bndldx (chkp_build_addr_expr (rhs1),
- node, &iter);
- TREE_ADDRESSABLE (rhs1) = 1;
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- break;
- case MINUS_EXPR:
- case PLUS_EXPR:
- case BIT_AND_EXPR:
- case BIT_IOR_EXPR:
- case BIT_XOR_EXPR:
- {
- tree rhs2 = gimple_assign_rhs2 (assign);
- tree bnd1 = chkp_find_bounds (rhs1, &iter);
- tree bnd2 = chkp_find_bounds (rhs2, &iter);
- /* First we try to check types of operands. If it
- does not help then look at bound values.
- If some bounds are incomplete and other are
- not proven to be valid (i.e. also incomplete
- or invalid because value is not pointer) then
- resulting value is incomplete and will be
- recomputed later in chkp_finish_incomplete_bounds. */
- if (BOUNDED_P (rhs1)
- && !BOUNDED_P (rhs2))
- bounds = bnd1;
- else if (BOUNDED_P (rhs2)
- && !BOUNDED_P (rhs1)
- && rhs_code != MINUS_EXPR)
- bounds = bnd2;
- else if (chkp_incomplete_bounds (bnd1))
- if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR
- && !chkp_incomplete_bounds (bnd2))
- bounds = bnd2;
- else
- bounds = incomplete_bounds;
- else if (chkp_incomplete_bounds (bnd2))
- if (chkp_valid_bounds (bnd1)
- && !chkp_incomplete_bounds (bnd1))
- bounds = bnd1;
- else
- bounds = incomplete_bounds;
- else if (!chkp_valid_bounds (bnd1))
- if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR)
- bounds = bnd2;
- else if (bnd2 == chkp_get_zero_bounds ())
- bounds = bnd2;
- else
- bounds = bnd1;
- else if (!chkp_valid_bounds (bnd2))
- bounds = bnd1;
- else
- /* Seems both operands may have valid bounds
- (e.g. pointer minus pointer). In such case
- use default invalid op bounds. */
- bounds = chkp_get_invalid_op_bounds ();
- }
- break;
- case BIT_NOT_EXPR:
- case NEGATE_EXPR:
- case LSHIFT_EXPR:
- case RSHIFT_EXPR:
- case LROTATE_EXPR:
- case RROTATE_EXPR:
- case EQ_EXPR:
- case NE_EXPR:
- case LT_EXPR:
- case LE_EXPR:
- case GT_EXPR:
- case GE_EXPR:
- case MULT_EXPR:
- case RDIV_EXPR:
- case TRUNC_DIV_EXPR:
- case FLOOR_DIV_EXPR:
- case CEIL_DIV_EXPR:
- case ROUND_DIV_EXPR:
- case TRUNC_MOD_EXPR:
- case FLOOR_MOD_EXPR:
- case CEIL_MOD_EXPR:
- case ROUND_MOD_EXPR:
- case EXACT_DIV_EXPR:
- case FIX_TRUNC_EXPR:
- case FLOAT_EXPR:
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- /* No valid bounds may be produced by these exprs. */
- bounds = chkp_get_invalid_op_bounds ();
- break;
- case COND_EXPR:
- {
- tree val1 = gimple_assign_rhs2 (assign);
- tree val2 = gimple_assign_rhs3 (assign);
- tree bnd1 = chkp_find_bounds (val1, &iter);
- tree bnd2 = chkp_find_bounds (val2, &iter);
- gimple stmt;
- if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2))
- bounds = incomplete_bounds;
- else if (bnd1 == bnd2)
- bounds = bnd1;
- else
- {
- rhs1 = unshare_expr (rhs1);
- bounds = chkp_get_tmp_reg (assign);
- stmt = gimple_build_assign (bounds, COND_EXPR, rhs1, bnd1, bnd2);
- gsi_insert_after (&iter, stmt, GSI_SAME_STMT);
- if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2))
- chkp_mark_invalid_bounds (bounds);
- }
- }
- break;
- case MAX_EXPR:
- case MIN_EXPR:
- {
- tree rhs2 = gimple_assign_rhs2 (assign);
- tree bnd1 = chkp_find_bounds (rhs1, &iter);
- tree bnd2 = chkp_find_bounds (rhs2, &iter);
- if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2))
- bounds = incomplete_bounds;
- else if (bnd1 == bnd2)
- bounds = bnd1;
- else
- {
- gimple stmt;
- tree cond = build2 (rhs_code == MAX_EXPR ? GT_EXPR : LT_EXPR,
- boolean_type_node, rhs1, rhs2);
- bounds = chkp_get_tmp_reg (assign);
- stmt = gimple_build_assign (bounds, COND_EXPR, cond, bnd1, bnd2);
- gsi_insert_after (&iter, stmt, GSI_SAME_STMT);
- if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2))
- chkp_mark_invalid_bounds (bounds);
- }
- }
- break;
- default:
- bounds = chkp_get_zero_bounds ();
- warning (0, "pointer bounds were lost due to unexpected expression %s",
- get_tree_code_name (rhs_code));
- }
- gcc_assert (bounds);
- if (node)
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- return bounds;
- }
- /* Compute bounds for ssa name NODE defined by DEF_STMT pointed by ITER.
- There are just few statement codes allowed: NOP (for default ssa names),
- ASSIGN, CALL, PHI, ASM.
- Return computed bounds. */
- static tree
- chkp_get_bounds_by_definition (tree node, gimple def_stmt,
- gphi_iterator *iter)
- {
- tree var, bounds;
- enum gimple_code code = gimple_code (def_stmt);
- gphi *stmt;
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Searching for bounds for node: ");
- print_generic_expr (dump_file, node, 0);
- fprintf (dump_file, " using its definition: ");
- print_gimple_stmt (dump_file, def_stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- }
- switch (code)
- {
- case GIMPLE_NOP:
- var = SSA_NAME_VAR (node);
- switch (TREE_CODE (var))
- {
- case PARM_DECL:
- bounds = chkp_get_bound_for_parm (node);
- break;
- case VAR_DECL:
- /* For uninitialized pointers use none bounds. */
- bounds = chkp_get_none_bounds ();
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- break;
- case RESULT_DECL:
- {
- tree base_type;
- gcc_assert (TREE_CODE (TREE_TYPE (node)) == REFERENCE_TYPE);
- base_type = TREE_TYPE (TREE_TYPE (node));
- gcc_assert (TYPE_SIZE (base_type)
- && TREE_CODE (TYPE_SIZE (base_type)) == INTEGER_CST
- && tree_to_uhwi (TYPE_SIZE (base_type)) != 0);
- bounds = chkp_make_bounds (node, TYPE_SIZE_UNIT (base_type),
- NULL, false);
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- }
- break;
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Unexpected var with no definition\n");
- print_generic_expr (dump_file, var, 0);
- }
- internal_error ("chkp_get_bounds_by_definition: Unexpected var of type %s",
- get_tree_code_name (TREE_CODE (var)));
- }
- break;
- case GIMPLE_ASSIGN:
- bounds = chkp_compute_bounds_for_assignment (node, def_stmt);
- break;
- case GIMPLE_CALL:
- bounds = chkp_build_returned_bound (as_a <gcall *> (def_stmt));
- break;
- case GIMPLE_PHI:
- if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (node))
- if (SSA_NAME_VAR (node))
- var = chkp_get_bounds_var (SSA_NAME_VAR (node));
- else
- var = make_temp_ssa_name (pointer_bounds_type_node,
- gimple_build_nop (),
- CHKP_BOUND_TMP_NAME);
- else
- var = chkp_get_tmp_var ();
- stmt = create_phi_node (var, gimple_bb (def_stmt));
- bounds = gimple_phi_result (stmt);
- *iter = gsi_for_phi (stmt);
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- /* Created bounds do not have all phi args computed and
- therefore we do not know if there is a valid source
- of bounds for that node. Therefore we mark bounds
- as incomplete and then recompute them when all phi
- args are computed. */
- chkp_register_incomplete_bounds (bounds, node);
- break;
- case GIMPLE_ASM:
- bounds = chkp_get_zero_bounds ();
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- break;
- default:
- internal_error ("chkp_get_bounds_by_definition: Unexpected GIMPLE code %s",
- gimple_code_name[code]);
- }
- return bounds;
- }
- /* Return CALL_EXPR for bndmk with specified LOWER_BOUND and SIZE. */
- tree
- chkp_build_make_bounds_call (tree lower_bound, tree size)
- {
- tree call = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndmk_fndecl)),
- chkp_bndmk_fndecl);
- return build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndmk_fndecl)),
- call, 2, lower_bound, size);
- }
- /* Create static bounds var of specfified OBJ which is
- is either VAR_DECL or string constant. */
- static tree
- chkp_make_static_bounds (tree obj)
- {
- static int string_id = 1;
- static int var_id = 1;
- tree *slot;
- const char *var_name;
- char *bnd_var_name;
- tree bnd_var;
- /* First check if we already have required var. */
- if (chkp_static_var_bounds)
- {
- /* For vars we use assembler name as a key in
- chkp_static_var_bounds map. It allows to
- avoid duplicating bound vars for decls
- sharing assembler name. */
- if (TREE_CODE (obj) == VAR_DECL)
- {
- tree name = DECL_ASSEMBLER_NAME (obj);
- slot = chkp_static_var_bounds->get (name);
- if (slot)
- return *slot;
- }
- else
- {
- slot = chkp_static_var_bounds->get (obj);
- if (slot)
- return *slot;
- }
- }
- /* Build decl for bounds var. */
- if (TREE_CODE (obj) == VAR_DECL)
- {
- if (DECL_IGNORED_P (obj))
- {
- bnd_var_name = (char *) xmalloc (strlen (CHKP_VAR_BOUNDS_PREFIX) + 10);
- sprintf (bnd_var_name, "%s%d", CHKP_VAR_BOUNDS_PREFIX, var_id++);
- }
- else
- {
- var_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (obj));
- /* For hidden symbols we want to skip first '*' char. */
- if (*var_name == '*')
- var_name++;
- bnd_var_name = (char *) xmalloc (strlen (var_name)
- + strlen (CHKP_BOUNDS_OF_SYMBOL_PREFIX) + 1);
- strcpy (bnd_var_name, CHKP_BOUNDS_OF_SYMBOL_PREFIX);
- strcat (bnd_var_name, var_name);
- }
- bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
- get_identifier (bnd_var_name),
- pointer_bounds_type_node);
- /* Address of the obj will be used as lower bound. */
- TREE_ADDRESSABLE (obj) = 1;
- }
- else
- {
- bnd_var_name = (char *) xmalloc (strlen (CHKP_STRING_BOUNDS_PREFIX) + 10);
- sprintf (bnd_var_name, "%s%d", CHKP_STRING_BOUNDS_PREFIX, string_id++);
- bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
- get_identifier (bnd_var_name),
- pointer_bounds_type_node);
- }
- TREE_PUBLIC (bnd_var) = 0;
- TREE_USED (bnd_var) = 1;
- TREE_READONLY (bnd_var) = 0;
- TREE_STATIC (bnd_var) = 1;
- TREE_ADDRESSABLE (bnd_var) = 0;
- DECL_ARTIFICIAL (bnd_var) = 1;
- DECL_COMMON (bnd_var) = 1;
- DECL_COMDAT (bnd_var) = 1;
- DECL_READ_P (bnd_var) = 1;
- DECL_INITIAL (bnd_var) = chkp_build_addr_expr (obj);
- /* Force output similar to constant bounds.
- See chkp_make_static_const_bounds. */
- varpool_node::get_create (bnd_var)->force_output = 1;
- /* Mark symbol as requiring bounds initialization. */
- varpool_node::get_create (bnd_var)->need_bounds_init = 1;
- varpool_node::finalize_decl (bnd_var);
- /* Add created var to the map to use it for other references
- to obj. */
- if (!chkp_static_var_bounds)
- chkp_static_var_bounds = new hash_map<tree, tree>;
- if (TREE_CODE (obj) == VAR_DECL)
- {
- tree name = DECL_ASSEMBLER_NAME (obj);
- chkp_static_var_bounds->put (name, bnd_var);
- }
- else
- chkp_static_var_bounds->put (obj, bnd_var);
- return bnd_var;
- }
- /* When var has incomplete type we cannot get size to
- compute its bounds. In such cases we use checker
- builtin call which determines object size at runtime. */
- static tree
- chkp_generate_extern_var_bounds (tree var)
- {
- tree bounds, size_reloc, lb, size, max_size, cond;
- gimple_stmt_iterator gsi;
- gimple_seq seq = NULL;
- gimple stmt;
- /* If instrumentation is not enabled for vars having
- incomplete type then just return zero bounds to avoid
- checks for this var. */
- if (!flag_chkp_incomplete_type)
- return chkp_get_zero_bounds ();
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generating bounds for extern symbol '");
- print_generic_expr (dump_file, var, 0);
- fprintf (dump_file, "'\n");
- }
- stmt = gimple_build_call (chkp_sizeof_fndecl, 1, var);
- size_reloc = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME);
- gimple_call_set_lhs (stmt, size_reloc);
- gimple_seq_add_stmt (&seq, stmt);
- lb = chkp_build_addr_expr (var);
- size = make_ssa_name (chkp_get_size_tmp_var (), gimple_build_nop ());
- if (flag_chkp_zero_dynamic_size_as_infinite)
- {
- /* We should check that size relocation was resolved.
- If it was not then use maximum possible size for the var. */
- max_size = build2 (MINUS_EXPR, chkp_uintptr_type, integer_zero_node,
- fold_convert (chkp_uintptr_type, lb));
- max_size = chkp_force_gimple_call_op (max_size, &seq);
- cond = build2 (NE_EXPR, boolean_type_node,
- size_reloc, integer_zero_node);
- stmt = gimple_build_assign (size, COND_EXPR, cond, size_reloc, max_size);
- gimple_seq_add_stmt (&seq, stmt);
- }
- else
- {
- stmt = gimple_build_assign (size, size_reloc);
- gimple_seq_add_stmt (&seq, stmt);
- }
- gsi = gsi_start_bb (chkp_get_entry_block ());
- gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
- bounds = chkp_make_bounds (lb, size, &gsi, true);
- return bounds;
- }
- /* Return 1 if TYPE has fields with zero size or fields
- marked with chkp_variable_size attribute. */
- bool
- chkp_variable_size_type (tree type)
- {
- bool res = false;
- tree field;
- if (RECORD_OR_UNION_TYPE_P (type))
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- {
- if (TREE_CODE (field) == FIELD_DECL)
- res = res
- || lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field))
- || chkp_variable_size_type (TREE_TYPE (field));
- }
- else
- res = !TYPE_SIZE (type)
- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
- || tree_to_uhwi (TYPE_SIZE (type)) == 0;
- return res;
- }
- /* Compute and return bounds for address of DECL which is
- one of VAR_DECL, PARM_DECL, RESULT_DECL. */
- static tree
- chkp_get_bounds_for_decl_addr (tree decl)
- {
- tree bounds;
- gcc_assert (TREE_CODE (decl) == VAR_DECL
- || TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL);
- bounds = chkp_get_registered_addr_bounds (decl);
- if (bounds)
- return bounds;
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Building bounds for address of decl ");
- print_generic_expr (dump_file, decl, 0);
- fprintf (dump_file, "\n");
- }
- /* Use zero bounds if size is unknown and checks for
- unknown sizes are restricted. */
- if ((!DECL_SIZE (decl)
- || (chkp_variable_size_type (TREE_TYPE (decl))
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))))
- && !flag_chkp_incomplete_type)
- return chkp_get_zero_bounds ();
- if (flag_chkp_use_static_bounds
- && TREE_CODE (decl) == VAR_DECL
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))
- && !DECL_THREAD_LOCAL_P (decl))
- {
- tree bnd_var = chkp_make_static_bounds (decl);
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple stmt;
- bounds = chkp_get_tmp_reg (gimple_build_nop ());
- stmt = gimple_build_assign (bounds, bnd_var);
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else if (!DECL_SIZE (decl)
- || (chkp_variable_size_type (TREE_TYPE (decl))
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))))
- {
- gcc_assert (TREE_CODE (decl) == VAR_DECL);
- bounds = chkp_generate_extern_var_bounds (decl);
- }
- else
- {
- tree lb = chkp_build_addr_expr (decl);
- bounds = chkp_make_bounds (lb, DECL_SIZE_UNIT (decl), NULL, false);
- }
- return bounds;
- }
- /* Compute and return bounds for constant string. */
- static tree
- chkp_get_bounds_for_string_cst (tree cst)
- {
- tree bounds;
- tree lb;
- tree size;
- gcc_assert (TREE_CODE (cst) == STRING_CST);
- bounds = chkp_get_registered_bounds (cst);
- if (bounds)
- return bounds;
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- tree bnd_var = chkp_make_static_bounds (cst);
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple stmt;
- bounds = chkp_get_tmp_reg (gimple_build_nop ());
- stmt = gimple_build_assign (bounds, bnd_var);
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- {
- lb = chkp_build_addr_expr (cst);
- size = build_int_cst (chkp_uintptr_type, TREE_STRING_LENGTH (cst));
- bounds = chkp_make_bounds (lb, size, NULL, false);
- }
- bounds = chkp_maybe_copy_and_register_bounds (cst, bounds);
- return bounds;
- }
- /* Generate code to instersect bounds BOUNDS1 and BOUNDS2 and
- return the result. if ITER is not NULL then Code is inserted
- before position pointed by ITER. Otherwise code is added to
- entry block. */
- static tree
- chkp_intersect_bounds (tree bounds1, tree bounds2, gimple_stmt_iterator *iter)
- {
- if (!bounds1 || bounds1 == chkp_get_zero_bounds ())
- return bounds2 ? bounds2 : bounds1;
- else if (!bounds2 || bounds2 == chkp_get_zero_bounds ())
- return bounds1;
- else
- {
- gimple_seq seq;
- gimple stmt;
- tree bounds;
- seq = NULL;
- stmt = gimple_build_call (chkp_intersect_fndecl, 2, bounds1, bounds2);
- chkp_mark_stmt (stmt);
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
- gimple_seq_add_stmt (&seq, stmt);
- /* We are probably doing narrowing for constant expression.
- In such case iter may be undefined. */
- if (!iter)
- {
- gimple_stmt_iterator gsi = gsi_last_bb (chkp_get_entry_block ());
- iter = &gsi;
- gsi_insert_seq_after (iter, seq, GSI_SAME_STMT);
- }
- else
- gsi_insert_seq_before (iter, seq, GSI_SAME_STMT);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Bounds intersection: ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " inserted before statement: ");
- print_gimple_stmt (dump_file, gsi_stmt (*iter), 0,
- TDF_VOPS|TDF_MEMSYMS);
- }
- return bounds;
- }
- }
- /* Return 1 if we are allowed to narrow bounds for addressed FIELD
- and 0 othersize. */
- static bool
- chkp_may_narrow_to_field (tree field)
- {
- return DECL_SIZE (field) && TREE_CODE (DECL_SIZE (field)) == INTEGER_CST
- && tree_to_uhwi (DECL_SIZE (field)) != 0
- && (!DECL_FIELD_OFFSET (field)
- || TREE_CODE (DECL_FIELD_OFFSET (field)) == INTEGER_CST)
- && (!DECL_FIELD_BIT_OFFSET (field)
- || TREE_CODE (DECL_FIELD_BIT_OFFSET (field)) == INTEGER_CST)
- && !lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field))
- && !chkp_variable_size_type (TREE_TYPE (field));
- }
- /* Return 1 if bounds for FIELD should be narrowed to
- field's own size. */
- static bool
- chkp_narrow_bounds_for_field (tree field)
- {
- HOST_WIDE_INT offs;
- HOST_WIDE_INT bit_offs;
- if (!chkp_may_narrow_to_field (field))
- return false;
- /* Accesse to compiler generated fields should not cause
- bounds narrowing. */
- if (DECL_ARTIFICIAL (field))
- return false;
- offs = tree_to_uhwi (DECL_FIELD_OFFSET (field));
- bit_offs = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
- return (flag_chkp_narrow_bounds
- && (flag_chkp_first_field_has_own_bounds
- || offs
- || bit_offs));
- }
- /* Perform narrowing for BOUNDS using bounds computed for field
- access COMPONENT. ITER meaning is the same as for
- chkp_intersect_bounds. */
- static tree
- chkp_narrow_bounds_to_field (tree bounds, tree component,
- gimple_stmt_iterator *iter)
- {
- tree field = TREE_OPERAND (component, 1);
- tree size = DECL_SIZE_UNIT (field);
- tree field_ptr = chkp_build_addr_expr (component);
- tree field_bounds;
- field_bounds = chkp_make_bounds (field_ptr, size, iter, false);
- return chkp_intersect_bounds (field_bounds, bounds, iter);
- }
- /* Parse field or array access NODE.
- PTR ouput parameter holds a pointer to the outermost
- object.
- BITFIELD output parameter is set to 1 if bitfield is
- accessed and to 0 otherwise. If it is 1 then ELT holds
- outer component for accessed bit field.
- SAFE outer parameter is set to 1 if access is safe and
- checks are not required.
- BOUNDS outer parameter holds bounds to be used to check
- access (may be NULL).
- If INNERMOST_BOUNDS is 1 then try to narrow bounds to the
- innermost accessed component. */
- static void
- chkp_parse_array_and_component_ref (tree node, tree *ptr,
- tree *elt, bool *safe,
- bool *bitfield,
- tree *bounds,
- gimple_stmt_iterator *iter,
- bool innermost_bounds)
- {
- tree comp_to_narrow = NULL_TREE;
- tree last_comp = NULL_TREE;
- bool array_ref_found = false;
- tree *nodes;
- tree var;
- int len;
- int i;
- /* Compute tree height for expression. */
- var = node;
- len = 1;
- while (TREE_CODE (var) == COMPONENT_REF
- || TREE_CODE (var) == ARRAY_REF
- || TREE_CODE (var) == VIEW_CONVERT_EXPR)
- {
- var = TREE_OPERAND (var, 0);
- len++;
- }
- gcc_assert (len > 1);
- /* It is more convenient for us to scan left-to-right,
- so walk tree again and put all node to nodes vector
- in reversed order. */
- nodes = XALLOCAVEC (tree, len);
- nodes[len - 1] = node;
- for (i = len - 2; i >= 0; i--)
- nodes[i] = TREE_OPERAND (nodes[i + 1], 0);
- if (bounds)
- *bounds = NULL;
- *safe = true;
- *bitfield = (TREE_CODE (node) == COMPONENT_REF
- && DECL_BIT_FIELD_TYPE (TREE_OPERAND (node, 1)));
- /* To get bitfield address we will need outer elemnt. */
- if (*bitfield)
- *elt = nodes[len - 2];
- else
- *elt = NULL_TREE;
- /* If we have indirection in expression then compute
- outermost structure bounds. Computed bounds may be
- narrowed later. */
- if (TREE_CODE (nodes[0]) == MEM_REF || INDIRECT_REF_P (nodes[0]))
- {
- *safe = false;
- *ptr = TREE_OPERAND (nodes[0], 0);
- if (bounds)
- *bounds = chkp_find_bounds (*ptr, iter);
- }
- else
- {
- gcc_assert (TREE_CODE (var) == VAR_DECL
- || TREE_CODE (var) == PARM_DECL
- || TREE_CODE (var) == RESULT_DECL
- || TREE_CODE (var) == STRING_CST
- || TREE_CODE (var) == SSA_NAME);
- *ptr = chkp_build_addr_expr (var);
- }
- /* In this loop we are trying to find a field access
- requiring narrowing. There are two simple rules
- for search:
- 1. Leftmost array_ref is chosen if any.
- 2. Rightmost suitable component_ref is chosen if innermost
- bounds are required and no array_ref exists. */
- for (i = 1; i < len; i++)
- {
- var = nodes[i];
- if (TREE_CODE (var) == ARRAY_REF)
- {
- *safe = false;
- array_ref_found = true;
- if (flag_chkp_narrow_bounds
- && !flag_chkp_narrow_to_innermost_arrray
- && (!last_comp
- || chkp_may_narrow_to_field (TREE_OPERAND (last_comp, 1))))
- {
- comp_to_narrow = last_comp;
- break;
- }
- }
- else if (TREE_CODE (var) == COMPONENT_REF)
- {
- tree field = TREE_OPERAND (var, 1);
- if (innermost_bounds
- && !array_ref_found
- && chkp_narrow_bounds_for_field (field))
- comp_to_narrow = var;
- last_comp = var;
- if (flag_chkp_narrow_bounds
- && flag_chkp_narrow_to_innermost_arrray
- && TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE)
- {
- if (bounds)
- *bounds = chkp_narrow_bounds_to_field (*bounds, var, iter);
- comp_to_narrow = NULL;
- }
- }
- else if (TREE_CODE (var) == VIEW_CONVERT_EXPR)
- /* Nothing to do for it. */
- ;
- else
- gcc_unreachable ();
- }
- if (comp_to_narrow && DECL_SIZE (TREE_OPERAND (comp_to_narrow, 1)) && bounds)
- *bounds = chkp_narrow_bounds_to_field (*bounds, comp_to_narrow, iter);
- if (innermost_bounds && bounds && !*bounds)
- *bounds = chkp_find_bounds (*ptr, iter);
- }
- /* Compute and return bounds for address of OBJ. */
- static tree
- chkp_make_addressed_object_bounds (tree obj, gimple_stmt_iterator *iter)
- {
- tree bounds = chkp_get_registered_addr_bounds (obj);
- if (bounds)
- return bounds;
- switch (TREE_CODE (obj))
- {
- case VAR_DECL:
- case PARM_DECL:
- case RESULT_DECL:
- bounds = chkp_get_bounds_for_decl_addr (obj);
- break;
- case STRING_CST:
- bounds = chkp_get_bounds_for_string_cst (obj);
- break;
- case ARRAY_REF:
- case COMPONENT_REF:
- {
- tree elt;
- tree ptr;
- bool safe;
- bool bitfield;
- chkp_parse_array_and_component_ref (obj, &ptr, &elt, &safe,
- &bitfield, &bounds, iter, true);
- gcc_assert (bounds);
- }
- break;
- case FUNCTION_DECL:
- case LABEL_DECL:
- bounds = chkp_get_zero_bounds ();
- break;
- case MEM_REF:
- bounds = chkp_find_bounds (TREE_OPERAND (obj, 0), iter);
- break;
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (obj, 0), iter);
- break;
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "chkp_make_addressed_object_bounds: "
- "unexpected object of type %s\n",
- get_tree_code_name (TREE_CODE (obj)));
- print_node (dump_file, "", obj, 0);
- }
- internal_error ("chkp_make_addressed_object_bounds: "
- "Unexpected tree code %s",
- get_tree_code_name (TREE_CODE (obj)));
- }
- chkp_register_addr_bounds (obj, bounds);
- return bounds;
- }
- /* Compute bounds for pointer PTR loaded from PTR_SRC. Generate statements
- to compute bounds if required. Computed bounds should be available at
- position pointed by ITER.
- If PTR_SRC is NULL_TREE then pointer definition is identified.
- If PTR_SRC is not NULL_TREE then ITER points to statements which loads
- PTR. If PTR is a any memory reference then ITER points to a statement
- after which bndldx will be inserterd. In both cases ITER will be updated
- to point to the inserted bndldx statement. */
- static tree
- chkp_find_bounds_1 (tree ptr, tree ptr_src, gimple_stmt_iterator *iter)
- {
- tree addr = NULL_TREE;
- tree bounds = NULL_TREE;
- if (!ptr_src)
- ptr_src = ptr;
- bounds = chkp_get_registered_bounds (ptr_src);
- if (bounds)
- return bounds;
- switch (TREE_CODE (ptr_src))
- {
- case MEM_REF:
- case VAR_DECL:
- if (BOUNDED_P (ptr_src))
- if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr))
- bounds = chkp_get_zero_bounds ();
- else
- {
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- break;
- case ARRAY_REF:
- case COMPONENT_REF:
- addr = get_base_address (ptr_src);
- if (DECL_P (addr)
- || TREE_CODE (addr) == MEM_REF
- || TREE_CODE (addr) == TARGET_MEM_REF)
- {
- if (BOUNDED_P (ptr_src))
- if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr))
- bounds = chkp_get_zero_bounds ();
- else
- {
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- }
- else
- {
- gcc_assert (TREE_CODE (addr) == SSA_NAME);
- bounds = chkp_find_bounds (addr, iter);
- }
- break;
- case PARM_DECL:
- gcc_unreachable ();
- bounds = chkp_get_bound_for_parm (ptr_src);
- break;
- case TARGET_MEM_REF:
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- break;
- case SSA_NAME:
- bounds = chkp_get_registered_bounds (ptr_src);
- if (!bounds)
- {
- gimple def_stmt = SSA_NAME_DEF_STMT (ptr_src);
- gphi_iterator phi_iter;
- bounds = chkp_get_bounds_by_definition (ptr_src, def_stmt, &phi_iter);
- gcc_assert (bounds);
- if (gphi *def_phi = dyn_cast <gphi *> (def_stmt))
- {
- unsigned i;
- for (i = 0; i < gimple_phi_num_args (def_phi); i++)
- {
- tree arg = gimple_phi_arg_def (def_phi, i);
- tree arg_bnd;
- gphi *phi_bnd;
- arg_bnd = chkp_find_bounds (arg, NULL);
- /* chkp_get_bounds_by_definition created new phi
- statement and phi_iter points to it.
- Previous call to chkp_find_bounds could create
- new basic block and therefore change phi statement
- phi_iter points to. */
- phi_bnd = phi_iter.phi ();
- add_phi_arg (phi_bnd, arg_bnd,
- gimple_phi_arg_edge (def_phi, i),
- UNKNOWN_LOCATION);
- }
- /* If all bound phi nodes have their arg computed
- then we may finish its computation. See
- chkp_finish_incomplete_bounds for more details. */
- if (chkp_may_finish_incomplete_bounds ())
- chkp_finish_incomplete_bounds ();
- }
- gcc_assert (bounds == chkp_get_registered_bounds (ptr_src)
- || chkp_incomplete_bounds (bounds));
- }
- break;
- case ADDR_EXPR:
- bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (ptr_src, 0), iter);
- break;
- case INTEGER_CST:
- if (integer_zerop (ptr_src))
- bounds = chkp_get_none_bounds ();
- else
- bounds = chkp_get_invalid_op_bounds ();
- break;
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "chkp_find_bounds: unexpected ptr of type %s\n",
- get_tree_code_name (TREE_CODE (ptr_src)));
- print_node (dump_file, "", ptr_src, 0);
- }
- internal_error ("chkp_find_bounds: Unexpected tree code %s",
- get_tree_code_name (TREE_CODE (ptr_src)));
- }
- if (!bounds)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (stderr, "chkp_find_bounds: cannot find bounds for pointer\n");
- print_node (dump_file, "", ptr_src, 0);
- }
- internal_error ("chkp_find_bounds: Cannot find bounds for pointer");
- }
- return bounds;
- }
- /* Normal case for bounds search without forced narrowing. */
- static tree
- chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter)
- {
- return chkp_find_bounds_1 (ptr, NULL_TREE, iter);
- }
- /* Search bounds for pointer PTR loaded from PTR_SRC
- by statement *ITER points to. */
- static tree
- chkp_find_bounds_loaded (tree ptr, tree ptr_src, gimple_stmt_iterator *iter)
- {
- return chkp_find_bounds_1 (ptr, ptr_src, iter);
- }
- /* Helper function which checks type of RHS and finds all pointers in
- it. For each found pointer we build it's accesses in LHS and RHS
- objects and then call HANDLER for them. Function is used to copy
- or initilize bounds for copied object. */
- static void
- chkp_walk_pointer_assignments (tree lhs, tree rhs, void *arg,
- assign_handler handler)
- {
- tree type = TREE_TYPE (lhs);
- /* We have nothing to do with clobbers. */
- if (TREE_CLOBBER_P (rhs))
- return;
- if (BOUNDED_TYPE_P (type))
- handler (lhs, rhs, arg);
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
- if (TREE_CODE (rhs) == CONSTRUCTOR)
- {
- unsigned HOST_WIDE_INT cnt;
- tree val;
- FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, field, val)
- {
- if (chkp_type_has_pointer (TREE_TYPE (field)))
- {
- tree lhs_field = chkp_build_component_ref (lhs, field);
- chkp_walk_pointer_assignments (lhs_field, val, arg, handler);
- }
- }
- }
- else
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && chkp_type_has_pointer (TREE_TYPE (field)))
- {
- tree rhs_field = chkp_build_component_ref (rhs, field);
- tree lhs_field = chkp_build_component_ref (lhs, field);
- chkp_walk_pointer_assignments (lhs_field, rhs_field, arg, handler);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- {
- unsigned HOST_WIDE_INT cur = 0;
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- tree esize = TYPE_SIZE (etype);
- if (TREE_CODE (rhs) == CONSTRUCTOR)
- {
- unsigned HOST_WIDE_INT cnt;
- tree purp, val, lhs_elem;
- FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, purp, val)
- {
- if (purp && TREE_CODE (purp) == RANGE_EXPR)
- {
- tree lo_index = TREE_OPERAND (purp, 0);
- tree hi_index = TREE_OPERAND (purp, 1);
- for (cur = (unsigned)tree_to_uhwi (lo_index);
- cur <= (unsigned)tree_to_uhwi (hi_index);
- cur++)
- {
- lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur);
- chkp_walk_pointer_assignments (lhs_elem, val, arg, handler);
- }
- }
- else
- {
- if (purp)
- {
- gcc_assert (TREE_CODE (purp) == INTEGER_CST);
- cur = tree_to_uhwi (purp);
- }
- lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur++);
- chkp_walk_pointer_assignments (lhs_elem, val, arg, handler);
- }
- }
- }
- /* Copy array only when size is known. */
- else if (maxval && !integer_minus_onep (maxval))
- for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
- {
- tree lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur);
- tree rhs_elem = chkp_build_array_ref (rhs, etype, esize, cur);
- chkp_walk_pointer_assignments (lhs_elem, rhs_elem, arg, handler);
- }
- }
- else
- internal_error("chkp_walk_pointer_assignments: unexpected RHS type: %s",
- get_tree_code_name (TREE_CODE (type)));
- }
- /* Add code to copy bounds for assignment of RHS to LHS.
- ARG is an iterator pointing ne code position. */
- static void
- chkp_copy_bounds_for_elem (tree lhs, tree rhs, void *arg)
- {
- gimple_stmt_iterator *iter = (gimple_stmt_iterator *)arg;
- tree bounds = chkp_find_bounds (rhs, iter);
- tree addr = chkp_build_addr_expr(lhs);
- chkp_build_bndstx (addr, rhs, bounds, iter);
- }
- /* Emit static bound initilizers and size vars. */
- void
- chkp_finish_file (void)
- {
- struct varpool_node *node;
- struct chkp_ctor_stmt_list stmts;
- if (seen_error ())
- return;
- /* Iterate through varpool and generate bounds initialization
- constructors for all statically initialized pointers. */
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- FOR_EACH_VARIABLE (node)
- /* Check that var is actually emitted and we need and may initialize
- its bounds. */
- if (node->need_bounds_init
- && !POINTER_BOUNDS_P (node->decl)
- && DECL_RTL (node->decl)
- && MEM_P (DECL_RTL (node->decl))
- && TREE_ASM_WRITTEN (node->decl))
- {
- chkp_walk_pointer_assignments (node->decl,
- DECL_INITIAL (node->decl),
- &stmts,
- chkp_add_modification_to_stmt_list);
- if (stmts.avail <= 0)
- {
- cgraph_build_static_cdtor ('P', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 3);
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- }
- }
- if (stmts.stmts)
- cgraph_build_static_cdtor ('P', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 3);
- /* Iterate through varpool and generate bounds initialization
- constructors for all static bounds vars. */
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- FOR_EACH_VARIABLE (node)
- if (node->need_bounds_init
- && POINTER_BOUNDS_P (node->decl)
- && TREE_ASM_WRITTEN (node->decl))
- {
- tree bnd = node->decl;
- tree var;
- gcc_assert (DECL_INITIAL (bnd)
- && TREE_CODE (DECL_INITIAL (bnd)) == ADDR_EXPR);
- var = TREE_OPERAND (DECL_INITIAL (bnd), 0);
- chkp_output_static_bounds (bnd, var, &stmts);
- }
- if (stmts.stmts)
- cgraph_build_static_cdtor ('B', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 2);
- delete chkp_static_var_bounds;
- delete chkp_bounds_map;
- }
- /* An instrumentation function which is called for each statement
- having memory access we want to instrument. It inserts check
- code and bounds copy code.
- ITER points to statement to instrument.
- NODE holds memory access in statement to check.
- LOC holds the location information for statement.
- DIRFLAGS determines whether access is read or write.
- ACCESS_OFFS should be added to address used in NODE
- before check.
- ACCESS_SIZE holds size of checked access.
- SAFE indicates if NODE access is safe and should not be
- checked. */
- static void
- chkp_process_stmt (gimple_stmt_iterator *iter, tree node,
- location_t loc, tree dirflag,
- tree access_offs, tree access_size,
- bool safe)
- {
- tree node_type = TREE_TYPE (node);
- tree size = access_size ? access_size : TYPE_SIZE_UNIT (node_type);
- tree addr_first = NULL_TREE; /* address of the first accessed byte */
- tree addr_last = NULL_TREE; /* address of the last accessed byte */
- tree ptr = NULL_TREE; /* a pointer used for dereference */
- tree bounds = NULL_TREE;
- /* We do not need instrumentation for clobbers. */
- if (dirflag == integer_one_node
- && gimple_code (gsi_stmt (*iter)) == GIMPLE_ASSIGN
- && TREE_CLOBBER_P (gimple_assign_rhs1 (gsi_stmt (*iter))))
- return;
- switch (TREE_CODE (node))
- {
- case ARRAY_REF:
- case COMPONENT_REF:
- {
- bool bitfield;
- tree elt;
- if (safe)
- {
- /* We are not going to generate any checks, so do not
- generate bounds as well. */
- addr_first = chkp_build_addr_expr (node);
- break;
- }
- chkp_parse_array_and_component_ref (node, &ptr, &elt, &safe,
- &bitfield, &bounds, iter, false);
- /* Break if there is no dereference and operation is safe. */
- if (bitfield)
- {
- tree field = TREE_OPERAND (node, 1);
- if (TREE_CODE (DECL_SIZE_UNIT (field)) == INTEGER_CST)
- size = DECL_SIZE_UNIT (field);
- if (elt)
- elt = chkp_build_addr_expr (elt);
- addr_first = fold_convert_loc (loc, ptr_type_node, elt ? elt : ptr);
- addr_first = fold_build_pointer_plus_loc (loc,
- addr_first,
- byte_position (field));
- }
- else
- addr_first = chkp_build_addr_expr (node);
- }
- break;
- case INDIRECT_REF:
- ptr = TREE_OPERAND (node, 0);
- addr_first = ptr;
- break;
- case MEM_REF:
- ptr = TREE_OPERAND (node, 0);
- addr_first = chkp_build_addr_expr (node);
- break;
- case TARGET_MEM_REF:
- ptr = TMR_BASE (node);
- addr_first = chkp_build_addr_expr (node);
- break;
- case ARRAY_RANGE_REF:
- printf("ARRAY_RANGE_REF\n");
- debug_gimple_stmt(gsi_stmt(*iter));
- debug_tree(node);
- gcc_unreachable ();
- break;
- case BIT_FIELD_REF:
- {
- tree offs, rem, bpu;
- gcc_assert (!access_offs);
- gcc_assert (!access_size);
- bpu = fold_convert (size_type_node, bitsize_int (BITS_PER_UNIT));
- offs = fold_convert (size_type_node, TREE_OPERAND (node, 2));
- rem = size_binop_loc (loc, TRUNC_MOD_EXPR, offs, bpu);
- offs = size_binop_loc (loc, TRUNC_DIV_EXPR, offs, bpu);
- size = fold_convert (size_type_node, TREE_OPERAND (node, 1));
- size = size_binop_loc (loc, PLUS_EXPR, size, rem);
- size = size_binop_loc (loc, CEIL_DIV_EXPR, size, bpu);
- size = fold_convert (size_type_node, size);
- chkp_process_stmt (iter, TREE_OPERAND (node, 0), loc,
- dirflag, offs, size, safe);
- return;
- }
- break;
- case VAR_DECL:
- case RESULT_DECL:
- case PARM_DECL:
- if (dirflag != integer_one_node
- || DECL_REGISTER (node))
- return;
- safe = true;
- addr_first = chkp_build_addr_expr (node);
- break;
- default:
- return;
- }
- /* If addr_last was not computed then use (addr_first + size - 1)
- expression to compute it. */
- if (!addr_last)
- {
- addr_last = fold_build_pointer_plus_loc (loc, addr_first, size);
- addr_last = fold_build_pointer_plus_hwi_loc (loc, addr_last, -1);
- }
- /* Shift both first_addr and last_addr by access_offs if specified. */
- if (access_offs)
- {
- addr_first = fold_build_pointer_plus_loc (loc, addr_first, access_offs);
- addr_last = fold_build_pointer_plus_loc (loc, addr_last, access_offs);
- }
- /* Generate bndcl/bndcu checks if memory access is not safe. */
- if (!safe)
- {
- gimple_stmt_iterator stmt_iter = *iter;
- if (!bounds)
- bounds = chkp_find_bounds (ptr, iter);
- chkp_check_mem_access (addr_first, addr_last, bounds,
- stmt_iter, loc, dirflag);
- }
- /* We need to store bounds in case pointer is stored. */
- if (dirflag == integer_one_node
- && chkp_type_has_pointer (node_type)
- && flag_chkp_store_bounds)
- {
- gimple stmt = gsi_stmt (*iter);
- tree rhs1 = gimple_assign_rhs1 (stmt);
- enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
- if (get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS)
- chkp_walk_pointer_assignments (node, rhs1, iter,
- chkp_copy_bounds_for_elem);
- else
- {
- bounds = chkp_compute_bounds_for_assignment (NULL_TREE, stmt);
- chkp_build_bndstx (addr_first, rhs1, bounds, iter);
- }
- }
- }
- /* Add code to copy bounds for all pointers copied
- in ASSIGN created during inline of EDGE. */
- void
- chkp_copy_bounds_for_assign (gimple assign, struct cgraph_edge *edge)
- {
- tree lhs = gimple_assign_lhs (assign);
- tree rhs = gimple_assign_rhs1 (assign);
- gimple_stmt_iterator iter = gsi_for_stmt (assign);
- if (!flag_chkp_store_bounds)
- return;
- chkp_walk_pointer_assignments (lhs, rhs, &iter, chkp_copy_bounds_for_elem);
- /* We should create edges for all created calls to bndldx and bndstx. */
- while (gsi_stmt (iter) != assign)
- {
- gimple stmt = gsi_stmt (iter);
- if (gimple_code (stmt) == GIMPLE_CALL)
- {
- tree fndecl = gimple_call_fndecl (stmt);
- struct cgraph_node *callee = cgraph_node::get_create (fndecl);
- struct cgraph_edge *new_edge;
- gcc_assert (fndecl == chkp_bndstx_fndecl
- || fndecl == chkp_bndldx_fndecl
- || fndecl == chkp_ret_bnd_fndecl);
- new_edge = edge->caller->create_edge (callee,
- as_a <gcall *> (stmt),
- edge->count,
- edge->frequency);
- new_edge->frequency = compute_call_stmt_bb_frequency
- (edge->caller->decl, gimple_bb (stmt));
- }
- gsi_prev (&iter);
- }
- }
- /* Some code transformation made during instrumentation pass
- may put code into inconsistent state. Here we find and fix
- such flaws. */
- void
- chkp_fix_cfg ()
- {
- basic_block bb;
- gimple_stmt_iterator i;
- /* We could insert some code right after stmt which ends bb.
- We wanted to put this code on fallthru edge but did not
- add new edges from the beginning because it may cause new
- phi node creation which may be incorrect due to incomplete
- bound phi nodes. */
- FOR_ALL_BB_FN (bb, cfun)
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- {
- gimple stmt = gsi_stmt (i);
- gimple_stmt_iterator next = i;
- gsi_next (&next);
- if (stmt_ends_bb_p (stmt)
- && !gsi_end_p (next))
- {
- edge fall = find_fallthru_edge (bb->succs);
- basic_block dest = NULL;
- int flags = 0;
- gcc_assert (fall);
- /* We cannot split abnormal edge. Therefore we
- store its params, make it regular and then
- rebuild abnormal edge after split. */
- if (fall->flags & EDGE_ABNORMAL)
- {
- flags = fall->flags & ~EDGE_FALLTHRU;
- dest = fall->dest;
- fall->flags &= ~EDGE_COMPLEX;
- }
- while (!gsi_end_p (next))
- {
- gimple next_stmt = gsi_stmt (next);
- gsi_remove (&next, false);
- gsi_insert_on_edge (fall, next_stmt);
- }
- gsi_commit_edge_inserts ();
- /* Re-create abnormal edge. */
- if (dest)
- make_edge (bb, dest, flags);
- }
- }
- }
- /* Walker callback for chkp_replace_function_pointers. Replaces
- function pointer in the specified operand with pointer to the
- instrumented function version. */
- static tree
- chkp_replace_function_pointer (tree *op, int *walk_subtrees,
- void *data ATTRIBUTE_UNUSED)
- {
- if (TREE_CODE (*op) == FUNCTION_DECL
- && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (*op))
- && (DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN
- /* For builtins we replace pointers only for selected
- function and functions having definitions. */
- || (DECL_BUILT_IN_CLASS (*op) == BUILT_IN_NORMAL
- && (chkp_instrument_normal_builtin (*op)
- || gimple_has_body_p (*op)))))
- {
- struct cgraph_node *node = cgraph_node::get_create (*op);
- struct cgraph_node *clone = NULL;
- if (!node->instrumentation_clone)
- clone = chkp_maybe_create_clone (*op);
- if (clone)
- *op = clone->decl;
- *walk_subtrees = 0;
- }
- return NULL;
- }
- /* This function searches for function pointers in statement
- pointed by GSI and replaces them with pointers to instrumented
- function versions. */
- static void
- chkp_replace_function_pointers (gimple_stmt_iterator *gsi)
- {
- gimple stmt = gsi_stmt (*gsi);
- /* For calls we want to walk call args only. */
- if (gimple_code (stmt) == GIMPLE_CALL)
- {
- unsigned i;
- for (i = 0; i < gimple_call_num_args (stmt); i++)
- walk_tree (gimple_call_arg_ptr (stmt, i),
- chkp_replace_function_pointer, NULL, NULL);
- }
- else
- walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL);
- }
- /* This function instruments all statements working with memory,
- calls and rets.
- It also removes excess statements from static initializers. */
- static void
- chkp_instrument_function (void)
- {
- basic_block bb, next;
- gimple_stmt_iterator i;
- enum gimple_rhs_class grhs_class;
- bool safe = lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl));
- bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
- do
- {
- next = bb->next_bb;
- for (i = gsi_start_bb (bb); !gsi_end_p (i); )
- {
- gimple s = gsi_stmt (i);
- /* Skip statement marked to not be instrumented. */
- if (chkp_marked_stmt_p (s))
- {
- gsi_next (&i);
- continue;
- }
- chkp_replace_function_pointers (&i);
- switch (gimple_code (s))
- {
- case GIMPLE_ASSIGN:
- chkp_process_stmt (&i, gimple_assign_lhs (s),
- gimple_location (s), integer_one_node,
- NULL_TREE, NULL_TREE, safe);
- chkp_process_stmt (&i, gimple_assign_rhs1 (s),
- gimple_location (s), integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
- grhs_class = get_gimple_rhs_class (gimple_assign_rhs_code (s));
- if (grhs_class == GIMPLE_BINARY_RHS)
- chkp_process_stmt (&i, gimple_assign_rhs2 (s),
- gimple_location (s), integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
- break;
- case GIMPLE_RETURN:
- {
- greturn *r = as_a <greturn *> (s);
- if (gimple_return_retval (r) != NULL_TREE)
- {
- chkp_process_stmt (&i, gimple_return_retval (r),
- gimple_location (r),
- integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
- /* Additionally we need to add bounds
- to return statement. */
- chkp_add_bounds_to_ret_stmt (&i);
- }
- }
- break;
- case GIMPLE_CALL:
- chkp_add_bounds_to_call_stmt (&i);
- break;
- default:
- ;
- }
- gsi_next (&i);
- /* We do not need any actual pointer stores in checker
- static initializer. */
- if (lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl))
- && gimple_code (s) == GIMPLE_ASSIGN
- && gimple_store_p (s))
- {
- gimple_stmt_iterator del_iter = gsi_for_stmt (s);
- gsi_remove (&del_iter, true);
- unlink_stmt_vdef (s);
- release_defs(s);
- }
- }
- bb = next;
- }
- while (bb);
- /* Some input params may have bounds and be address taken. In this case
- we should store incoming bounds into bounds table. */
- tree arg;
- if (flag_chkp_store_bounds)
- for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
- if (TREE_ADDRESSABLE (arg))
- {
- if (BOUNDED_P (arg))
- {
- tree bounds = chkp_get_next_bounds_parm (arg);
- tree def_ptr = ssa_default_def (cfun, arg);
- gimple_stmt_iterator iter
- = gsi_start_bb (chkp_get_entry_block ());
- chkp_build_bndstx (chkp_build_addr_expr (arg),
- def_ptr ? def_ptr : arg,
- bounds, &iter);
- /* Skip bounds arg. */
- arg = TREE_CHAIN (arg);
- }
- else if (chkp_type_has_pointer (TREE_TYPE (arg)))
- {
- tree orig_arg = arg;
- bitmap slots = BITMAP_ALLOC (NULL);
- gimple_stmt_iterator iter
- = gsi_start_bb (chkp_get_entry_block ());
- bitmap_iterator bi;
- unsigned bnd_no;
- chkp_find_bound_slots (TREE_TYPE (arg), slots);
- EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi)
- {
- tree bounds = chkp_get_next_bounds_parm (arg);
- HOST_WIDE_INT offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
- tree addr = chkp_build_addr_expr (orig_arg);
- tree ptr = build2 (MEM_REF, ptr_type_node, addr,
- build_int_cst (ptr_type_node, offs));
- chkp_build_bndstx (chkp_build_addr_expr (ptr), ptr,
- bounds, &iter);
- arg = DECL_CHAIN (arg);
- }
- BITMAP_FREE (slots);
- }
- }
- }
- /* Find init/null/copy_ptr_bounds calls and replace them
- with assignments. It should allow better code
- optimization. */
- static void
- chkp_remove_useless_builtins ()
- {
- basic_block bb;
- gimple_stmt_iterator gsi;
- FOR_EACH_BB_FN (bb, cfun)
- {
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple stmt = gsi_stmt (gsi);
- tree fndecl;
- enum built_in_function fcode;
- /* Find builtins returning first arg and replace
- them with assignments. */
- if (gimple_code (stmt) == GIMPLE_CALL
- && (fndecl = gimple_call_fndecl (stmt))
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (fcode = DECL_FUNCTION_CODE (fndecl))
- && (fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS))
- {
- tree res = gimple_call_arg (stmt, 0);
- update_call_from_tree (&gsi, res);
- stmt = gsi_stmt (gsi);
- update_stmt (stmt);
- }
- }
- }
- }
- /* Initialize pass. */
- static void
- chkp_init (void)
- {
- basic_block bb;
- gimple_stmt_iterator i;
- in_chkp_pass = true;
- for (bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; bb; bb = bb->next_bb)
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- chkp_unmark_stmt (gsi_stmt (i));
- chkp_invalid_bounds = new hash_set<tree>;
- chkp_completed_bounds_set = new hash_set<tree>;
- delete chkp_reg_bounds;
- chkp_reg_bounds = new hash_map<tree, tree>;
- delete chkp_bound_vars;
- chkp_bound_vars = new hash_map<tree, tree>;
- chkp_reg_addr_bounds = new hash_map<tree, tree>;
- chkp_incomplete_bounds_map = new hash_map<tree, tree>;
- delete chkp_bounds_map;
- chkp_bounds_map = new hash_map<tree, tree>;
- chkp_abnormal_copies = BITMAP_GGC_ALLOC ();
- entry_block = NULL;
- zero_bounds = NULL_TREE;
- none_bounds = NULL_TREE;
- incomplete_bounds = integer_zero_node;
- tmp_var = NULL_TREE;
- size_tmp_var = NULL_TREE;
- chkp_uintptr_type = lang_hooks.types.type_for_mode (ptr_mode, true);
- /* We create these constant bounds once for each object file.
- These symbols go to comdat section and result in single copy
- of each one in the final binary. */
- chkp_get_zero_bounds_var ();
- chkp_get_none_bounds_var ();
- calculate_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_POST_DOMINATORS);
- bitmap_obstack_initialize (NULL);
- }
- /* Finalize instrumentation pass. */
- static void
- chkp_fini (void)
- {
- in_chkp_pass = false;
- delete chkp_invalid_bounds;
- delete chkp_completed_bounds_set;
- delete chkp_reg_addr_bounds;
- delete chkp_incomplete_bounds_map;
- free_dominance_info (CDI_DOMINATORS);
- free_dominance_info (CDI_POST_DOMINATORS);
- bitmap_obstack_release (NULL);
- entry_block = NULL;
- zero_bounds = NULL_TREE;
- none_bounds = NULL_TREE;
- }
- /* Main instrumentation pass function. */
- static unsigned int
- chkp_execute (void)
- {
- chkp_init ();
- chkp_instrument_function ();
- chkp_remove_useless_builtins ();
- chkp_function_mark_instrumented (cfun->decl);
- chkp_fix_cfg ();
- chkp_fini ();
- return 0;
- }
- /* Instrumentation pass gate. */
- static bool
- chkp_gate (void)
- {
- return cgraph_node::get (cfun->decl)->instrumentation_clone
- || lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl));
- }
- namespace {
- const pass_data pass_data_chkp =
- {
- GIMPLE_PASS, /* type */
- "chkp", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_NONE, /* tv_id */
- PROP_ssa | PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_il
- | TODO_update_ssa /* todo_flags_finish */
- };
- class pass_chkp : public gimple_opt_pass
- {
- public:
- pass_chkp (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_chkp, ctxt)
- {}
- /* opt_pass methods: */
- virtual opt_pass * clone ()
- {
- return new pass_chkp (m_ctxt);
- }
- virtual bool gate (function *)
- {
- return chkp_gate ();
- }
- virtual unsigned int execute (function *)
- {
- return chkp_execute ();
- }
- }; // class pass_chkp
- } // anon namespace
- gimple_opt_pass *
- make_pass_chkp (gcc::context *ctxt)
- {
- return new pass_chkp (ctxt);
- }
- #include "gt-tree-chkp.h"
|