JesusFreke / smali

smali/baksmali
6.29k stars 1.07k forks source link

[QUESTION] method annotations #819

Closed auermich93 closed 3 years ago

auermich93 commented 3 years ago

Hi @JesusFreke,

could you elaborate a bit on the format of method annotations. Assume the following method as use case:

.method public constructor <init>(Landroid/support/v4/app/FragmentActivity;Ljava/lang/Class;)V
    .locals 2
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Landroid/support/v4/app/FragmentActivity;",
            "Ljava/lang/Class<",
            "+",
            "Lcom/woefe/shoppinglist/dialog/TextInputDialog;",
            ">;)V"
        }
    .end annotation

Basically, I would like to map each annotation to its parameter. Btw, what's the purpose of the method parameter annotations, i.e. MethodParameter::getAnnotations(), in contrast to the method annotations? (It seems like they are always non-existent.) If I gonna parse this string array, what's here the actual separator between two annotations/parameters? Can there be multiple annotations per parameter? Has the '+' sign within the generic a special meaning?

Thanks in advance.

JesusFreke commented 3 years ago

Method parameter annotations and method annotations are 2 different things. What is the difference? One annotates the method, while one annotates an individual parameter..

I'm not sure I follow your question. The code snippet you posted only shows a single annotation. Its value is made up of multiple strings so that it can be composed of strings that very likely already exist in the string pool, instead of having to insert a new string.

The splits between the strings in that annotation do not convey any semantic information. And in general, the format of the Signature annotation is not specified, and can vary depending on compiler, language, etc.

from https://source.android.com/devices/tech/dalvik/dex-format#dalvik-signature

The .dex format does not define the format for signatures; it is merely meant to be able to represent whatever signatures a source language requires for successful implementation of that language's semantics.

Lanchon commented 3 years ago

The code snippet you posted only shows a single annotation. Its value is made up of multiple strings so that it can be composed of strings that very likely already exist in the string pool, instead of having to insert a new string.

so the annotation type is String and not String[]? how is this presented to dexlib2 clients? as String or String[]? thanks

Lanchon commented 3 years ago

Basically, I would like to map each annotation to its parameter.

you don't need to. each annotated param already has its annotations separate in java bytecode and dex.

what's the purpose of the method parameter annotations, i.e. MethodParameter::getAnnotations(), in contrast to the method annotations? (It seems like they are always non-existent.)

the purpose is whatever the annotation designer chooses. annotations are a way to extend the java language. param annotations were not part of the initial java annotation spec; they were introduced later. you can search for the rationale to do this in the corresponding JSR: changes to the language are discussed in extreme detail before committing to any.

If I gonna parse this string array, what's here the actual separator between two annotations/parameters?

the [...]/annotation/Signature annotation is a special annotation. most annotations end up in bytecode because the code writer explicitly chose to annotate a code item. but Signature is implicitly generated by javac when needed. its purpose is to encode the full compile type before type erasure.

java bytecode and dalvik bytecode are not generic. to cope with this, the compiler replaces generic types with non-generic types plus casts and other details. as specified in the java specs, javac implicitly creates Signature annotations to preserve the erased types for tools such as debugger, decompilers, etc. but the JVM does not parse these in any way (*). so java obfuscators can fill these with trash or remove them altogether. compilers for languages other than java to java bytecode are free to choose whatever format they want for these annotations, if any.

the dalvik spec does even less, and does not even specify a format for dex compiled from java. but dx/d8/r8 just copy the contents of the java bytecode annotations, AFAIK.

the Signature annotation is also special because the standard annotation reflection APIs will filter it out, i believe. (*) but the reflection runtime can get you the generalized types (non-type-erased), i think, by attempting to parse these annotations. so the VMs do not use these, but the VM runtimes can for some reflection functions.

Has the '+' sign within the generic a special meaning?

possibly ? extends TYPE? you have to look the specifics up in the java spec.

NOTE: dexlib2 and smali do not process Signature in any special way. to smali, it is a String-typed annotation.

JesusFreke commented 3 years ago

Right. It's just an annotation, with no semantic information at the dex level. The signature is conceptually a single string, but the annotation value is an array of strings, so that the same type strings that are already in the string pool can be reused.

Lanchon commented 3 years ago

the annotation value is an array of strings, so that the same type strings that are already in the string pool can be reused.

this is an encoding used for all String annotations in dex? or just for this one? thanks

auermich93 commented 3 years ago

Thanks for the informative answer.

Method parameter annotations and method annotations are 2 different things. What is the difference? One annotates the method, while one annotates an individual parameter..

Well, in the example above it looks like method annotations are quite the same as method parameter annotations.

As @Lanchon already mentioned, I parse the string array to get back the generic type information, wich is lost by compilation, e.g. the method looks as follows in the source code: public Builder(FragmentActivity activity, Class<? extends TextInputDialog> clazz) {...}