我成功地将Android项目的ARM库更改为PIC(与位置无关的代码),因为我想进行一些无关的修复,并且自Lollipop以来,Android仅支持PIC库。 (我的叉子的最新来源是http://github.com/sleekweasel/Beebdroid)
现在,我希望在整理并提出拉取请求之前也能使x86正常工作(因为它在原始项目中),但是我的x86比我的ARM知识要弱得多,而Android汇编文章似乎只能处理ARM。
我遇到的两条指令是LEA
和CALL
:它们导致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的方式?
感谢您的帮助。
[就像我怀疑的那样,您正在跨源文件(从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]
引用相对于当前地址的符号。