
Esta historia trata sobre un enfoque no estándar para el desarrollo de aplicaciones de Android. Una cosa es instalar Android Studio y escribir "Hello, World" en Java o Kotlin. Pero mostraré cómo se puede realizar la misma tarea de manera diferente.
Le recordamos: para todos los lectores de "Habr": un descuento de 10.000 rublos al registrarse en cualquier curso de Skillbox con el código de promoción "Habr".
Skillbox recomienda: El curso educativo en línea "Profession Java-developer" .
¿Cómo funciona mi teléfono inteligente con el sistema operativo Android?
Primero, un poco de historia. Una tarde una amiga llamada Ariella me llamó. Ella me preguntó: "Escucha, ¿cómo funciona mi teléfono inteligente? ¿Qué hay dentro? ¿Cómo la energía eléctrica y las unidades ordinarias y ceros hacen que todo funcione?
Mi amiga no es una novata en desarrollo, creó varios proyectos en Arduino, que consistían tanto en software como en hardware. Tal vez por eso quería saber más. Logré responder con la ayuda del conocimiento obtenido en uno de los cursos de informática tomados en la universidad.
Luego trabajamos un par de semanas juntas, ya que Ariella quería descubrir cómo funcionan los ladrillos de la tecnología electrónica, es decir, los elementos semiconductores, incluidos los transistores. Luego pasamos a un nivel superior: le mostré cómo crear puertas lógicas, por ejemplo, NAND (lógico Y) más NOR (lógico OR) usando una combinación específica de transistores.
Investigamos elementos lógicos de diferentes tipos, los combinamos para realizar cálculos (por ejemplo, agregar dos números binarios) y celdas de memoria (disparadores). Cuando todo quedó claro, comenzaron a desarrollar un procesador simple (imaginario), en el que había dos registros de propósito general y dos instrucciones simples (agregar estos registros). Incluso escribimos un programa simple que multiplica estos dos números.
Por cierto, si está interesado en este tema, lea las instrucciones para crear una computadora de 8 bits desde cero. Explica casi todo, desde lo más básico. ¡Ojalá lo hubiera leído antes!
Hola android
Después de completar todas las etapas del estudio, me pareció que Ariella tenía suficiente conocimiento para comprender cómo funciona el procesador del teléfono inteligente. Su teléfono inteligente es el Galaxy S6 Edge, cuya base es la arquitectura ARM (como, de hecho, con la mayoría de los teléfonos inteligentes). Decidimos escribir una aplicación "Hello, World" para Android, pero en lenguaje ensamblador.
.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 nunca antes ha encontrado código de ensamblador, este bloque puede asustarlo. Pero está bien, analicemos el código juntos.
Entonces, nuestro programa consta de dos partes. El primero es texto con instrucciones de código de máquina, y el segundo son variables, líneas y otra información (comenzando con la línea 15). La sección .text generalmente es de solo lectura y .data también se puede escribir.
En la línea 2, definimos una función global llamada _start. Es el punto de entrada a la aplicación. El sistema operativo comienza a ejecutar el código desde este punto. La definición de la función se declara en la línea 4.
Además, la función hace dos cosas más. En las líneas 5–9, el mensaje se muestra en la pantalla; en las líneas 11–13, el programa finaliza. Incluso si elimina las líneas 11–13, el programa mostrará nuestra línea "Hola, Mundo" y saldrá. Sin embargo, la salida no será correcta, porque el programa terminará con un error. Sin las líneas 11–13, la aplicación intentará ejecutar una instrucción no válida.
La impresión se muestra utilizando la función del sistema "Llamada del sistema" del sistema operativo. En la aplicación, llamamos a la función write (). Lo indicamos cuando cargamos el valor 4 en el registro del procesador con el nombre r7 (línea 8). A continuación, se ejecuta la instrucción swi $ = 0 (línea 9), donde hay una transición directamente al kernel de Linux, que es la base de Android.
En cuanto a los parámetros para la llamada al sistema, se transmiten a través de otros registros. Por ejemplo, r0 muestra el número del descriptor de archivo que necesitamos imprimir. Ponemos el valor 1 allí (línea 5), indicando salida estándar (stdout), es decir, salida a la pantalla.
r1 indica la dirección de memoria de los datos que queremos escribir, por lo que simplemente cargamos la dirección de la cadena "Hola, Mundo" (línea 6) en esta área, y el registro r2 muestra cuántos bytes queremos escribir. En nuestro programa, está configurado en message_len (línea 7), calculado en la línea 18 usando una sintaxis especial: el símbolo de punto indica la dirección de memoria actual. Por esta razón - mensaje indica la dirección de memoria actual menos la dirección del mensaje. Bueno, como declaramos message_len inmediatamente después del mensaje, todo esto se calcula como la longitud del mensaje.
Si escribe el código para las líneas 5–9 usando C, obtendrá lo siguiente:
#define message "Hello, World\n" write(1, message, strlen(message));
Cerrar el programa es un poco más fácil. Para hacer esto, simplemente registramos el código de salida en el registro r0 (línea 11), después de lo cual agregamos el valor 1, que es el número de la llamada a la función del sistema exit (), a r7 (línea 12), luego llamamos nuevamente al núcleo (línea 13).
Puede encontrar una lista completa de las llamadas al sistema Android y sus números en el código
fuente del sistema operativo . También hay una implementación de
write () y
exit () que llama a las funciones del sistema correspondientes.
Poniendo el programa juntos
Para compilar nuestro proyecto, necesitará Android NDK (Native Development Kit). Contiene un conjunto de compiladores y herramientas de compilación para la plataforma ARM. Puede descargarlo desde el sitio oficial, instalarlo, por ejemplo, a través de Android Studio.

