[{"data":1,"prerenderedAt":30},["ShallowReactive",2],{"note-plugins-that-change-a-callee-saved-register-ca":3},{"slug":4,"order":5,"title":6,"summary":7,"measured":8,"topic":9,"toc":10,"html":29},"plugins-that-change-a-callee-saved-register",20,"Plugins que canvien un registre que el cridat ha de preservar","Un plugin que torna amb un registre preservat canviat fa fallar l'amfitrió més tard i en un altre lloc. Ara el nucli de l'amfitrió protegeix cada crida a un plugin, n'enregistra el primer canvi i la qualificació només ofereix aquest plugin aïllat.","Raspberry Pi 5 (Debian 13, arm64) i un ordinador x86-64, octubre del 2026; omx-clap-core 0.26.2.","qualify",[11,14,17,20,23,26],{"id":12,"title":13},"que-passa","Què passa",{"id":15,"title":16},"com-es-va-trobar","Com es va trobar",{"id":18,"title":19},"la-proteccio","La protecció",{"id":21,"title":22},"que-es-fa-amb-un-plugin-aixi","Què es fa amb un plugin així",{"id":24,"title":25},"el-que-la-proteccio-no-demostra","El que la protecció no demostra",{"id":27,"title":28},"com-tornar-ho-a-veure","Com tornar-ho a veure","\u003Ch2 id=\"que-passa\">Què passa\u003C\u002Fh2>\n\u003Cp>Cada convenció de crides dona al cridat uns registres que ha de tornar tal com els ha trobat:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>aarch64: de x19 a x28, de d8 a d15, FPCR\u003C\u002Fli>\n\u003Cli>x86-64: rbx, rbp, de r12 a r15, els bits de control de l'MXCSR, la paraula de control de l'x87\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>El qui crida hi pot guardar un valor durant la crida. Un plugin que torna amb un d'aquests registres canviat en substitueix\nel valor sense que res no falli en aquell moment. El dany es veu més tard, en un altre lloc.\u003C\u002Fp>\n\u003Ch2 id=\"com-es-va-trobar\">Com es va trobar\u003C\u002Fh2>\n\u003Cp>Quatre plugins limiters d'una mateixa col·lecció LV2, mesurats en una Raspberry Pi 5, tornaven de \u003Ccode>run()\u003C\u002Fcode> amb d14 i d15\ncanviats, a partir del bloc 227 de l'escombratge de controls del mesurador, en 386 de les seves crides. El mesurador guardava\nla freqüència de mostratge a d15. La instància següent es va obrir a 0 Hz, el plugin va dimensionar un búfer a partir\nd'aquella freqüència i va escriure a través d'un punter NULL: cadascun dels quatre es va enregistrar com una fallada\n(SIGSEGV).\u003C\u002Fp>\n\u003Cp>Compilat a -O0, on el compilador guarda les variables locals a la memòria i no en aquests registres, el mesurador els va\nfer córrer fins al final. A -O2 fallava cada vegada. Aquesta diferència és el senyal d'un registre trepitjat.\u003C\u002Fp>\n\u003Ch2 id=\"la-proteccio\">La protecció\u003C\u002Fh2>\n\u003Cp>El nucli de l'amfitrió crida cada funció d'un plugin a través d'una sola protecció, \u003Ccode>omx_abi_call6\u003C\u002Fcode> (\u003Ccode>abi_guard.S\u003C\u002Fcode>):\u003C\u002Fp>\n\u003Cul>\n\u003Cli>desa els registres preservats, crida, compara i els torna a posar;\u003C\u002Fli>\n\u003Cli>enregistra la primera crida que n'ha canviat algun: els registres, la funció del plugin i el bloc.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Costa de 14 a 15 ns per crida en una Raspberry Pi 5 i 5,6 ns en un ordinador x86-64. Per a 24 plugins a 48 kHz en blocs\nde 256 mostres, són 0,36 µs de cada bloc de 5.333 µs.\u003C\u002Fp>\n\u003Cp>El mesurador LV2 fa totes les crides al plugin a través de la mateixa protecció, també \u003Ccode>lilv_plugin_instantiate\u003C\u002Fcode> i\n\u003Ccode>lilv_instance_free\u003C\u002Fcode>. El seu registre porta \u003Ccode>abi\u003C\u002Fcode>, per exemple:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>{&quot;clobbered&quot;:true,&quot;registers&quot;:[&quot;d14&quot;,&quot;d15&quot;],&quot;call&quot;:&quot;run&quot;,&quot;block&quot;:227,&quot;calls&quot;:386}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Un plugin que no ha trepitjat mai cap registre hi diu \u003Ccode>{&quot;clobbered&quot;:false}\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"que-es-fa-amb-un-plugin-aixi\">Què es fa amb un plugin així\u003C\u002Fh2>\n\u003Cp>El veredicte és \u003Ccode>hosting.stability.callee-saved-clobbered\u003C\u002Fcode>, qualificat de condicional: el plugin només s'ofereix aïllat, en\nun worker propi, mai dins del procés de la consola ni en una cadena compartida. La protecció manté sencer el marc de\nl'amfitrió; no protegeix l'estat dels altres plugins del mateix procés.\u003C\u002Fp>\n\u003Cp>Un worker ho comunica pel seu port de retorn, \u003Ccode>abi_clobber &lt;instance&gt; &lt;mask&gt; &lt;registers&gt; &lt;call&gt; &lt;block&gt;\u003C\u002Fcode>, una vegada per\ninstància i aleshores plugin-hostd trasllada la instància, amb el seu estat, a un worker propi.\u003C\u002Fp>\n\u003Cp>Amb la protecció, els quatre limiters es mesuren fins al final: cap reserva de memòria a \u003Ccode>run()\u003C\u002Fcode> i totes les freqüències\nsonen. Les seves xifres d'abans de la protecció poden estar corrompudes i no es fan servir.\u003C\u002Fp>\n\u003Ch2 id=\"el-que-la-proteccio-no-demostra\">El que la protecció no demostra\u003C\u002Fh2>\n\u003Cp>Un plugin que fallava sense la protecció i arriba al final amb ella, sense enregistrar cap canvi, no queda demostrat net: la\nprotecció canvia el marc de pila del mesurador i pot amagar un error de memòria. I un plugin que encara falla amb la\nprotecció i a -O0 té la causa en un altre lloc.\u003C\u002Fp>\n\u003Ch2 id=\"com-tornar-ho-a-veure\">Com tornar-ho a veure\u003C\u002Fh2>\n\u003Cp>La peça de prova \u003Ccode>native\u002Ffixtures\u002Flv2-gain.c\u003C\u002Fcode> de plugin-qualify, compilada amb \u003Ccode>-DFIXTURE_ABI_BREAKER\u003C\u002Fcode>, torna del seu tercer\n\u003Ccode>run()\u003C\u002Fcode> amb x19 i d15 canviats (rbx i r15 en x86-64), en assemblador, després que el \u003Ccode>run()\u003C\u002Fcode> de C hagi tornat. La prova\n\u003Ccode>src\u002Flv2-measure-native.test.ts\u003C\u002Fcode> el mesura fins al final i en comprova el registre.\u003C\u002Fp>\n",1793954996362]