Not happy

Did you know that signing Windows binaries has gotten a lot more fun since june 2023 ? It was already a great experience, but not on the same scale as today.

So, up to june 2023, in order to sign windows binaries, you had to choose between buying:

  • an OV certificate, cheaper and easier validation. You pay, you get a phone call for identify verification, and at the end of the process you receive a pkcs12 certificate file. You can copy this file on any computer, it is pretty easy to integrate it in you build servers etc. Life is relatively easy as long as the correct phone number is listed in the phone registry that matters to the company issuing the certificates (in my case it was not..).

Drawback of the OV certificate: Microsoft does not trust you by default, so the first people that will download your signed executables will received a pretty scary warning from Windows Defender SmartScreen, telling them that they most likely have downloaded malware and they should erase it ASAP. Thank you MS, I'm glad I paid $700 for such a great service. After a few days/downloads, the warning is gone as MS now thinks your identity is trustworthy. Until.. 3 years later, when you renew the certificate, and all your earned "trust" is gone.

  • an EV certificate, a bit more expensive, with deeper verification (no sure what), and instead of a pkcs12 file, you get a shiny USB key that must be plugged in the computer doing the signing. You are immediately trusted by Microsoft, none of your users will be scared by the SmartScreen malware alert, but having to deal with a physical token instead of a simple file is such a headache that I have always preferred the OV certificate.

So, on 27 may 2023, I decided to renew our previous OV certificate which was about to expire. The provider I use is Sectigo. I could have been one of the last people on earth to receive a 3-years valid PKCS12 OV Certificate file, but no, what I got was a massive delay, and then a stupid SafeNet USB key that must be plugged when signing a file.

Why did this change ? Apparently there are these guys, named the 'CA/Browser forum', who have decided that handing a pkcs12 file to the user was not secure enough and that it was much more fun to make their life miserable by mailing them a stupid USB dongle.

How miserable ? Well quite a lot, indeed. You get a small USB key, and some software (from Thalès, mind you), the "SafeNet Authentification Client", that must be running when signing files. When you launch it, you get a screen with buttons such as "Change Token Password", "Unlock Tocken" etc. But, you cannot unlock the token because you do not have its password, the company that sold you the USB dongle is not giving you the token password. You can have a look at what is inside the token, and find that there is indeed a certificate at your name. You can only export its public key, not the private part. When signing, the SafeNet software will prompt you for a password. That is not the token admin password, but a "PIN" specific to the certificate. This one you can change.

So, by default, EACH time you sign a file, you get that dialog box from the SafeNet Authentification Client that asks you for the PIN of the certificate. EACH time. But that's not all, if you enter the wrong PIN 3 times, then you just bricked your dongle. This happens really quick... you type too fast once, the second time fails and then you realize your keyboard has switched to qwerty, and now you're already at your last chance then you start to sweat for the last chance) (according to what Sectigo told me, they can un-brick the dongle, so it is not completely bricked, but it is still a massive loss of time).

There is a setting in SafeNet to control the number of tries, but it cannot be changed by me, the 'master' password of the dongle is required, I think. Fortunately it is possible to change a setting so that it asks for the password only once per session instead of every time (until logged out, or until the screen is locked) (url)

Still, even with this setting, you have to enter the password once each time the computer reboots, and windows computer do reboot quite often..

There is no official way to enter the token password from command-line. It has to be typed, from you physical keyboard with your little fingers into that stupid dialog box from SafeNet. So it is not possible to automate builds. Fortunately, people have figured some hacks around this, they are ugly and a bit dangerous since the dongle is locked after three unsuccessful attempts:

  • having an application watching the list of windows displayed, if one window title is "Token Logon", it sends it the password (url)
  • using an undocumented feature of signtool (url) . It works great, but for how long ? What happens when the CA/Browser people find out that people can work around their stupid requirements ?

I saw, after buying that SafeNet USB key, that some providers (not Sectigo) do offer "cloud signing" solutions. Of course it is more expensive, these poor guys have to run servers etc.. I guess in the end, for them, these new requirement from the CA/Browser forum are very good for the business, they will sell both usb keys and cloud signing solutions. For everybody else it is just a TOTAL waste of time. Also, in the end, security will be downgraded instead of improved because everybody will have a script that enters the password automatically when the dialog box appears, and will run it on a Windows boxes with auto-login enabled, because it does not work if the user is not logged on. And then this box will connect to the internet with some half-finished python script so that other machines can connect to it to sign binaries.

A final remark : isn't that strange that I can sign my macOS/iOS binaries from any of my macs, without any stupid Thalès USB dongle plugged in my mac ? If that is safe enough for Apple, why does Microsoft enforce this dongle on us, for a more expensive price, and with a much worse service (the SmartScreen malware warning that appears while the 'reputation' is being built is not what I call a great service) ?

In conclusion: fuck the CA/Browser forum, we were fine with the pkcs12 files.

Apple notarization : it sucks

