Écrire une application Android dans l'assembleur

image

Cette histoire concerne une approche non standard du développement d'applications Android. C'est une chose d'installer Android Studio et d'écrire "Hello, World" en Java ou Kotlin. Mais je vais montrer comment la même tâche peut être exécutée différemment.

Nous vous rappelons: pour tous les lecteurs de «Habr» - une remise de 10 000 roubles lors de l'inscription à un cours Skillbox en utilisant le code promo «Habr».

Skillbox recommande: Le cours éducatif en ligne "Profession Java-developer" .

Comment fonctionne mon smartphone avec Android OS?


Tout d'abord, un peu d'histoire. Un soir, un ami appelé Ariella m'a appelé. Elle m'a demandé: «Écoutez, comment fonctionne mon smartphone? Qu'y a-t-il à l'intérieur? Comment l'énergie électrique et les unités et zéros ordinaires font-ils fonctionner tout cela? »

Mon amie n'est pas une noob en développement, elle a créé plusieurs projets sur Arduino, qui comprenaient à la fois des logiciels et du matériel. C'est peut-être pour ça qu'elle voulait en savoir plus. J'ai réussi à répondre grâce aux connaissances acquises dans l'un des cours d'informatique suivis à l'université.

Ensuite, nous avons travaillé quelques semaines ensemble, car Ariella voulait savoir comment fonctionnent les briques de la technologie électronique, c'est-à-dire les éléments semi-conducteurs, y compris les transistors. Ensuite, nous sommes allés à un niveau supérieur: je lui ai montré comment créer des portes logiques, par exemple, NAND (ET logique) plus NOR (OU logique) en utilisant une combinaison spécifique de transistors.

Nous avons étudié des éléments logiques de différents types, les avons combinés pour effectuer des calculs (par exemple, en ajoutant deux nombres binaires) et des cellules de mémoire (déclencheurs). Lorsque tout est devenu clair, ils ont commencé à développer un processeur simple (imaginaire), dans lequel il y avait deux registres à usage général et deux instructions simples (en ajoutant ces registres). Nous avons même écrit un programme simple qui multiplie ces deux nombres.

Soit dit en passant, si vous êtes intéressé par cette rubrique, lisez les instructions pour créer un ordinateur 8 bits à partir de zéro. Cela explique presque tout, depuis les bases. J'aurais aimé le lire plus tôt!

Bonjour Android!


Après avoir terminé toutes les étapes de l'étude, il me semblait qu'Ariella avait suffisamment de connaissances pour comprendre le fonctionnement du processeur du smartphone. Son smartphone est le Galaxy S6 Edge, dont la base est l'architecture ARM (comme, en fait, avec la plupart des smartphones). Nous avons décidé d'écrire une application «Hello, World» pour Android, mais en langage assembleur.

.text .globl _start _start: mov %r0, $1 // file descriptor number 1 (stdout) ldr %r1, =message mov %r2, $message_len mov %r7, $4 // syscall 4 (write) swi $0 mov %r0, $0 // exit status 0 (ok) mov %r7, $1 // syscall 1 (exit) swi $0 .data message: .ascii "Hello, World\n" message_len = . - message 

Si vous n'avez jamais rencontré de code assembleur auparavant, ce bloc peut vous faire peur. Mais ça va, analysons le code ensemble.

Donc, notre programme se compose de deux parties. Le premier est un texte avec des instructions de code machine, et le second est des variables, des lignes et d'autres informations (en commençant par la ligne 15). La section .text est généralement en lecture seule et .data est également accessible en écriture.

À la ligne 2, nous définissons une fonction globale appelée _start. C'est le point d'entrée de l'application. Le système d'exploitation commence à exécuter le code à partir de ce point. La définition de la fonction est déclarée à la ligne 4.

De plus, la fonction fait deux autres choses. Aux lignes 5 à 9, le message s'affiche à l'écran; aux lignes 11 à 13, le programme se termine. Même si vous supprimez les lignes 11 à 13, le programme affichera notre ligne «Hello, World» et quittera. Cependant, la sortie ne sera pas correcte, car le programme se terminera par une erreur. Sans les lignes 11 à 13, l'application essaiera d'exécuter une instruction non valide.

L'impression est affichée à l'aide de la fonction système "Appel système" du système d'exploitation. Dans l'application, nous appelons la fonction write (). Nous l'indiquons lorsque nous chargeons la valeur 4 dans le registre du processeur avec le nom r7 (ligne 8). Ensuite, l'instruction swi $ = 0 (ligne 9) est exécutée, où il y a une transition directe vers le noyau Linux, qui est la base d'Android.

Quant aux paramètres de l'appel système, ils sont transmis via d'autres registres. Par exemple, r0 indique le numéro du descripteur de fichier que nous devons imprimer. Nous y mettons la valeur 1 (ligne 5), indiquant la sortie standard (stdout), c'est-à-dire la sortie à l'écran.

