V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
StonyGround
V2EX  ›  Android

为什么 Android dex 文件会有 64K 引用限制

  •  
  •   StonyGround · 2022-04-25 10:33:39 +08:00 · 5400 次点击
    这是一个创建于 731 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天在研究 linux lds 文件的时候,看到一篇博客,如下所写:

    许多脚本是相当的简单的.
    
    可能的最简单的脚本只含有一个命令: 'SECTIONS'. 你可以使用'SECTIONS'来描述输出文件的内存布局.
    
    'SECTIONS'是一个功能很强大的命令. 这里这们会描述一个很简单的使用. 让我们假设你的程序只有代码节,
    初始化过的数据节, 和未初始化过的数据节. 这些会存在于'.text','.data'和'.bss'节, 另外, 让我们进一
    步假设在你的输入文件中只有这些节.
    
    对于这个例子, 我们说代码应当被载入到地址'0x10000'处, 而数据应当从 0x8000000 处开始. 下面是一个实现
    这个功能的脚本:
    
        SECTIONS
        {
          . = 0x10000;
          .text : { *(.text) }
          . = 0x8000000;
          .data : { *(.data) }
          .bss : { *(.bss) }
        }
    
    你使用关键字'SECTIONS'写了这个 SECTIONS 命令, 后面跟有一串放在花括号中的符号赋值和输出节描述的内容.
    
    上例中, 在'SECTIONS'命令中的第一行是对一个特殊的符号'.'赋值, 这是一个定位计数器. 如果你没有以其
    它的方式指定输出节的地址(其他方式在后面会描述), 那地址值就会被设为定位计数器的现有值. 定位计数器
    然后被加上输出节的尺寸. 在'SECTIONS'命令的开始处, 定位计数器拥有值'0'.
    
    第二行定义一个输出节,'.text'. 冒号是语法需要,现在可以被忽略. 节名后面的花括号中,你列出所有应当被
    放入到这个输出节中的输入节的名字. '*'是一个通配符,匹配任何文件名. 表达式'*(.text)'意思是所有的输
    入文件中的'.text'输入节.
    
    因为当输出节'.text'定义的时候, 定位计数器的值是'0x10000',连接器会把输出文件中的'.text'节的地址设
    为'0x10000'.
    
    余下的内容定义了输出文件中的'.data'节和'.bss'节. 连接器会把'.data'输出节放到地址'0x8000000'处. 连接
    器放好'.data'输出节之后, 定位计数器的值是'0x8000000'加上'.data'输出节的长度. 得到的结果是连接器会
    把'.bss'输出节放到紧接'.data'节后面的位置.
    
    连接器会通过在必要时增加定位计数器的值来保证每一个输出节具有它所需的对齐. 在这个例子中, 为'.text'
    和'.data'节指定的地址会满足对齐约束, 但是连接器可能会
    需要在'.data'和'.bss'节之间创建一个小的缺口.
    
    就这样,这是一个简单但完整的连接脚本.
    

    0x10000 正是 64*1024 ,不禁想到 Android 的 64K 引用限制,好奇为什么都是这个值,还是业界标准如此?

    6 条回复    2022-04-25 19:52:16 +08:00
    DaVinci42
        1
    DaVinci42  
       2022-04-25 11:19:56 +08:00   ❤️ 1
    maokabc
        2
    maokabc  
       2022-04-25 12:16:56 +08:00 via Android
    没这么复杂,就是谷歌设计 dex 格式时没考虑到。.class 索引是 u2 那是别人只是单文件,这么用没问题,它.dex 也跟着这样,然后很快就有问题了。dalivk 字节码很多指令只有 u2 的索引,所以那些常量池只能在 u2 范围内。
    StonyGround
        3
    StonyGround  
    OP
       2022-04-25 16:44:05 +08:00
    @DaVinci42 找到了,https://source.android.com/devices/tech/dalvik/dalvik-bytecode 里面有一段这么写的:
    “在实践中,一个方法需要 16 个以上的寄存器不太常见,而需要 8 个以上的寄存器却相当普遍,因此很多指令仅限于寻址前 16 个寄存器。在合理的可能情况下,指令允许引用最多前 256 个寄存器。此外,某些指令还具有允许更多寄存器的变体,包括可寻址 v0 - v65535 范围内的寄存器的一对 catch-all move 指令。如果指令变体不能用于寻址所需的寄存器,寄存器内容会(在运算前)从原始寄存器移动到低位寄存器和 /或(在运算后)从低位结果寄存器移动到高位寄存器。”
    StonyGround
        4
    StonyGround  
    OP
       2022-04-25 16:45:21 +08:00
    @DaVinci42 所以,大概就是 2 楼说的,只是因为没考虑到?
    StonyGround
        5
    StonyGround  
    OP
       2022-04-25 16:47:29 +08:00
    @maokabc 感谢!是否是像 3 楼写的那样呢
    maokabc
        6
    maokabc  
       2022-04-25 19:52:16 +08:00 via Android
    @StonyGround 和寄存器这个关系不大,比如 const-class vAA, type@BBBB ,type@BBBB 就是索引到类型常量池,这个只在 u2 范围内,如果一个 dex 包含的 class 太多那么类型常量池可能超过 0xffff ,这时就出现了 64k 问题。实际还有更多的指令都是 u2 索引,像 invoke 系列指令的 method ref 也只能在 u2 范围内。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1652 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:51 · PVG 00:51 · LAX 09:51 · JFK 12:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.