Después de instalar el NDK, necesitamos el archivo arm-linux-androideabi-as, este es el ensamblador para ARM. Si descargaste a través de Android Studio, búscalo en la carpeta del SDK de Android. Por lo general, su ubicación es
ndk-bundle \ toolchains \ arm-linux-androideabi-4.9 \ prebuilt \ windows-x86_64 \ bin.Después de encontrar el ensamblador, guarde lo que está escrito en un archivo llamado hello.s, y luego ejecute el siguiente comando para convertirlo en código de máquina:
arm-linux-androideabi-as -o hello.o hello.sEsta operación crea un archivo de objeto ELF llamado hello.o. Para convertirlo a un archivo binario que pueda funcionar en su dispositivo, llame al vinculador:
arm-linux-androideabi-ld -o hola hola.oAhora tenemos un archivo de saludo que contiene un programa que está listo para usar.
Inicie la aplicación en su dispositivo
Las aplicaciones de Android generalmente se distribuyen en formato .apk. Este es un tipo especial de archivo comprimido que incluye clases Java (sí, puede escribir con C / C ++, pero Java debe ser el punto de entrada).
Para evitar problemas al iniciar la aplicación, se utilizó adb en el ejemplo, que nos permitió copiarlo en la carpeta temporal de nuestro dispositivo Android. Después de eso, lanzamos el shell adb para iniciar la aplicación y evaluar el resultado:
adb push hello / data / local / tmp / hello
adb shell chmod + x / data / local / tmp / helloY finalmente, ejecute la aplicación:
adb shell / data / local / tmp / helloQue escribes
Ahora tiene un entorno de trabajo similar al que tenía Ariella. Pasó varios días estudiando el ensamblador ARM, luego se le ocurrió un proyecto simple: este es el juego Sven Boom (una variación de Fizz Buzz originario de Israel). Los jugadores cuentan por turno y cada vez que el número se divide por 7 o contiene el número 7, deben decir "boom" (de ahí el nombre del juego).
Vale la pena señalar que el programa del juego no es una tarea tan fácil. Ariella escribió un método completo que muestra números, un dígito a la vez. Como ella escribió todo en ensamblador sin llamar a las funciones estándar de la biblioteca C, le llevó varios días resolverlo.
El primer programa de Ariella para Adnroid está disponible
aquí . Por cierto, algunos identificadores de código en la aplicación son en realidad palabras hebreas (por ejemplo, _sifra_ahrona).
Escribir una aplicación de Android en ensamblador es una buena manera de conocer mejor la arquitectura ARM, y también de comprender mejor la cocina interna del dispositivo que usa a diario. Le sugiero que haga esto de cerca e intente crear una pequeña aplicación de ensamblador para su dispositivo. Podría ser un juego simple o algo más.
Skillbox recomienda: