Multidex file format - android

I'm interested in understanding how a dex file (classesN.dex) references methods in another classesN.dex file.
In a standard dex layout, you have all of the class, method, type, etc... definitions in different tables. Things that are dynamically linked (such as those from the Android framework) simply have their method prototypes included, but no code data. Is it true that in a multidex setup, each classesN.dex contains a set of class implementations, and methods that are implemented in other dex files are merely included in the same way as dynamically linked calls?
In other words, if classes.dex needs to reference a method classes1.dex, it will include that method as a prototype within classes.dex, and then include its implementation in classes1.dex?

I ended up solving this question: it turns out that in a multidex layout the relevant method and class definitions are included in each dex file. For example, if classes.dex references methods foo() from classes1.dex, it will include a relevant entry in the method table for foo() within classes.dex's method table. But the implementation of foo() will appear in classes1.dex. This works because foo() is usually something like the entry of a library used by the app. The entry points of that library can be used without all of the methods called by foo. In classes.dex, foo will be defined without a corresponding code item, just as if it were a part of the dynamically linked Android standard library.

Related

Kotlin classes with underscores

Please explain me what is the purpose and meaning of classes like StringsKt__StringsKt (i.e. doubled class name with one or more underscores in between) and, similarly, StringsKt__StringsJVMKt?
Strings are not the only example, there are many others too. I see them when looking into the structure of the classes.dex file in my .apk.
screenshot here
I'm asking because I faced a situation where I had to explicitly state some of them in my proguard-rules. The app crashed without it.
It is a generated file. A file like that will be generated if there are several Kotlin files named with same JvmName.
Let's take Strings.kt and StringNumberConversions.kt as an example:
// StringNumberConversions.kt
#file:kotlin.jvm.JvmMultifileClass
#file:kotlin.jvm.JvmName("StringsKt")
...
// Strings.kt
#file:kotlin.jvm.JvmMultifileClass
#file:kotlin.jvm.JvmName("StringsKt")
...
Both of them have #file:kotlin.jvm.JvmName("StringsKt"), so StringsKt and StringsKt__StringNumberConversionsKt are generated to distinguish them.

Making sense of symbol table entry (when header function is not present)

I have a JNI function in a C++ library.
When I create the library using cmake (but forget to put function declaration) in the header file, the library is created successfully.
When I look for function name in library symbol table I get following output
nm libuserlibrary.so | grep printxx
00506e60 T _Z60Java_com_example_user_myapplication_userlibrary_printxxP7JNIEnv_P8_jobject
But when I give function declaration in the header file, I get following output
nm libuserlibrary.so | grep printxx
00506e50 T Java_com_example_user_myapplication_userlibrary_printxx
Why is there this difference between these two symbol table entries? What is the purpose of _Z60 and P7JNIEnv_P8_jobject around the function name?
I also noticed that in the first case, I cannot call the JNI funciton from Android java code (it says unsatisfied-linker-error, implementation not found).
C++ allows function overloads and namespaces like Java does. So, it annotated the function name with parameter information so the linker can bind to the correct overload.
JNI was designed for C which does not allow function overloads or namespaces. So it invented it's own annotation system and provides the javah tool to help you use it. The header can be used in C++ too. C++ was designed to allow some functions to be called as if they written in C. The header has code that indicates that to the compiler. So, put it all together and you can write Java-callable functions in C++.

Do private methods increase Dex Count in Android?

