文章

Android IL2cpp Crash堆栈还原

如何将内存地址转为可读的函数名

1
${addr2line.exe} -a -C -f -e "C:/Program Files/Unity/Hub/Editor/2018.4.0f1/Editor/Data/PlaybackEngines/AndroidPlayer/Variations/il2cpp/Development/Symbols/armeabi-v7a/libunity.sym.so" 0043a05c

Windows批处理

1
2
3
4
5
6
7
@ECHO OFF
SET Ad2LinePath="addr2line.exe"
SET LinInfo="lineinfo.txt"
ECHO "==============================="
"%Ad2LinePath%" @"%LinInfo%"
ECHO "==============================="
PAUSE

lineinfo.txt

1
 -a -C -f -e "F:/WorkProject/_Develop/program/client/trunk/game/Prj-G/addr2lineTools/libil2cpp.sym" 01a23918 00f45b68 01c335f0 00488808 019df9ec

列出 .so 文件中的符号

nm

列出符号的标准工具是nm,可以像这样简单地使用它:

1
nm -gD yourLib.so

如果想查看 C++ 库的符号,请添加“-C”选项来对符号进行分解(分解后的可读性更高)。

1
nm -gDC yourLib.so

如果你的 .so 文件是 elf 格式,你有两个选择:

objdump

要么objdump-C对于分解 C++ 也很有用):

1
2
3
4
5
6
7
8
9
$ objdump -TC libz.so

libz.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000002010 l    d  .init  0000000000000000              .init
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 free
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __errno_location
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable

readelf

或使用readelf

1
2
3
4
5
6
7
8
$ readelf -Ws libz.so
Symbol table '.dynsym' contains 112 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000002010     0 SECTION LOCAL  DEFAULT   10
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (14)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (14)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTabl

问题

在libil2cpp.so上进行Android(IL2CPP)生产构建时,在发生的崩溃中表征一个调用堆栈,需要保存该库的符号表。

该符号文件每次在 <ProjectFolder>/Temp/ directory下构建时被创建,当编辑器应用退出后被移除,所以可能看不到它们。

解决方案

每次构建后,可以从下述位置获得符号:

  • <ProjectFolder>\Temp\StagingArea\libs\x86\libil2cpp.so.debug
  • <ProjectFolder>\Temp\StagingArea\libs\armeabi-v7a\libil2cpp.so.debug

确保在关闭Unity编辑器之前将符号文件复制到不同的文件夹中。也可以使用下面的后期构建脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using UnityEngine;
using System.Collections;
using UnityEditor.Callbacks;
using UnityEditor;
using System.IO;
using System;

public class MyBuildPostprocessor  
{
        [PostProcessBuildAttribute()]
        public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
        {
                if (target == BuildTarget.Android)
    		        PostProcessAndroidBuild(pathToBuiltProject);
        }

        public static void PostProcessAndroidBuild(string pathToBuiltProject)
        {
                UnityEditor.ScriptingImplementation backend = UnityEditor.PlayerSettings.GetScriptingBackend(UnityEditor.BuildTargetGroup.Android) as UnityEditor.ScriptingImplementation;

                if (backend == UnityEditor.ScriptingImplementation.IL2CPP)
                {
                        CopyAndroidIL2CPPSymbols(pathToBuiltProject, PlayerSettings.Android.targetDevice);
                }
        }

        public static void CopyAndroidIL2CPPSymbols(string pathToBuiltProject, AndroidTargetDevice targetDevice)
        {
                string buildName = Path.GetFileNameWithoutExtension(pathToBuiltProject);
                FileInfo fileInfo = new FileInfo(pathToBuiltProject);
                string symbolsDir = fileInfo.Directory.Name;
                symbolsDir = symbolsDir + "/"+buildName+"_IL2CPPSymbols";

                CreateDir(symbolsDir);

                switch (PlayerSettings.Android.targetDevice)
                {
                      case AndroidTargetDevice.FAT:
                        {
                            CopyARMSymbols(symbolsDir);
                            CopyX86Symbols(symbolsDir);
                            break;
                        }
                      case AndroidTargetDevice.ARMv7:
                        {
                            CopyARMSymbols(symbolsDir);
                            break;
                        }
                      case AndroidTargetDevice.x86:
                        {
                            CopyX86Symbols(symbolsDir);
                            break;
                        }
                      default:
                      break;
                }
        }


        const string libpath = "/../Temp/StagingArea/libs/";
        Const string libFilename = "libil2cpp.so.debug";
        private static void CopyARMSymbols(string symbolsDir)
        {
                string sourcefileARM = Application.dataPath + libpath + "armeabi-v7a/" + libFilename;
                CreateDir(symbolsDir + "/armeabi-v7a/");
                File.Copy(sourcefileARM, symbolsDir + "/armeabi-v7a/libil2cpp.so.debug");
        }

        private static void CopyX86Symbols(string symbolsDir)
        {
                string sourcefileX86 = Application.dataPath + libpath + "x86/libil2cpp.so.debug";
                File.Copy(sourcefileX86, symbolsDir + "/x86/libil2cpp.so.debug");
        }

        public static void CreateDir(string path)
        {
                if (Directory.Exists(path))
                    return;

                Directory.CreateDirectory(path);
        }
} 
本文由作者按照 CC BY 4.0 进行授权