I've been trying to notarize my Fart3000.app the whole afternoon... I was following these directions: hardened-runtime-and-app-notarization . Everything seemed to work ,

xcrun altool --notarization-info blahblah

was displaying

Status: success
Status Message: Package Approved

At first I was happy , until I tried to staple it:

xcrun stapler staple -v Fart3000.app
CloudKit query for Fart3000.app (2/1cff807b7db46318f4c15d93199fc6b3bf454edb) failed due to "record not found".
Could not find base64 encoded ticket in response for 2/1cff807b7db46318f4c15d93199fc6b3bf454edb
The staple and validate action failed! Error 65.

Strange isn't it ? The app is a fat binary with i386 and x86_64 arch. It turned out that if I build only the 64-bit file, then the stapling succeeds...

I don't reality need to built 32-bit versions of the App, so not a big sacrifice, but still it does not smell good, one tools says package approved and the other fails with a stupid and wrong message.

As a bonus, "altool" is a java application so it takes ages to start...

De la fragilité des outils de debug

Une des choses les plus exaspérantes avec le dev en c/c++ c'est de ne pas comprendre *pourquoi* tel outil de profiling ou de debugage n'est pas foutu d'afficher les noms de fonctions corrects, ou d'afficher le code source. C'est par exemple perdre des heures à chercher pourquoi la fonction backtrace de la glibc a besoin d'une option particuliere du compilateur (-rpath) pour donner des noms de fonctions au lieu de leur addresse, alors que celle de la libunwind se demerde toute seule comme une grande. C'est perdre des heures à chercher comment faire en sorte que perf sous linux sur arm affiche des noms de symboles plutot que des addresses hexa. Ca n'est pas spécifique à une plateforme, sur *chaque* plateforme je me retrouve à un moment où a un autre à me battre contre des outils récalcitrants et autistes , qui ne fonctionnent pas et qui ne disent pas quel est leur problème. Mentions spéciales à perf sous linux, windbg sous windows et Instruments.app sous macos.

Et donc pour Instruments.app , ce qui est nécessaire si on veut que cette mule soit capable d'annoter les sources du code profilé, c'est de bien veiller à compiler les sources en donnant le chemin *absolu* à clang, et non pas un chemin relatif.

chrooter une veille debian pour compiler des trucs

Quelques notes pour que je me souvienne comment on met en place une debian wheezy sous ubuntu tranquillos (ici avec une abi armhf):

  • installer schroot:
> apt-get install schroot
  • debootstraper la wheezy:
> debootstrap --arch armhf --foreign wheezy /armhf-wheezy http://ftp.fr.debian.org/debian
  • configurer le schroot:
> sudo vim /etc/schroot/schroot.conf

Et y entrer:

[armhf-wheezy]
description=Debian Wheezy, ARMHF
type=directory
directory=/armhf-wheezy
users=MONLOGINDEUSER
root-groups=root
aliases=armhf,default

Comme ça le home sera directement utilisable dans le chroot.

  • finir le debootstrap:
> sudo schroot
# /debootstrap/debootstrap --second-stage
# cat > /etc/apt/sources.list
deb http://ftp.fr.debian.org/debian wheezy main
deb http://httpredir.debian.org/debian wheezy-updates main
^D
# apt-get update
# apt-get install g++ libasound2-dev scons make vim

Si il y a des montage qui n'apparaissent pas dans le chroot, le plus simple et d'editer /etc/schroot/default/fstab et de les rajouter dedans.

Et c'est bon, on a un environnement de dev un peu vintage qui permet de fabriquer des binaires bien compatibles puisque compilés sur une vieille debian des familles.

Gros dictionnaire

Un truc dont j'ai pris conscience très récemment, c'est l'importance de la taille du dictionnaire pour des algos de compression genre lzma, quand on compresse des données relativement redondantes. L'exemple typique, c'est un fichier tar contenant plusieurs fois le même fichier avec juste quelques petites altérations, le genre de chose qui arrive de temps en temps, et en tout cas qui m'arrive à moi. Si le dictionnaire lzma est assez gros, alors la compression va capter toutes ces redondances et va compresser *beaucoup plus* que si on utilise le dictionnaire par défaut. Si il est trop petit, ben ça va marcher beaucoup moins bien.

Du coup , sous linux, en utilisant un dictionnaire de 128MB (la valeur par défaut est 8MB) mon archive .7z de 54Mo passe à 19Mo seulement ! Impressive.

Ca marche aussi pour un setup.exe sous windows. Si on utilise l'excellent innosetup il y a une option pour modifier la taille de dictionnaire lzma, avec la même efficacité. C'est aussi possible avec nsis (que j'exècre), mais il faut aussi lui demander explicitement de faire une compression "solid" c-à-d de comprimer tous les fichiers en même temps, et pas un par un.

Par contre le plus gros défi c'était d'en profiter aussi sous macos. C'est plus compliqué sous osx puisque PackageMaker utilise gzip pour compresser l'archive mpkg, et là y'a pas d'options ( je ralais déjà à ce sujet ici , mais a l'époque je ne connaissais pas la feinte du dictionnaire lzma pour gagner encore plus en compression). Du coup j'ai fait une petite appli cocoa , qui embarque le mpkg dans ses ressources. Mais c'est un mpkg dont le Archive.pax.gz (qui est grosso modo un tar.gz de tous ce qu'il y a à installer) a été d'abord dégzippé puis recompressé avec lzma et un gros dictionnaire bien touffu. L'appli copie ce mpkg dans un dossier temporaire, décompresse le Archive.pax.lzma et passe ensuite le relai à l'installeur d'osx pour installer le mpkg (même pas besoin de le re-gzipper, manifestement l'installeur macos s'en accomode très bien).

Du coup on va économiser un peu de bande passante et je poireauterai un peu moins pendant les uploads de nouvelle version.

Format AIFF

mega sample rate vs giga float

J'étais en train de regarder le format des fichiers des AIFF (le .wav d'Apple), qui semble a priori un format assez basique, et pourtant mes bras m'en sont tombés quand j'ai vu comment il codent la fréquence d'echantillonnage du fichier: sur un ieee long double , un float géant de 10 bytes ! je me demande ce qui est passé dans la tête du type qui a pondu ça.

Excellent blog

Je kiffe: http://pulsar.webshaker.net/

clock_gettime is slow

..on the pandaboard. I just measured it , and each call to clock_gettime(CLOCK_MONOTONIC) takes approximately 800ns . On a core 2 quad it takes 27ns , and it has also a muuuuchh better resolution that the ARM one, which has a resolution of 1/32787th second. Using CLOCK_REALTIME or whatever does not improve the timings.

NEON optimized single precision sin / cos / exp and log

This is a translation of the SSE version to ARM NEON intrinsics. Je profite de l'occasion pour dire que : the official ARM NEON documentation is quite shitty.

ARM Cortex A9 Peak FLOPS

I have just measured the peak gflops performance for the cortex A9, using this snippet of code:



@@ int ni = 65536*16;

   __asm__ volatile("mov  r5, #0\n"
                    "vdup.f32 q0, r5\n"
                    "vdup.f32 q1, r5\n"
                    "vdup.f32 q2, r5\n"
                    "vdup.f32 q3, r5\n"
                    "vdup.f32 q4, r5\n"
                    "mov r5, %0\n"
                    "1:\n"
                    "vmla.f32 q0, q0, q0\n"
                    "vmla.f32 q1, q1, q1\n"
                    "vmla.f32 q2, q2, q2\n"
                    "vmla.f32 q3, q3, q3\n"
                    "vmla.f32 q4, q4, q4\n"
                    "subs r5, r5, #1\n"
                    "bne 1b\n" : : "r"(ni) : "q0", "q1", "q2", "q3", "q4", "r5");@@

Each loop does (4 float per vec) * (5 vmla operations) * (2 flop per vlma) = 40 flop, repeated ni times. The result for the 1GHz cortex A9 of my pandaboard: 3.99919 GFlops , which was quite expected but it is always good to see a confirmation. Reducing the number of VMLA (fused multiply add) in the loop results in a lower performance (the vlma latency is 5 cycles if I recall correctly, so that is also expected). Using separate VMUL and VADD operations produces 2 GFlops instead of 4.

Interestingly, replacing the five VLMA on size-4 vectors by 10 VLMA on size-2 vector produces almost the peak 4 GFlops: 3.64 GFlops.

Assembleur ARM

Vous serez heureux d'apprendre que je commence à masteuriser l'assembleur ARM ! gcc a quand même un peu de mal à pondre du code optimal pour NEON , et le cortex-A9 n'est quand même pas un foudre de guerre. Donc au lieu de passer mon temps à rearranger le code C un peu au pif pour qu'en sortie gcc recrache de l'asm bien gaulé, je me suis décidé a faire directement de l'asm. Un bon exemple se trouve dans les sources de ffmpeg, il y a même une FFT dont je suis sûr qu'elle dépote les platypus.

Et le fait est que les instructions NEON sont largement mieux foutues que l'espece de truc bancal qu'est SSE, avec ses 8 pauvres registres 128 bits, ses instructions qui ecrasent toujours une des deux operandes, et son shuffle dont chaque utilisation donne l'impression d'être en train de résoudre un problème de rubik-cube. NEON c'est 16 registres 128 bits, qui peuvent etre manipulés comme 32 registres 64-bits, des instructions avec deux registres sources et un registre destination (comme altivec). Des instructions plutot claires et non ambigues (contrairements aux shuffle/unpacklo/unpackhi/movehl,movelh etc de sse). Des instructions load/store pas chiantes avec l'alignement , par defaut elles marchent sur des données non alignées sur 16-bytes, mais si on est sûr de l'alignement on peut leur donner un hint qui permet de gagner quelque chose comme 10% de vitesse. Pas sûr qu'on puisse encore considerer tout ça comme du RISC, par exemple : vld1 {q0,q1}, [r0,:256]! charge d'un coup 32 octets dans les deux registres q0 et q1 , avec un hint d'alignement sur 256-bits, et post-incrémente r0 d'autant. Du coup, sur les calculs qui sont assez soft avec la mémoire j'arrive à approcher les 2 GFlops sur un coeur de cortex A9 à 1GHz ce qui commence a être assez satisfaisant.