I was doing code review and told someone to remove a private method that was only used once. They said that it didn't matter since dex count wouldn't get increased by private method references. Is this true? I wasn't able to find an answer with a simple google search.
The 64k limit is a limit on the number of unique method references in a dex file. A method reference consists of a specific class name, the method name and the method prototype, and is created when you either invoke a method or declare/define/override a method.
So yes, defining a new private method will add a method reference to the dex file.
For more information, see: https://source.android.com/devices/tech/dalvik/dex-format.html and https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html, which are the main references for the dex format.
The "method reference list" is a sorted list of method_id_items in the dex file. e.g. look for "method_ids" in the "File layout" section of dex-format.html. And further down the page, method_id_item is defined as consisting of a class reference, a method name and a method prototype.
The class_data_item section is used to define the methods and fields that are defined by the class. The "direct_methods" and "virtual_methods" lists are lists of indexes into the method_ids list - which requires that a reference for that method exists in the method_ids list.
And in dalvik-bytecode.html, the invoke-* instructions use a method index to refer the method to invoke.
Interestingly, the method reference list itself is defined with a 32-bit size value (search for "method_ids_size" in dex-format.html). So the list of method references itself can be as large as 4294967296 entries.
However, the problem comes when you need to reference any of these methods. The invoke-* instructions only use 16 bits to encode the method index.
Additionally, the method references in the class_data item can be up to the full 32 bits. So you could theoretically have definitions of methods past the 64k limit in a dex file, as long as you never actually tried to invoke them from within that dex file. But they could still be invoked from another dex file.
They said that it didn't matter since dex count wouldn't get increased by private method references. Is this true?
I am going to assume that you are worried about the 64K DEX method reference limit. In this case, the reviewee is correct: whether this code is wrapped in a method or not has no impact.
The "method reference" of relevance in "the 64K DEX method reference limit" refers to one DEX referring to a method in another DEX. In conventional Android projects, there are two DEXes of relevance: yours and the framework's. How many methods your own code is divided into does not matter. What matters is how many methods of the framework you are referencing (where by "you" I mean your code plus any library modules and JARs you are including).
When you enable multidex on a project, you are splitting your code into more than one DEX file. Each can refer to 64K methods in other DEX files. Here, though, "other DEX files" refers to both the framework DEX and the other DEX files of your own app created by multidex. However, AFAIK, multidex does not split a single class across DEX files. Since this is a private method, it could only be referenced by another method in the same class, and so both methods should be in the same DEX file. Hence, even in this case, having the private method would not add to the DEX method reference count of the DEX containing that method.
Based on JesusFreke's comments, I retract my original position. Defining a private method will increase the DEX method reference count.
That being said, on a one-off basis, worrying about inlining an individual method, just to reduce the DEX method reference count, is premature optimization IMHO. If you are bumping up towards the DEX method reference limit (and Android Studio's APK Analyzer can help you determine this), worry first about "trimming the fat" in libraries. Otherwise, worry about maintainability. Now, it may be that removing the method actually helps with that (e.g., it's a two-line method, not worth pulling out separately). But if there is maintainability value in having the method, leave it alone.

Library class as return type for methods in aidl interface

In IPC to interpret data received the process should be knowing the Class structure. So i made a Library of all required classes which are all implementing Parcelable.
In library i have defined aidl files for all calsses and ensured that these file are present in JAR.
In application, i have created same aidl file and place in the same package name as in library.
In aidl file which contains method definitions no error is shown but in code section of Stub() i get that the Library class as return could not be found.
if i create the same library object inside the method it shows valid object, but something with it as return type.
so, i moved one of the class from library to application there is problem with this class.
What have i missed so that library classes are not recognized as return type.
Note: i edited the compiler created class file for aidl in gen folder and added import to my library, error is solved but i cant save it, compiler will overwrite it.
what a shame... Package name of classes in my library had first letter as capital while i messed it by creating package structure in my application with small letter...
Wont delete the Question. someone else might also do same mistake

dlopen on Android ndk

I made a c++ main aplication that loads a so library also made by me.
Both sources shares a common header (TestFlags.h).
Inside TestFlags.h I have an class and a pointer declaration of it which is intended to be global to the whole application, that is define a instance in the main app and use it inside a library function.
class TestFlags {
public:
TestFlags() : behaviour(1)
{}
int behaviour;
};
extern __attribute__ ((visibility("default"))) TestFlags * gpTestFlags;
then a sequence of execution steps followed to reach the named goal are:
main application creates a new instance of TestFlags ---> gpTestFlags = new TestFlags();
main application load the library ---> dlopen(library.so, RTLD_LAZY|RTLD_GLOBAL)
invoke a function that resides inside the library which uses previous instance declared ---> gpTestFlags->behaviour = 2;
Received a SIGSEGV: Segmentation fault because gpTestFlags is NULL
It seems that inside the library gpTestFlags instance is not seen for some reason.
Same thing also happens with other static class I have, values which are configured on the main application not seen inside the library.
As far I can research it seems that the library manages a totally different memory space for those declarations like if it was duplicated.
This is the expected way dlopen() would work.
The two modules have independent global symbols called gpTestFlags. If you try to link them together, the linker would scream about duplicates.
You can declare the pointer in library as weak, or you can use dlsym() to resolve the linkage programmatically.

Categories

Resources