链接脚本*.lds的编写示例(MIPS/龙芯适用)


  作为合格的程序员,是有必要拜读《程序员的自我修养-链接、装载与库》那本书的。里面第4章(静态链接)通过最“小”程序实例,讲解了链接过程控制和链接脚本的编写。书中的例子是x86架构的,按照书上的代码编写可以顺利运行。但是在mips平台上编写一个链接脚本来实现一个最“小”程序还是有一些坑要踩的。这个坑我已经趟过了,现在分享出来供需要的同学参考。


编写最“小”的程序并编译成.o文件

  正如书中所说,所谓最“小”程序,就是它能够脱离c语言运行库,成立一个独立于任何库的纯正的程序。就是说这个程序不需要依赖任何libc等库(如果需要直接使用系统调用)就可以运行。同时这个最小程序不需要实现main函数,可以指定我们自己想要的入口函数。本例中,我们编写这个最小程序就叫它NoMain.c。源代码如下:

char *str = "Hello world!\n";
void print(){
/* sys_write (int fd, const void *buf, unsigned long count) */
    asm(
	"li	$2,5001     
	"li	$4,0             \n\t"       
	"move	$5,%0  \n\t"      
	"li     $6,13       \n\t"        
	"syscall          \n\t"
	:
	:"r"(str)
	:"$2","$4","$5","$6");
}

void exit(){
/* sys_exit (unsigned long error_code) */
    asm(
	"li	$2,5058\n\t" 
	"li	$4,0\n\t"   
	"syscall      \n\t");
}

void nomain(){
    print();
    exit();
}

源代码解读:这个程序实现的功能和书中例子一样,打印“Hello world!”。在后面链接过程中,我会指定程序入口为nomain()函数,然后该函数调用print()函数,打印“Hello world!”,接着调用exit()退出。阅读这段代码还是需要程序员熟悉gcc内嵌汇编格式。
mips中系统调用通过syscall指令实现,系统调用号通过v0寄存器($2)传递,其他参数通过a0-a7 ($4-$11)传递。
mips平台下,sys_write的调用号为5001,即$2 = 5001。不知道调用号可以去/usr/include/asm/unistd.h查看。stdout的文件句柄为0,使用a0寄存器传递,即$4 = 0。str使用a1寄存器传递,即$5 = str。str字符串长度为13,使用a2寄存器传递,即$6 = 13。
同样sys_exit的调用号为5058,通过v0寄存器传递即$2 = 5058。退出码(err_code)为0,通过a0传递即$4=0。

程序编写完成后,我们先使用gcc编译

 gcc -c -fno-builtin NoMain.c -mno-abicalls

编译完成后生成文件NoMain.o 。其中:

  • -c表示“编译、汇编到目标代码,不进行链接”。

  • -fno-builtin表示“关闭gcc内置函数功能”。

  • -mno-abicalls表示“do not generate SVR4-style position-independent code. -mabicalls is the default for SVR4-based systems.”。
    -mno-abicalls这个参数具体啥意思我还是没有搞明白,没有它这个最小程序一直不能正常跑起来,我调试了n天。

现在还需要编写链接脚本来完成链接过程,生成可执行程序。

ld链接脚本

假设你知道上面的NoMain.o文件虽然是ELF文件,但还不是最终可以运行的ELF文件。我们还缺少链接过程。NoMain.o仅仅完成了c语言到机器语言的转换,而地址和空间分配、重定位、指定入口函数等过程就是在链接过程完成。当然链接过程的需要的工具是ld。ld的执行还需要一个核心的文件叫链接脚本。
通常GCC使用的是默认链接脚本。如果想查看默认脚本内容,执行ld -verbose查看。

本文要使用自定义的链接脚本。不费话了,直接上代码tiny.lds

OUTPUT_ARCH(mips)
ENTRY(nomain)
SECTIONS
{
	. = 0x120000000 + SIZEOF_HEADERS; 
	tinytext : {  
		*(.head.text)
        	*(.text*)
		*(.data)
		*(.rodata)
	}

	/DISCARD/ : { /*释义:需要丢弃的输入文件段*/
	*(.comment)
	}

/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
}

代码解读:OUTPUT_ARCH(mips)指定了体系架构为mips。ENTRY(nomain)指定程序入口函数为nomain()。SECTIONS{ …}为链接脚本主体。里面的各段介绍参考书上吧。

下面开始执行链接过程:

ld -static -T tiny.lds -o NoMain NoMain.o 
  • -static 表示静态链接。默认使用的是动态链接方式,也就是.so库动态加载和链接。
  • -o NoMain 表示输出可执行文件名为NoMain 。
  • -T tiny.lds 指定使用链接脚本tiny.lds 。

注意:无论我们是否使用了”-T“选项,我们都可以通过选项”-e“来指定程序的入口函数。使用"-e"指定的程序入口优先级要高于在ld脚本中通过ENTRY()指定程序入口。

     ld -static -e nomain -o NoMain NoMain.o 

这里通过”-e"指定了程序入口参数是void nomain() 。下面可以执行NoMain查看效果:

$ ./NoMain 
Hello world!
finish

附:mips 内嵌汇编 https://blog.csdn.net/junmuzi/article/details/8762637

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海棠花败

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值