Android链接器为ARM汇编而不是x86的C函数重定位?

问题描述 投票:0回答:1

我成功地将Android项目的ARM库更改为PIC(与位置无关的代码),因为我想进行一些无关的修复,并且自Lollipop以来,Android仅支持PIC库。 (我的叉子的最新来源是http://github.com/sleekweasel/Beebdroid

现在,我希望在整理并提出拉取请求之前也能使x86正常工作(因为它在原始项目中),但是我的x86比我的ARM知识要弱得多,而Android汇编文章似乎只能处理ARM。

我遇到的两条指令是LEACALL:它们导致ld发出

warning: shared library text segment is not shareable

如果我将它们注释掉,然后./gradlew build会很高兴地链接,但显然代码无法正常运行。

这里是该项目的摘要-可能比我的英语描述要清楚。我认为这是完整的上下文,因为只有指令及其与链接程序的交互才有问题:

app / src / main / jni / 6502asm_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

app / src / main / jni / main.h

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) {
...
}

app / src / main / jni / main.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;
}

app / src / main / jni / Android.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 ...

app / src / main / jni / Application.mk

# 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

[我记得看到我需要将LEA重写为下一条指令的CALL,将返回值弹出堆栈,然后将CALL指令的地址与目标内存之间的差加起来)偏移量(也在文本段中):由于仅需要在文本段内计算偏移量,因此可以避免链接器。 (我只有一个LEA可以重写:访问汇编代码中的偏移量跳转表-其他两个将变成输入参数和该参数指向的块中的指针,就像我所做的那样ARM。)

[我对CALL感到更加困惑,因为链接器没有处理C函数,因为ARM的加载程序很乐意重新分配BL指令。我在C编译标志中已经有-fPIC。添加.global do_poll_C对于该功能以及可能与其他功能没有区别。 ARM不需要外部C函数的.global声明。

我知道我可以传入一个用C函数指针初始化的块-我开始对ARM库执行此操作,但随后发现加载器使此操作变得不必要。 (我想我甚至可以向汇编语言表fns_asm添加C指针,因为C大概找到了汇编exec6502符号。)

[我是否需要x86的功能指针块,或者是否可以使用一些神奇的咒语来要求加载程序处理我的x86 CALL指令,即BL'只适用于ARM的方式?

感谢您的帮助。

android c assembly x86 ld
1个回答
0
投票

[就像我怀疑的那样,您正在跨源文件(从asm到C)call,但是没有跨共享对象边界,因此不需要运行时重定位。使用__attribute__ ((visibility ("hidden"))),链接器可以在静态链接时解析call do_poll_C,而无需在动态链接时参与符号插入。

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


IDK为什么ARM会有所不同;也许Android / x86根本不愿意进行运行时重定位。

在GNU / Linux(不是Android)下,动态链接将对64位绝对地址(或我认为是32位模式,32位绝对或相对)进行运行时修复。

在64位模式下,call rel32无法达到任意64位目标,因此链接器将不允许您对需要运行时重定位的符号执行此操作。但这并不适用于您。您(出于某种原因)正在使用-m32制作过时的32位代码。


lea ebp,acpu // Causes PIC to fail与LEA无关,与将符号引用作为32位绝对地址有关。

((IDK,为什么您首先要在这里使用LEA,因为您不能使用x86-64 RIP相对寻址; mov reg, imm32是将32位绝对地址放入寄存器的更有效方法:短1个字节。)。

查看将地址放入寄存器的clang -m32 -fPIC代码生成:是的,它使用call / pop将EIP读入寄存器,并加上和偏移以获取指向GOT的指针。然后,它可以处理相对于此的“私有”静态数据。在lea ebp, [ebx + acpu@GOTPCREL]作为GOT指针的情况下,检查编译器输出或google是否为正确的语法,例如ebx或类似内容。

如果使用x86-64,就可以使用RIP相对LEA,这要容易得多;那么您就无需跳圈就能制作PIC代码。 lea rbp, [RIP + acpu]引用相对于当前地址的符号。

© www.soinside.com 2019 - 2024. All rights reserved.