martes, 2 de noviembre de 2010

Dalvik VM: Ficheros .dex

Dalvik VM: Ficheros .dex: "

En esta entrada vamos a hablar de la estructura interna de los ficheros .dex. Lo primero, decir que, los ficheros .dex se encuentran empaquetados dentro los archivos .apk (Android Package). A continuación podemos ver una imagen con la estructura interna de estos ficheros:


dex_structure


Como podemos apreciar, un fichero .dex esta divido en distintas secciones llamadas “pools”. El pool de strings contiene todos los Strings que las clases dentro del .dex usan. En pool de tipos (type_ids) se guardan los distintos tipos datos usados en la aplicación, etc. Además de las secciones mostradas en la imagen, en la estructura actual de los ficheros dex existe una sección más por debajo de la de datos (data) llamada link_data o enlace de datos. En este momento dicha sección no está documentada. Todo lo que dice la documentación oficial es que en ella se guarda información sobre los ficheros enlazados estáticamente. Si el fichero no es enlazado esta sección permanece vacía y dicha documentación concluye diciendo que dicha sección se use como mejor se adecue a nuestra implementación.


Veamos la estructura de cada sección.



Antes de nada vamos a ver los distintos tipos de datos y su longitud.


























































NameDescription
byte8-bit signed int
ubyte8-bit unsigned int
short16-bit signed int, little-endian
ushort16-bit unsigned int, little-endian
int32-bit signed int, little-endian
uint32-bit unsigned int, little-endian
long64-bit signed int, little-endian
ulong64-bit unsigned int, little-endian
sleb128signed LEB128, variable-length (see below)
uleb128unsigned LEB128, variable-length (see below)
uleb128p1unsigned LEB128 plus 1, variable-length (see below)

Header o Cabecera

La cabecera es quizás unas de las partes más importantes de un fichero y no sólo de los .dex, sino que cualquier otro formato, como: PDF, doc. rtf, etc.

En este caso, las estructura de la cabecera es como sigue:



































































































































NameFormatDescription
magicubyte[8]magic value. Este valor es el que identifica el tipo de fichero. En este caso: { 0×64 0×65 0×78 0x0a 0×30 0×33 0×35 0×00 }= “dex\n035\0″
checksumuintadler32 checksum. Típico valor para identificar cualquier tipo de corrupción. Este valor se calcula en base a todo el fichero, menos magic y checksum.
signatureubyte[20]Hash SHA-1 de todo el fichero menos magic, checksum y signature. Se usa para la identificación de ficheros.
file_sizeuintTamaño de todo el fichero incluida la cabecera en bytes.
header_sizeuint = 0×70Tamaño de la cabecera en bytes. Se usa para guardar la compatibilidad.
endian_taguint = ENDIAN_CONSTANTEtiqueta endian. Nos indica que tipo de formato endian usa el fichero. Puede tener los siguientes valores:

uint ENDIAN_CONSTANT = 0×12345678;


uint REVERSE_ENDIAN__CONSTANT.

link_sizeuintIndica el tamaño de la sección de enlace (link section) o 0, si el fichero es enlazado de forma dinámica.
link_offuintDesplazamiento al comienzo de la sección de enlace desde el inicio del fichero o 0 si link_size == 0.
map_offuintDesplazamiento al map_list en caso que éste exista o 0 en caso contrario. El map_list es una lista con todo el contenido del fichero. Esta estructura de datos puede contener datos redundantes, pero la intención de la misma es el poder recorrer el contenido del fichero de una forma más cómoda. Esta lista está ordenada.
string_ids_sizeuintNúmero de elementos en la lista de strings.
string_ids_offuintDesplazamiento a la lista de strings o 0 en caso que dicha lista este vacía, circunstancia que raramente se va a dar.
type_ids_sizeuintNúmero de elementos en la lista de tipos (type).
type_ids_offuintDesplazamiento a la lista de tipos o 0 en caso que dicha lista este vacía. Caso que raramente también se dará.
proto_ids_sizeuintNúmero de elementos en la lista de prototipos.
proto_ids_offuintDesplazamiento a la lista de prototipos. 0 en caso que dicha lista este vacía. De nuevo, situación que raramente se dará.
field_ids_sizeuintNúmero de elementos en la lista de campos.
field_ids_offuintDesplazamiento a la lista de campos o 0 en caso que dicha lista esté vacía.
method_ids_sizeuintNúmero de elementos en la lista de métodos.
method_ids_offuintDesplazamiento a la lista de métodos. 0 si la lista está vacía.
class_defs_sizeuintNúmero de elementos en la lista de clases.
class_defs_offuintDesplazamiento a la lista de clases. 0 en caso dicha lista este vacía, situación poco probable.
data_sizeuintTamaño en bytes de la sección de datos. Debe ser un número par múltiplo del tamaño de un uint (sizeof(uint)).
data_offuintDesplazamiento a la sección de datos.