Optimization tip for SCons on cygwin

Do not build many Environment() ! build one, and clone it when you need it. Each time Environment() is built, it prepares for the "mingw" tools, and this calls "gcc --version" and "g++ --version". On cygwin, where forking is extremely slow, this can take ages.

Python

python vient d'être élu Best Programming Language par les lecteurs du Linux Journal.

Ahhhh python....

def python_is_ok(s='do not sux'):
        print s
        s='sux'

def python_sux(l={}):
        if 'sux' in l:
                raise Exception('python sux')
        l['sux']=True

python_is_ok() # ok..
python_is_ok() # ok..

python_sux() # OK...
python_sux() # fail..

outputs:

do not sux
do not sux
Traceback (most recent call last):
  File "sux.py", line 13, in <module>
    python_sux() # fail..
  File "sux.py", line 7, in python_sux
    raise Exception('python sux')
Exception: python sux

Ok, ce comportement bizarre est indiqué dans le tutoriel de python, n'empeche que c'est louche.

A not so minimalistic OSC library

Finalement j'ai trouvé une utilité à oscpkt, ce n'est pas l'usage que je prévoyais quand j'ai commencé mais ça ma donné l'occasion de fignoler un peu le truc, de le rendre cross-plateforme (pour la partie sockets) et d'y ajouter le pattern matching sur les chemins OSC. J'ai aussi agrémenté la homepage avec une image de Troy McClure.

Ultra minimalistic OSC library

oscpkt , si ça peut servir à quelqu'un faites vous plaisir.

Quick comparison of the ARM Cortex A9 and Intel Atom

Here are two small benchmarks that I have just run on an Intel Atom N270, running at 1.6GHz, and a ARM Cortex A9 castrated by nvidia (the neon unit has been ablated in tegra2). The arm is a dual-core cpu running at 1GHz. Both are running ubuntu, the compiler used being gcc 4.4.

First bench: compilation of a very large (250000 lines) c++ file. This stresses the cpu and memory, but not the swap (the ARM one does not even have any swap). The compilation consumes ~300MB. This is of course a single-threaded test.

  • Atom: 1m15 sec.
  • Cortex: 1m49 sec.

That is a ratio of 1.44. gcc 4.4 on the Atom is much faster than on the cortex A9 (this is the gcc version of ubuntu maverick, so it is built for ARMv7).

The second test compares the anemic VFP floatting point unit of the Cortex A9 with the old Atom fpu. This is a typical linear system solve, in double precision, with pretty straighforward scalar code without any specific optimization or tricks, repeated 100 times. Standard compiler optimisations ( -O3 -ffast-math , with also -mfpu=vfpv3-d16 -march=cortex-a9 for the ARM)

  • Atom: t=4.68 sec.
  • Cortex: t=6.16 sec.

The ratio is 1.31 , so basically the same as the gcc compilation bench. Clock for clock, the cortex is slightly faster than Atom, but the Atom has a higher clock. And it still has its ballsSIMD unit, which will allow any Atom to humiliate the tegra2 cpu in any single precision floating point benchmark.

malloc lent

Le malloc de macos a la réputation d'être relativement pas rapide, et je viens de le constater sur un petit bout de code, qui passe la majeure partie de son temps à faire du malloc/free (vérifié avec Shark). Sous Snow Leopard, il met 14s à s'executer, alors que sous linux (dans une VM tournant sous macos), le même code, avec une version similaire de g++ et les mêmes optimisations, ne prend plus que 9s , ce qui fait une différence somme toute assez significative ! Voilà qui est édifiant.

Cross-compiler en 32-bit sur une debian 64-bit

