7

I have a .so file provided by a third-party vendor that is to be included in my Android application. I do not have access to the source code nor can I (easily) contact the vendor.

Is there a way for me to figure out by examining the .so file whether or not it was compiled targeting the armeabi or armeabi-v7a ABI?

I'm asking for two reasons. First, I prefer for it to have been compiled targeting the armeabi-v7a ABI to get the improved performance compared to armeabi; knowing this will give me confidence that I'm getting the best possible performance. Also, I would like to name the directory in which the .so files live appropriately (i.e. name the folder "armeabi" or "armeabi-v7a" corresponding to the ABI that it was compiled targeting).

denversc
  • 468
  • 7
  • 9

3 Answers3

6

readelf in Android SDK should solve something like this easier:

PATH(may change based on your own platform): sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-readelf

$ arm-linux-androideabi-readelf -A liba.so
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "5TE"
  Tag_CPU_arch: v5TE
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_FP_arch: VFPv2
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_enum_size: int
  Tag_ABI_optimization_goals: Aggressive Speed
  Tag_DIV_use: Not allowed
imcaspar
  • 652
  • 8
  • 9
  • How shall I interpret this output? (Because I do not directly see any armeabi.) Thanks! – ch271828n Jul 18 '19 at 13:52
  • For a armeabi-v7a so the output will be: File: ./shared_libs/armeabi-v7a/libscimd_lib.so Attribute Section: aeabi File Attributes Tag_CPU_name: "ARM v7" Tag_CPU_arch: v7 – drlolly Jun 01 '22 at 08:42
2

I ended up disassembling the .so files using the arm-linux-androideabi-objdump program from the Android NDK. In the disassembled code I found the vmaxnm.f32 instruction, which is present in the armeabi-v7a instruction set but not in armeabi. Based on that I concluded that the .so was compiled targeting armeabi-v7a. There are probably other instructions that I could have looked for but I'm not at all familiar with the ARM instruction set to be able to tell. I got lucky that this one was fairly obvious (being that it is a floating point operation, one of the major differences between armeabi and armeabi-v7a). Thanks for the ideas to those who posted.

denversc
  • 468
  • 7
  • 9
  • 2
    Yes, verifying that the library was **not** built for arm-v6 is easier than the opposite. It should be mentioned for completeness that some weird builds use arm-v7 instructions but are still built for **armeabi**. They use runtime CPU detection and avoid instructions that would otherwise crash (see e.g. http://stackoverflow.com/questions/7679363/android-build-system-neon-and-non-neon-builds). **libx264** even goes one step further: they try to issue an **arm-v7** instruction and catch the exception, this way they decide which set of instructions is available. – Alex Cohn Mar 25 '14 at 21:34
  • if you cat a shared library compiled for armeabi-v7a, you'll see something like "GCC: (GNU) 4.7 GNUgold 1.11A=aeabi3ARM v7 A" near the end. The corresponding armeabi data is similar to "GCC: (GNU) 4.8 GNUgold 1.11A*aeabi 5TE". It's only a guess, but the pattern of 'v7' vs. '5TE' seems to hold for libs compiled for armeabi-v7a and armeabi, respectively. Full disclosure, I know very little about ARM architecture details – CCJ Dec 08 '15 at 23:12
-1

Yes you can find ABI version of .SO file using ReadElf.java.

Reference-https://android.googlesource.com/platform/cts/+/17fcb6c/libs/deviceutil/src/android/cts/util/ReadElf.java

I have made a sample Android app which finds ABI of provided .SO libs using ReadElf.java.
GitHub Link : https://github.com/robust12/ArchFinderBLStack

public class MainActivity extends AppCompatActivity {

private final String TAG = "MainActivity";

private final String ARMV7ABI = "armeabi-v7a";
private final String X86 = "x86";
private final String MIPS = "mips";
private final String X86_64 = "x86_64";
private final String ARM64_V8 = "arm64-v8a";
private final String ARMABI = "armeabi";
private String result = "";

private File[] libFilesArray;
private int request_code = 1;
HashMap<Integer, String> typeMap;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.textViewId);
    typeMap = new HashMap<>();
    initializeMap();
    readFilesFromStorage();
    textView.setText(result);
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void readFilesFromStorage() throws NullPointerException {
    String filePath = Environment.getExternalStorageDirectory() + "/test-input/";
    File readSOFILE = new File(filePath);
    if(!readSOFILE.exists()) {
        result = getString(R.string.path_not_exist);
        return;
    }
    libFilesArray = readSOFILE.listFiles();
    if(libFilesArray == null) {
        result = getString(R.string.error);
        return;
    }
    findAbiType();
}

private void findAbiType() {
    int count = libFilesArray.length;
    int soCount = 0;
    result = "";
    Log.e(TAG, "Count  is  " + count);
    for (int i = 0; i < count; i++) {
        try {
            if (libFilesArray[i].isFile()) {
                int type = ReadElf.read(libFilesArray[i]).getType();
                if (type == 3) {
                    soCount++;
                    int archCode = ReadElf.e_machine;
                    result += libFilesArray[i].getName() + " - " + typeMap.get(archCode) + "\n\n";
                    Log.e(TAG, "Code  is  " + archCode);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    if(soCount != 0) {
        result += "Total Libs Count: " + soCount + "\n\n";
    } else{
        result = getString(R.string.incorrect_type_libs);
    }
}

private void initializeMap() {
    typeMap.put(40, ARMV7ABI);
    typeMap.put(3, X86);
    typeMap.put(8, MIPS);
    typeMap.put(62, X86_64);
    typeMap.put(183, ARM64_V8);
    typeMap.put(164, ARMABI);
}

}

Jitendra
  • 52
  • 8