Sección de Strings

Esta sección está compuesta por una lista de referencias a todas las Strings o cadenas de texto de la aplicación.





















NameFormatDescription
string_data_offuintDesplazamiento desde el inicio del fichero hasta la cadena de texto en sí. La estructura de datos a la que este puntero apunta debe de estar en la sección de datos.

String_data_item es la estructura de datos referenciada por la lista de punteros anterior. Como dije en la descripción, estos datos deben estar situados en la sección de datos.


























NameFormatDescription
utf16_sizeuleb128Tamaño del string en formato UTF-16 (2 bytes por caracter).
dataubyte[]Array de bytes en formato MUTF-8. Aunque también acepta la codificación UTF-16.

Sección de tipos

Aquí nos encontramos con la lista de los tipos de datos usados en el fichero.





















NameFormatDescription
descriptor_idxuintIndica el tipo (clase, arrays o tipos primitivos) de cada String especificada en el sección de Strings. Debe estar en el mismo orden que la sección de strings.

Estos son los valores que puede tomar:





































































TypeDescriptor
'V'
|FieldTypeDescriptor
FieldTypeDescriptor
NonArrayFieldTypeDescriptor
|('[' * 1…255) NonArrayFieldTypeDescriptor
NonArrayFieldTypeDescriptor
'Z'
|'B'
|'S'
|'C'
|'I'
|'J'
|'F'
|'D'
|'L' FullClassName ';'

Tipos de datos correspondientes a los valores anteriores:


























