Un truc que j'aime bien sur le mac c'est la facilité avec laquelle on compile pour du i386, du x86_64, ou du ppc, voire ppc64 (pour celui là c'est fini depuis Snow Leopard, macos 10.5 a été la seule version qui permette de compiler pour ces 4 architectures). Sous windows, avec visual c++, c'est pas super commode mais MS fournit des platform SDK pour i386 et x64 qui sont suffisants (y'a un milliard de dlls là dedans). En plus aussi bien windows que macos savent executer n'importe quelle appli 32-bit sur la variante 64-bit de l'OS. Par contre sous linux, quand on choisit d'installer une distro 64-bit, c'est qu'on a decidé de bannir le 32-bit (qui pue), parce que le 64-bit saitrogénial. Donc par défaut les paquets du genre ia32-libs ne sont pas installés. Les users ne peuvent pas executer simplement n'importe quelle appli 32-bit sur leur gentoo hyper tunée pour aller trop vite parce que les libs sont manquantes. Et c'est la croix et la bannière pour mettre en place un environnement qui permette de compiler des applis non triviales en i386 sur la distro x86_64. En tout cas moi j'ai pas mal galéré mais comme c'était il y a un bout de temps, sur une debian Lenny, j'ai oublié, du coup je vais noter ici un ou deux trucs pour m'en souvenir.

Le probleme, c'est que ia32-libs et ia32-libs-gtk ça ne fournit qu'un sous ensemble des libs qu'on peut avoir besoin d'utiliser... Par exemple, y'a pas libasound (ALSA), c'est ballot. Y'a pas liblo non plus. Etc. Alors comment qu'on fait ?

Dans l'ordre des trucs que j'ai essayé je crois que j'ai tenté l'approche "je compile le cross compilateur, qui va cross-compiler toutes les libs dont j'ai besoin", tout ça sur un core 2 solo 1.2GHz sous-ventilé autant dire que ça a vite tourné à l'échec critique.

Ensuite j'ai mis en place un chroot dans lequel j'ai installé une debian 32-bits, super pénible, pas pratique (faut chrooter pour aller compiler en 32-bit). Mais ça marche et j'ai utilisé cette méthode pendant un certain temps.

Et puis finalement la methode qui marche et qui est relativement pratique, c'est juste de telecharger les .deb i386 à la main, et de les decompresser directement dans /emul/ia32-linux . Par exemple pour liblo:

wget http://ftp.debian.org/debian/pool/main/libl/liblo/liblo0ldbl_0.23-2.2_i386.deb
dpkg -X liblo0ldbl_0.23-2.2_i386.deb /emul/ia32-linux

(et pareil pour le paquet liblo0-dev )

Et ça roulaize.

Avoiding objective-c class name conflicts with brute force

The issue is described here : there is no way to control the visibility an objective-c class, and in a plugin context where many plugins might embed variations of a unique library that means conflicts, and ugly crashes. The usual solution, as far as I understand, is just to append some prefix to your own class names. And if you build more than one product, make sure that this prefix is unique for each product. Other solutions are listed here : shared framework, or building classes at runtime but I don't want to consider them for now. So I suggest another solution, which is a brutal but efficient variation of the unique prefix appended to obj-c class names: just append a specific prefix to the names of all you obj-c classes names , lets say "BALAI_BROSSE". Compile your products as usual. And then, for each product, generate a random string of the same length, let's say "OIETDKFJKDSF" , and just do a binary search & replace on the compiled binary, replacing all occurences of BALAI_BROSSE with OEITDKFJKDSF. This step (generating the random string and doing the binary search&replace) can be easily integrated into the build process.

The program I am using to do that is here: binrepl.cc. But feel free to find it dumb and ugly, I'm sure it can be done in one line of perl.

And apparently it works perfectly , I'm not getting anymore the Class Foobar_BALAI_BROSSE is implemented in both foo0 and foo1. One of the two will be used. Which one is undefined. warnings . Which is great.

Déçu par le packagemaker de macos

Le packagemaker c'est l'appli qui permet de fabriquer des installeurs pour macos. Par le passé on ne pouvait pas faire grand chose en ligne de commande, mais maintenant c'est mieux et on peut creer des packages sans dégainer la souris et aller traffiquer dans la gui.

Par contre quand je compare la taille d'un package generé sous macos et celle du même installeur sous windows (fabriqué avec NSIS), la version mac est notablement plus grosse. J'avais mis ça sur le compte des fat binaries puisque la version mac embarque aussi des binaires ppc, mais il semblerait qu'il y ait aussi une autre explication: la compression de packagemaker juste pue. C'est du gzip des année 30. Si je compresse les fichiers du package avec lzma je me retrouve avec une archive 18Mo , alors que le mpkg pour les mêmes fichiers occupe 25Mo :'(

Ainsi j'en appelle à Steve Jobs pour qu'il fixe ça au plus vite ! Merci d'avance !

La dure vie du plugin

C'est pas tous les jours facile d'être un plugin. Le probleme principal du plugin c'est les autres. Parce qu'il n'est pas chargé seul, il est chargé en même temps que d'autres plugins plus ou moins bien elevés, et il doit partager le même espace mémoire qu'eux. Du coup quand un plugin débile commence à faire n'importe quoi et à pourrir le heap, ben c'est pas forcement lui qui plante et qui est accusé d'être buggé. C'est pas non plus toujours facile de convaincre l'auteur du plugin moisi que le bug est de son coté et pas du mien. Et ça ne fait pas non plus toujours plaisir de perdre du temps à aller identifier et corriger les bugs des autres. Bref.

Mais il n'y a pas que l'espace mémoire qui est partagé. Bien souvent, les plugins executent une partie de leur code sur les même threads (cad sequentiellement, l'un après l'autre sur le même thread et pas en parallele). Et ça c'est la porte ouverte à plein d'effets de bords merdiques.

Donc aujourd'hui, l'effet de bord merdique c'est quand un plugin s'amuse à modifier les flags du FPU et a les laisser dans un état completement naze. Par exemple , en construisant sa GUI avec Direct3D: http://blogs.msdn.com/tmiller/archive/2004/06/01/145596.aspx . Car le FPU x87 a un super mode qui force la precision de tous les calculs à 24 bits (cad la simple precision) au lieu de 53 bits (double precision) habituels. Et là c'est le drame, les systèmes linéaires pas trop bien conditionnés commencent à renvoyer des solutions vraiment très fausses, les fonctions exp() etc du runtime c++ commence à devier mechamment , le toolkit de la gui perd completement les pedales et oublie de raffraichir des bouts de gui, affiche une lettre sur deux etc..

visual c++ : sux

Bon j'en ai marre de lire un peu partout que gcc est mauvais et que msvc est un excellent compilateur (pareil pour icc). En tout cas pour ce qui concerne le SSE msvc est juste une bouse abominable qui ne sait pondre que du bloat bien lent. Et mon experience est que gcc est presque toujours meilleur que icc sur du code contenant des intrinsincs SSE, malgré toutes les options bling bling de ce dernier. Ca fait plaisir de voir que c'est confirmé ici: http://www.liranuna.com/sse-intrinsics-optimizations-in-popular-compilers/

Mini coup de gueule contre std::sort

Aujourd'hui cher blog j'aimerais pousser un petit coup de gueule contre std::sort, dont le comportement n'est pas hyper developper-friendly quand on lui fourni une fonction de comparaison un peu buggée. Figure-toi cher lecteur que lorsque la fonction de comparaison n'est pas tout à fait un StrictWeakOrdering (genre elle renvoie a<b , b<c et a>c), le comportement du std::sort de la libstdc++ est tout simplement de partir en excursion hors des bornes du tableau qu'on est en train de trier. A l'arrivée ça crashe dur. Alors certes je ne doute pas que l'implementation de la libstdc++ est hyper-optimale et plus rapide que son ombre, néanmoins j'apprécierais qu'elle detecte quand la fonction de comparaison n'est pas coherente plutot que d'exploser en vol comme une grosse baleine en allant trier des elements qui ne sont pas dans le tableau. ça me fait râler parce que ça fait plusieurs fois que je me fais avoir par ce truc (oui j'ai du mal a écrire des StrictWeakOrdering sans bugs)

Merging .text and .rdata on windows

Not sure that this may ever be useful to someone else, but here is how to merge the .text and .rdata of a windows PE file when compiling with mingw or cygwin. The msvc linker has a built-in option to do that

/MERGE:.rdata=.text

But the gnu linker do not provide such an option. Instead it relies on linker scripts. The following linker script "ld_script_merge_rdata_and_text.x" does the trick:

SECTIONS {
 .text : {
  *(.text)
  *(.rdata)
  *(SORT(.rdata$*))
}

I won't explain how it works because I don't really understand it /o\ there was some amount of trial and errors before I converged to this script. Here is an example of use (note that is just passed to the linker as an implicit linker script, the default linker script is not overridden):

echo 'int main() { printf("Quack quack !"); return 0; }' > quack.c
gcc -Wl,ld_script_merge_rdata_and_text.x quack.c && objdump -h a.exe

The output is:

a.exe:     file format pei-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000003a8  00401000  00401000  00000200  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA
  1 .bss          00000040  00402000  00402000  00000000  2**2
                  ALLOC
  2 .idata        00000188  00403000  00403000  00000600  2**2
                  CONTENTS, ALLOC, LOAD, DATA

Look mum ! no .rdata !

Tinypy

L'autre jour en feuilletant freshmeat je suis tombé sur tinypy. ça a juste l'air excellent, du python embarquable en 64k avec la vm et l'interpreteur, tout ça sous license MIT.. ça fait un bout de temps que j'ai envie d'embarquer un petit langage de scripting dans une appli pour faire des choses hyper-puissantes mais le problème c'est que je n'en ai pas besoin, et que je n'ai pas d'idées de choses hyper-puissantes :'(

Je conchie les iostream

Je profite d'avoir un blog pour cracher un peu de bile: je hais les iostream du c++, ce truc est la honte des flux d'IO (enfin pas tout a fait, il faudrait aussi aborder le cas du Fortran). En bref les iostream c'est:

  1. lourd: rien que les cout << "plop" << std::setw(10) << std::hex << i << "\n" ça me donne déjà envie de casser mon clavier, et pourtant je n'ai même pas essayé de formatter un nombre en virgule flottante.
  2. lent (plus lent que les io du C)
  3. pas integré avec la stl. Ok la stl est venue bien après, mais quand même... un std::fstream qui accepte les noms de fichier en std::string ça serait pas mal..
  4. imbitable et pas du tout extensible: c'est un comble ! comment on fait un flux qui lit depuis la mémoire ? qui dézippe à la volée ? qui crypte à la volée ? C'est affreusement compliqué, ça oblige à se plonger dans les streambuf qui sont un chef d'oeuvre d'obfuscation avec leurs fonctions aux noms evocateurs "xsgetn", "gbump", "setg". Pour faire un iostream qui dezippe il faut surcharger une dizaine de ces fonctions merdiques et c'est hyper compliqué. Résultat: chaque toolkit, chaque petite lib, construit sa propre classe de flux from scratch parce que c'est impossible de repartir des iostream. Bravo Stroustrup ! (en fait je crois que ce n'est pas lui qui a commit les iostream)
  5. autiste: y'a pas de diagnostique des erreurs ! pourtant en C, "strerror" c'est bien sympa pour savoir si on n'a pas pu lire toto.txt parce qu'il n'existe pas , ou bien parce qu'il y a un probleme de permissions. Avec les iostream on sait juste que c'est "fail", après demerde toi.

Alors oui, si il y a un truc que je conchie dans le c++, c'est bien ses iostreams.

Le dicton c'est bon

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. – Brian W. Kernighan

Premières impressions sur Leopard

En fait je crois que toutes les upgrades d'os se passent pareil. D'abord on est impatient, on fait des bonds quand ups livre la boite un vendredi à 17h alors que tout espoir semblait perdu. On installe le truc, c'est long, ça marche, on s'emerveille sur les nouveautés (spaces, quicklook) et puis on commence à travailler et là c'est le drame, la série de machins qui marchent plus, ou mal, et que c'est vraiment super lourd.

Leopard n'y coupe pas puisque X11.app est plus buggé que jamais:

  • comportement bizarre quand on utilise alt-tab pour le faire passer au premier plan
  • j'arrive pas a lui faire simuler les middle clic (ni même les right-click) avec alt+click et pomme+clic, je comprends pas ils ont du le faire exprès y'a l'option dans les prefs et elle ne MARCHE PAS.

Ma belle partoche cryptée a été déclarée comme "partiellement corrompue" par Leopard, qui m'a quand même laissé un accès en lecture seul le temps d'en faire une copie: bizarre.

Et enfin apple a fait evoluer le SDK coreaudio, c'est bien sauf que j'ai une appli qui ne compile plus parce qu'ils ont rajouté des fonctions virtuelles pure dans leur classe: relou.

Resultat j'ai restauré une copie de Tiger dans un partoche voisine de Leopard. J'hesite...

Choix d'un toolkit

rodinY'a un truc que j'aime pas c'est de devoir choisir un toolkit graphique et en assumer la responsabilité. C'est pas evident de faire un choix qu'on ne regrette pas, parce que mine de rien ces bêtes là ont tendance à planter leurs racines assez profondement dans le code dès qu'on commence à les utiliser: chacun vient avec ses propres strings plus ou moins unicode, ses propres abstrations de flux d'io (zip, fichier, memoire etc), ses mutex, ses threads, son implémentation des shared_ptr et de quelques structures de données elementaires. Sa propre manière de nommer les fonctions et les classes aussi, et j'aime pas les mélanges, moi mes classes s'appellent CommeCa, et le reste c'est comme_ça.

Un toolkit graphique ça impose aussi ses propres classes d'exception, son code d'initialisation spécifique, parfois même un préprocesseur adapté. Et après il faut peser le pour et le contre, entre celui qui fait tout (y compris des executables de 10Mo minimum, et qui prends 10h à compiler) et celui qui fait peu de choses et pas très bien. Entre celui qui est gpl avec une option proprio à 1500 euros par développeur et par OS, celui qui est lgpl avec 42 dependances, et celui qui est bsd. Il faut aussi peser la perennité du bousin, ça serait ballot de se casse le luc à faire un portage vers un environnement en cours d'abandon, ou bien un environnement qui a fait de mauvais choix techniques. Il faut choisir si on en veut un qui utilise les exceptions ou si au contraire on veut se passer des exceptions. Il faut prendre en compte la portabilité. La qualité de la doc aussi. La qualité du code aussi, parce qu'ils sont plus ou moins testés, plus ou moins buggés. Tout ça c'est pas facile, et quand je vois le code de certaines boites qui sont coincées depuis des années avec des toolkits de "compatibilité" cross-plateforme (une émulation moisie des api macos sous windows, dont l'éditeur n'existe plus) je me dis qu'un mauvais choix peut être cherement payé.

L'heure est grave.

Actuellement mon choix se porte sur Juce dont le domaine d'applications me convient plutôt bien, qui est adapté aux gui de jackies (la démo est assez sympa), et qui est raisonnablement petit (par rapport à Qt, gtk ou wx) sans être ridicule.

ouuff

Ce soir j'ai appris que les trucs du genre <form action="<?php echo $_SERVER['PHP_SELF']; ?>"> sont non seulement inutiles (le form action="" marche aussi), mais sont aussi exploitables en XSS. J'aurais un peu réflechi et lu attentivement la doc php, je m'en serais peut être douté mais il faut bien avouer qu'elle n'est pas très alarmiste et un peu misleading... je cite: 'PHP_SELF' The filename of the currently executing script, relative to the document root. a ceci près que c'est pas exactement un filename, et que l'utilisateur peut le manipuler comme bon lui semble, comme expliqué ici. Puxor.

cygwin tuning

J'aime bien cygwin. Un peu d'unix dans un monde de brutes. Y'a quand même deux-trois petits trucs dans la conf par défaut qui me défrisent. Tout d'abord il y a le nom dont la prononciation me semble douteuse. "sailleg-win" ou "sigouine" ?

Il y a aussi le /cygdrive qui préfixe tous les chemins, c'est laid. J'imagine qu'il a une raison d'être, mais c'est laid, et c'est chiant à taper. Et c'est different de msys ce qui peut poser probleme quand on veut utiliser des scripts indifferement sous l'un ou l'autre. Heureusement, on peut corriger ça avec la commande magique que je vais m'empresser de noter ci-dessous parce que j'ai toujours un mal de chien à la retrouver quand je la cherche:

 mount --change-cygdrive-prefix /

Il suffit de la lancer une fois pour toutes, et on accède au disque c: avec /c/ etc. ça poutror. cygwin c'est vraiment bien.

Un autre truc que j'aime pas, et même que je conchie, c'est l'espèce de bouse qui sert de terminal sous windows, ce truc tout moche, non redimensionnable, avec un copier coller anti convivial au possible franchement j'échangerai pas un baril d'xterm contre un container de consoles windows. D'un autre côté j'ai rarement envie de me coltiner le serveur X de cygwin pour lancer un authentique xterm, alors dans ce cas la solution c'est de modifier cygwin.bat pour lancer rxvt à la place d'un bash tout bête.

Donc je remplace

bash --login -i

par

c:\cygwin\bin\rxvt -fn 9x16 -bg black -fg white -sl 2500 -title cygwin -e bash --login -i

Et là miracle, c'est beau, c'est réactif, c'est redimensionnable, y'a une scrollbar, et le copier coller c'est bonheur.

Octave et tuning d'octaverc

J'essaye de me désintoxiquer de matlab en douceur, alors je me lance dans la convertion de quelques scripts pour qu'ils fonctionnent correctement sous octave. Le premier reflexe c'est de prendre les sources du bouzin et de les compiler, mais

  • il y a quand même un bon petit paquet de dépendances
  • ça prend des putain de plombes (eh oui c'est du c++) (si on considère qu'une plombe dure une heure)

Comme d'habitude avec les projets libres, c'est un peu le bordel, en fait le bon site pour octave c'est octave-forge , là on a accès à un certain nombre de toolboxes (qui ne fonctionnent que sur la version bleeding-edge), et à des binaires precompilés pour macos et windows.

Comme d'habitude avec les projets libres, la conf par défaut est un peu merdique (oui je pense très fort à emacs): le pager est ultra pénible, le prompt est moche, et chaque écran d'aide est pourri par du bullshit indiquant qu'il y a soit-disant plus d'info dans la doc en ligne et blahblahblah.

Voici donc mon .octaverc, il est tout pourri mais ça rend la ligne de commande d'octave un peu plus conviviale, avec un prompt de jacky en vert:

more off;
PS1('[\033[1;32m]\W[\033[0m] >> ');
debug_on_interrupt(1);
suppress_verbose_help_message(1);

Sinon, pour reprendre la comparaison avec Matlab, j'ai été agréablement surpris par le niveau de compatibilité d'octave -- ça déchire, y compris sur les mex files ! En terme de stabilité c'est pas tout à fait ça, la 2.9.13 que j'ai testé plante quand je fais un dbquit. La rapidité est honorable pour tout ce qui est sous forme de tableaux, mais les boucles sont très lente par rapport à matlab (un facteur 100), pas étonnant puisque matlab a un compilateur jit. Le point noir c'est les graphiques, franchement utiliser gnuplot ça craint, je hais ce truc. Et utiliser imagemagick pour les pcolor et les specgram ça craint encore plus :'(

sinus, cosinus, log et exp vectoriel en SSE

pouet pouet Pour commencer ce blog en fanfare, j'offre au monde ébahi les fonctions sin_ps, cos_ps, exp_ps et log_ps qui prennent en entrée un petit vecteur de 4 float et recrachent les 4 resultats correspondants, tout ça en un temps bref et avec une précision tout a fait honorable. C'est du SSE1 donc ça marche même sur les vieux athlons et pentium 3, c'est écrit en C avec les fonctions intrinsèques SSE, donc ça compile sans probleme avec gcc, icc, msvc, .. Rien de révolutionnaire, mais ça marche pas trop mal.

C'est ici: http://gruntthepeon.free.fr/ssemath