你有两个选择,
- 获取正确的编译器。
- 编写您自己的“C”库。
获取正确的编译器。
让编译器与您的系统匹配,您总是是最安全的。这适用于 x86 Linux 和各种发行版。如果不同的编译器工作,你很幸运。当您交叉编译时会更加困难,因为编译器不会自动同步。尝试在 2014 Ubuntu 系统上编译的 1999 x86 Mandrake Linux 上运行程序。
除了指令兼容性(您已确定)之外,还有 ABI 和操作系统依赖项。具体来说,armv7 很可能是 hardfloat(具有 浮点 FPU 和寄存器调用约定),您需要一个 softfloat(模拟 FPU)。特定的glibc(或ucLibc)对Linux OS 有特定的调用和期望。例如,threads 的工作方式随着时间的推移发生了变化。
自己写
您始终可以使用-fno-builtin 和-ffreestanding 以及-static。那么你就不能使用任何libc函数了,但是你可以program them your self.
有外部源,如Mark Martinec's snprintf 和易于实现的构建块如write(),
#define _SYS_IOCTL_H 1
#include <linux/unistd.h>
#include <linux/ioctl.h>
static inline int write(int fd, void *buf, int len)
{
int rval;
asm volatile ("mov r0, %1\n\t"
"mov r1, %2\n\t"
"mov r2, %3\n\t"
"mov r7, %4\n\t"
"swi #0\n\t"
"mov %0, r0\n\t"
: "=r" (rval)
: "r" (fd),
"r" (buf),
"r" (len),
"Ir" (__NR_write)
: "r0", "r1", "r2", "r7");
return rval;
}
static inline void exit(int status)
{
asm volatile ("mov r0, %0\n\t"
"mov r7, %1\n\t"
"swi #0\n\t"
: : "r" (status),
"Ir" (__NR_exit)
: "r0", "r7");
}
您必须添加由“C”库负责的自己的启动机器,
/* Called from assembler startup. */
int main (int argc, char*argv[])
{
write(STDOUT, "Hello world\n", sizeof("Hello world\n"));
return 0;
}
/* Wrapper for main return code. */
void __attribute__ ((unused)) estart (int argc, char*argv[])
{
int rval = main(argc,argv);
exit(rval);
}
/* Setup arguments for estart [like main()]. */
void __attribute__ ((naked)) _start (void)
{
asm(" sub lr, lr, lr\n" /* Clear the link register. */
" ldr r0, [sp]\n" /* Get argc... */
" add r1, sp, #4\n" /* ... and argv ... */
" b estart\n" /* Let's go! */
);
}
如果这太令人生畏了,因为您需要实现很多功能,那么您可以尝试获取各种库源并使用-fno-builtin 重新构建它们,并确保这些库不与 Ubuntu 库链接,不兼容。
像crosstool-ng 这样的项目可以让您构建完全适合armv5 系统的正确编译器(可能具有更高级的代码生成)。这可能看起来很痛苦,但上述替代方案也并不容易。