代码之家  ›  专栏  ›  技术社区  ›  android.weasel

Android程序集为ARM调用C函数,但不为x86调用?

  •  0
  • android.weasel  · 技术社区  · 5 年前

    我成功地将一个Android项目的ARM库修改为PIC(位置无关代码),因为我想做一些无关的修复,Android只支持PIC库,因为Lollipop。(我的叉子最新消息来源是 http://github.com/sleekweasel/Beebdroid )

    现在我想在整理和编写一个pull请求之前让x86也工作起来(因为它在原始项目中),但是我的x86比我的ARM知识要弱,Android汇编文章似乎只处理ARM。

    我遇到的两个问题是 LEA CALL :他们导致 ld 发出

    warning: shared library text segment is not shareable
    

    如果我把他们说出来的话 ./gradlew build 很高兴链接,但显然代码工作得不太好。

    以下是这个项目的片段-也许它们会比我的英文描述更清晰。我认为这是一个完整的上下文,因为只有指令和它们与链接器的交互才是问题所在:

    应用程序/src/main/jni/6502asm\u x86.S:

    .intel_syntax noprefix
    .text
    .global exec6502
    .global acpu
    
    exec6502:
        pusha
        # Keep CPU* in EBP
        lea  ebp,acpu  // Causes PIC to fail.
        // ... code removed ...
        lea   ebx, fns_asm // Causes PIC to fail.
        // ... code removed ...
        call do_poll_C // Causes PIC to fail.
        // ... code removed ...
        popa
        ret
    
    // Lots of op-code implementations here - they have no effect on linking
    
    // .section .rodata
    .balign 4
    
    fns_asm:
        .long 0                 // 0x00 BRK
        .long 0 - fns_asm + opasm_ora_indzx     // 0x01 ORA (,x)
        .long 0 - fns_asm + opasm_undef
    

    typedef struct M6502_struct M6502;
    
    struct M6502_struct { ... };
    
    void exec6502(M6502* cpu);
    extern void do_poll_C(M6502*, int c);
    

    app/src/main/jni/6502.c号文件

    M6502 acpu;
    M6502* the_cpu = &acpu;
    
    void do_poll_C(M6502* cpu, int c) {
    ...
    }
    

    JNIEXPORT jint JNICALL Java_com_littlefluffytoys_beebdroid_Beebdroid_bbcRun(JNIEnv * env, jobject  obj)
    {
      // Position independent code, hopefully!
      the_cpu->c_fns = &fns; // +40
      exec6502(the_cpu);
      return the_cpu->pc_trigger_hit;
    }
    

    应用程序/src/main/jni/安卓.mk

    # ... stuff ...
    ifeq ($(TARGET_ARCH),arm)
        LOCAL_CFLAGS +=  -march=armv6t2 -O9 -D_ARM_ -fPIC
        LOCAL_SRC_FILES := 6502asm_arm.S
    endif
    ifeq ($(TARGET_ARCH),x86)
        LOCAL_CFLAGS += -m32 -fPIC
        LOCAL_SRC_FILES := 6502asm_x86.S
    endif
    # ... stuff ...
    

    # The ARMv7 is significanly faster due to the use of the hardware FPU
    APP_ABI := armeabi-v7a x86
    APP_PLATFORM := android-16
    
    ifneq ($(APP_OPTIM),debug)
      APP_CFLAGS += -O3 -fPIC
    endif
    APP_CFLAGS += -fPIC
    
    LOCAL_SRC_FILES += \
        6502.c \
        main.c \
        and_more_files.c
    

    我记得看到了我需要重写的地方 利亚 变成一个 呼叫 指令和目标内存偏移量(也在文本段中):因为只需要文本段中的计算偏移量,所以绕过了链接器。(我只有一个 重写:访问汇编代码中偏移量的跳转表-其他两个将变成输入参数和由该参数指向的块中的指针,就像我为ARM所做的那样。)

    我更困惑于 呼叫 to C函数没有被链接器处理,因为ARM的加载程序很乐意重新定位 BL 说明。我已经有了 -fPIC 在C编译标志中。添加 .global do_poll_C .global 外部C函数的声明。

    我知道我可以传入一个用C函数指针初始化的块-我开始对ARM库这样做,但后来发现加载程序不需要这样做。(我甚至可以向汇编语言表添加一个C指针 fns_asm exec6502

    我是否需要x86的函数指针块,或者是否有一些魔法咒语我可以用来要求加载程序处理我的x86调用指令,就像BL‘just works’与ARM一样?

    谢谢你的帮助。

    0 回复  |  直到 5 年前
        1
  •  0
  •   Peter Cordes Steve Bohrer    5 年前

    就像我怀疑的,你 call __attribute__ ((visibility ("hidden"))) 所以链接器可以解析 call do_poll_C 在静态连接时,不让它在动态连接时参与符号插入。

    或者更简单地说,使用 -fvisibility=hidden -fvisibility-inlines-hidden 更改默认值,并仅导出需要使用属性导出的少数符号。 https://gcc.gnu.org/wiki/Visibility


    在64位模式下, call rel32 无法访问任意64位目标,因此对于需要运行时重新定位的符号,链接器不允许您这样做。但这并不适用于您;您(出于某种原因)正在使用 -m32 .


    lea ebp,acpu // Causes PIC to fail

    (还有,IDK既然不能使用x86-64 RIP相对寻址,那么为什么首先要在那里使用LEA; mov reg, imm32

    看看 clang -m32 -fPIC 呼叫 / pop 将EIP读入寄存器并进行加法和偏移,以获得指向GOT的指针。然后它可以处理与之相关的“私有”静态数据。检查编译器输出或google,以获得正确的语法,如 lea ebp, [ebx + acpu@GOTOFF] 或者别的什么,如果有的话 ebx

    Difference between GOT and GOTOFF 显示了一个示例 在AT&T语法中: leal .LC0@GOTOFF(%eax), %edx 获取标签的地址 .LC0

    如果使用x86-64,只需使用RIP-relative-LEA,那么就更容易了;这样就不需要跳转来生成PIC代码。 lea rbp, [RIP + acpu] 引用相对于当前地址的符号。