r1 indique l'adresse mémoire des données que nous voulons écrire, nous chargeons donc simplement l'adresse de la chaîne "Hello, World" (ligne 6) dans cette zone, et le registre r2 montre combien d'octets nous voulons écrire. Dans notre programme, il est défini sur message_len (ligne 7), calculé sur la ligne 18 en utilisant une syntaxe spéciale: le symbole de point indique l'adresse mémoire actuelle. Pour cette raison. - le message indique l'adresse mémoire actuelle moins l'adresse du message. Eh bien, puisque nous déclarons message_len immédiatement après le message, tout cela est calculé comme la longueur du message.

Si vous écrivez le code des lignes 5 à 9 à l'aide de C, vous obtenez les informations suivantes:

 #define message "Hello, World\n" write(1, message, strlen(message)); 

Arrêter le programme est un peu plus facile. Pour ce faire, nous enregistrons simplement le code de sortie dans le registre r0 (ligne 11), après quoi nous ajoutons la valeur 1, qui est le numéro de l'appel de fonction système exit (), à r7 (ligne 12), puis appelons à nouveau le noyau (ligne 13).

Une liste complète des appels système Android et de leurs numéros se trouve dans le code source du système d'exploitation . Il existe également une implémentation de write () et exit () qui appellent les fonctions système correspondantes.

Assembler le programme


Afin de compiler notre projet, vous aurez besoin d'Android NDK (Native Development Kit). Il contient un ensemble de compilateurs et d'outils de construction pour la plate-forme ARM. Vous pouvez le télécharger sur le site officiel, l'installer, par exemple, via Android Studio.



Une fois le NDK installé, nous avons besoin du fichier arm-linux-androideabi-as, c'est l'assembleur pour ARM. Si vous avez téléchargé via Android Studio, recherchez-le dans le dossier Android SDK. Son emplacement est généralement

ndk-bundle \ toolchains \ arm-linux-androideabi-4.9 \ prebuilt \ windows-x86_64 \ bin.

Une fois l'assembleur trouvé, enregistrez ce qui est écrit dans un fichier appelé hello.s, puis exécutez la commande suivante pour le convertir en code machine:

arm-linux-androideabi-as -o hello.o hello.s

Cette opération crée un fichier objet ELF appelé hello.o. Afin de le convertir en un fichier binaire qui peut fonctionner sur votre appareil, appelez l'éditeur de liens:

arm-linux-androideabi-ld -o bonjour bonjour.o

Nous avons maintenant un fichier bonjour qui contient un programme prêt à l'emploi.

Lancez l'application sur votre appareil


Les applications Android sont généralement distribuées au format .apk. Il s'agit d'un type spécial de fichier compressé qui inclut des classes Java (oui, vous pouvez écrire en C / C ++, mais Java doit être le point d'entrée).

Afin d'éviter les problèmes lors du démarrage de l'application, adb a été utilisé dans l'exemple, ce qui nous a permis de le copier dans le dossier temporaire de notre appareil Android. Après cela, nous lançons le shell adb afin de lancer l'application et d'évaluer le résultat:

adb push bonjour / data / local / tmp / bonjour
adb shell chmod + x / data / local / tmp / hello

Et enfin, lancez l'application:

shell adb / data / local / tmp / bonjour

Qu'écrivez-vous?


Vous avez maintenant un environnement de travail similaire à celui qu'Ariella avait. Elle a passé plusieurs jours à étudier l'assembleur ARM, puis a proposé un projet simple - c'est le jeu Sven Boom (une variation de Fizz Buzz originaire d'Israël). Les joueurs comptent à leur tour et chaque fois que le nombre est divisé par 7 ou contient le nombre 7, ils doivent dire «boom» (d'où le nom du jeu).

Il convient de noter que le programme de jeu n'est pas une tâche aussi facile. Ariella a écrit toute une méthode qui affiche les chiffres, un chiffre à la fois. Puisqu'elle a tout écrit en assembleur sans appeler les fonctions standard de la bibliothèque C, cela a pris plusieurs jours à résoudre.

Le premier programme Ariella pour Adnroid est disponible ici . Soit dit en passant, certains identificateurs de code dans l'application sont en fait des mots hébreux (par exemple, _sifra_ahrona).

L'écriture d'une application Android dans l'assembleur est un bon moyen de mieux connaître l'architecture ARM et de mieux comprendre la cuisine intérieure du gadget que vous utilisez quotidiennement. Je vous suggère de faire cela de près et d'essayer de créer une petite application d'assemblage pour votre appareil. Ce pourrait être un jeu simple ou autre chose.

Skillbox recommande:

Source: https://habr.com/ru/post/fr441798/


All Articles