SyntaxMeaning
Vvoid; sólo válido para valores de retorno
Zboolean
Bbyte
Sshort
Cchar
Iint
Jlong
Ffloat
Ddouble
Lfully/qualified/Name;Nombre completo de la clase, incluido los paquetes.
[descriptorarray of descriptor, usable recursively for arrays-of-arrays, though it is invalid to have more than 255 dimensions.

Sección de prototipo de métodos

Esta sección contiene la lista de los prototipos de métodos de nuestras clases. Lo que se conoce también como el signature (firma) del método.































NameFormatDescription
shorty_idxuintÍndice dentro de la sección de Strings que contiene la cadena de texto con el nombre del método
return_type_idxuintÍndice dentro de la cadena de tipos correspondiente al tipo de dato devuelto por este método.
parameters_offuintDesplazamiento a la lista de tipos de parámetros o 0 en case de que el método no espere ningún parámetro. Dicha sección se encuentra en la sección de datos.

Sección de campos (fields)

Lista de todos los campos referenciados por este fichero, estén en este mismo fichero o no, es decir, si referenciamos un campo que pertenece a alguna libraría, aunque éste no se encuentre en nuestro fichero, de cualquier forma aparecerá en esta lista.































NameFormatDescription
class_idxushortÍndice correspondiente a la clase a la que pertenece dicho campo. Este elemento se encuentra en la lista de tipos. Debe referenciar un tipo clase.
type_idxushortÍndice en la lista de tipos que se corresponde con el tipo de este campo
name_idxuintÍndice dentro de la lista de Strings que contiene el nombre de dicho campo.

Sección de métodos

Ésta contiene la lista de métodos referenciados en nuestro fichero y al igual que la lista de campos, aquí también aparecen los métodos que no se encuentran en este fichero.































NameFormatDescription
class_idxushortÍndice dentro de la lista de tipos que define este método. Éste debe ser del tipo clase o array, pero un tipo primitivo.
proto_idxushortÍndice que define el prototipo de este método en la sección de prototipos.
name_idxuintÍndice en la sección de strings que contiene el nombre de este método.

Sección de clases

Esta sección compone la lista de las clases en nuestro fichero. Esta lista está ordenada por clases de forma que un clase que herede o implemente otra, ésta/s deben aparecer antes en dicha lista.
























































NameFormatDescription
class_idxuintÍndice en la lista de tipos. Debe ser del tipo clase y no array o primitivo.
access_flagsuintTipo de acceso (public, final, etc)
superclass_idxuintÍndice en la sección de tipos del tipo de la superclase o clase que hereda. NO_INDEX en caso de que no tenga superclase. Por ejemplo Object
interfaces_offuintDesplazamiento desde el inicio del fichero hasta la o las interfaces que esta clase implementa o 0 caso que no implemente ninguna. Dicha lista de interfaces deber aparecer en la sección de datos.
source_file_idxuintÍndice en la lista de Strings con el nombre del fichero que contiene la fuente original o la constante NO_INDEX en caso de que se carezca de dicha información.
annotations_offuintDesplazamiento a la estructura de anotaciones de esta clase o 0 en caso de que esta clase no contenga anotaciones. La estructura de anotaciones aparece en la sección de datos.
class_data_offuintDesplazamiento a los datos asociados con esta clase. Esos datos deben estar definidos en la sección de datos.
static_values_offuintDesplazamiento a la sección de datos donde se guardan los valores iniciales de los campos estáticos o 0 si la clase no tiene valores estáticos.

Con esto hemos visto las estructuras de datos que compone cada sección. Como podréis apreciar, la mayoría de los datos referenciados desde estas secciones, se encuentran en la sección de datos. Todavía existen más estruturas de datos que no hemos visto aquí, pero que siempre podéis consultar en la documentación oficial.


Sólo he intentado repasar las estructuras, que desde mi punto de vista son más atractivas. También podemos apreciar el papel que juega la sección de Strings y quizás ahora se vea un poco más claro el proceso de optimización de los ficheros .dex.


Para tener una idea un poco más clara de la diferencia entre ficheros .jar y ficheros vamos a ver unas imágenes de ejemplo donde se puede ver la diferencia entre ambos.


En esta primera imagen podemos ver como se relacionan, o como se van a distribuir los datos con generemos el fichero .dex.


java_dex


A continuación veremos una imagen de como estarían distribuidas las clases y sus datos dentro de un .jar.


jar_structure


Aquí vemos como cada clase contiene su propia información. No se comparte nada. Ahora veremos como se distribuyen los datos dentro de un fichero .dex.


dex_data_link


Como podemos ver no existen elementos repetidos y todo está enlazado y compartido. Como ya hemos dicho esta una de las formas en que Dalvik reduce el tamaño de los ficheros y además acelera el acceso a los datos.


La estructura de los ficheros .jar es mucho más clara y ordenada, pero recordemos que cuando hablamos de Dalvik hablamos de dispositivos móviles y ligeros, dónde el ahorro de espacio y proceso es crítico.


Enlaces anteriores de esta serie:

Dalvik VM: Introducción

Dalvik VM: Optimización


Fuente:

http://www.netmite.com/android/mydroid/dalvik/docs/dex-format.html




Related posts:

  1. Dalvik VM: Optimización

  2. Curso de desarrollo de aplicaciones libre y código abierto

  3. Android LiveCD



Creado por tuxotron for CyberHades, 2010. |
Permalink |
No comment |


Post tags: , , , , , , ,

"