Unknown mandatory EABI object attribute 44

ARM 86浏览

底层链接时报错Unknown mandatory EABI object attribute 44,因为这个是使用NDK8.0 ARM 编译的,而到linux下编译
是使用prebuilt下的盒子编译器,两者都是ARM编译器,上网搜都是说交叉编译器使用的和底层不一样。

第一部分:EABI 是什么呢?
Embedded application binary interface, 即嵌入式应用二进制接口
EABI是新的“嵌入式”ABI(by ARM ltd)。EABI实际上是ABI的一个家族。对于Linux,GNU EABI也是一个subABIs。
与传统的ABI比较明显的更新包括:
A、支持软件浮点和硬件实现浮点功能混用
B、系统调用的效率更高
C、后今后的工具更兼容
D、软件浮点的情况下,EABI的软件浮点的效率要比OABI高很多,使用Vector Float Point(矢量浮点),因此可以极大提高涉及到浮点运算的程序

两种ABI在如下方面有区别:(OABI中的O,表示“Old”,“Lagacy”,EABI中的E,表示“Embedded”,是一种新的ABI)

A、调用规则(包括参数如何传递及如何获得返回值)
B、系统调用的数目以及应用程序应该如何去做系统调用
C、目标文件的二进制格式,程序库等
D、结构体中的 填充(padding/packing)和对齐

其实从百度查的资料说明: http://baike.baidu.com.cn/view/3547622.htm 其本质就是系统调用有本质的区别:
我们知道,sys_call_table 在内核中是个跳转表,这个表中存储的是一系列的函数指针,这些指针就是系统调用函数的指针,
如(sys_open).系统调用是根据一个调用号(通常就是表的索引)找到实际该调用内核哪个函数,然后运行该函数完成的。
首先,对于old ABI,内核给出的处理是给它建立一个单独的system calltable,叫sys_oabi_call_table,这样,兼容方式下就会有两个
system call table, 以oldABI方式的系统调用会执行old_syscall_table表中的系统调用函数,EABI方式的系统调用会用sys_call_table中的函数指针。
配置无外乎以下四种
第一 两个宏都配置 行为就是上面说的那样
第二 只配置CONFIG_OABI_COMPAT , 那么以old ABI方式调用的会用sys_oabi_call_table,以EABI方式调用的 用sys_call_table,和1实质相同,只是情况1更加明确。
第三 只配置CONFIG_AEABI 系统中不存在 sys_oabi_call_table, 对old ABI方式调用不兼容。只能 以EABI方式调用,用sys_call_table
第四 两个都没有配置 系统默认会只允许old ABI方式,但是不存在old_syscall_table,最终会通过sys_call_table 完成函数调用

链接工具命名:
   arch-vendor-(os-)abi
1、arm-none-linux-gnueabi (ARM architecture, no vendor, linux OS, and the gnueabi ABI)
       用于编译ARM架构的u-boot、linux内核、linux应用等
2、arm-none-eabi 
       用于编译ARM架构的裸机系统(包括linux的 boot、kernel)
3、arm-eabi 
       Android ARM 编译器

第二部分:如何解决EABI的问题
1、浮点数问题
Makefile中参数意义:

-mabi=apcs-gnu :apcs-gnu是OABI的 参数,因此将OABI的参数传给符合EABI标准的编译,编译阶段没有报错,但在板上运行时,程序中涉及到浮点数的部分出现了许多莫名的问题。

        比如 printf("%s %f",s,f);这句话输出的浮点数值并不是传给printf的参数,而是一个莫名其妙的数字。

        解决办法:在Makefile中将 “-mabi=apcs-gnu”去掉,重新编译运行,成功!
编译选项:
OABI: ABI flags passed to binutils: -mabi=apcs-gnu -mfpu=fpa
EABI: ABI flags passed by gcc to binutils: -mabi=aapcs-linux -mfloat-abi=soft -meabi=4

2、内核支持:需要内核同样配置EABI编译属性才能支持EABI编译出来的应用程序
make menuconfig
Kernel Features
[ ] Use the ARM EABI to compile the kernel
解决方法:将它寻上之后自动多出下面一行,这样再次编译的内核就ok了,嘿嘿:)
[*] Use the ARM EABI to compile the kernel
[*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL)

3、使用NDK编译的库与在linux下编译的库link报错
两者使用使用同样EABI选项但还是无是link成功,Unknown mandatory EABI object attribute 44
最后利用显示动态库加载并查找函数符号解决问题。
由于需要明确需要找到的函数比较多,所以利用OpenGL导出函数的方式来做。

定义entries.in文件,用于放置所有需要导出的函数
类似:
GL_ENTRY(void, glActiveTexture, GLenum texture)
GL_ENTRY(void, glAlphaFunc, GLenum func, GLclampf ref)
GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref

#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
定义此函数方便对于多参数的函数加载,可以省去很多麻烦

然后定义结构:
struct gl_hooks_t {
       #include "entries.in"

}; 


样例代码如下:

	struct gl_hooks_t {
       #include "entries.in"
	};

	char const * const gl_names[] = {
	    #include "entries.in"
	    NULL
	};

	/* This is a generic function pointer type, whose name indicates it must
	 * be cast to the proper type *and calling convention* before use.
	 */
	typedef void (*__eglMustCastToProperFunctionPointerType)(void);

	struct gl_hooks_t* g_hooks = NULL;

	void init_api(void *dso, char const * const * api,
			__eglMustCastToProperFunctionPointerType* curr) {
				while (*api) {
					char const * name = *api;
					__eglMustCastToProperFunctionPointerType f =
					(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
	        *curr++ = f;
	        api++;
	    }
	}

	int load_library(const char*path,const char *sym){
		int status;
		void *dso = NULL;

	    /*
	     * load the symbols resolving undefined symbols before
	     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
	     * RTLD_NOW the external symbols will not be global
	     */
		dso = dlopen(path, RTLD_NOW);
		if (dso == NULL) {
			char const *err_str = dlerror();
			LOGE("load: module=%sn%s", path, err_str?err_str:"unknown");
			status = -EINVAL;
		}

		init_api(dso, gl_names, g_hooks);

		/* success */
		status = 0;
		done: if (status != 0) {
			if (dso != NULL) {
				dlclose(dso);
				dso = NULL;
			}
		}
		return status;
	}

          这种方法最后在盒子上运行没有问题,证明是可行的。