Assembly 如何在不影响rip相对寻址的情况下正确链接两个对象文件?

Assembly 如何在不影响rip相对寻址的情况下正确链接两个对象文件?,assembly,rust,linker,ld,instructions,Assembly,Rust,Linker,Ld,Instructions,我有两个对象文件,一个是从汇编生成的,另一个是从rust编译的。从组装中,我调用了rust中的一个函数(如下所示),因此我需要将它们链接在一起 //boot.asm section .text ... extern _start call _start ... //main.rs #![no_std] #![no_main] #[no_mangle] pub extern fn _start() { let hello = b"hello_world!"; ... } 编译

我有两个对象文件,一个是从汇编生成的,另一个是从rust编译的。从组装中,我调用了rust中的一个函数(如下所示),因此我需要将它们链接在一起

//boot.asm
section .text
...
extern _start
call _start
...

//main.rs
#![no_std]
#![no_main]

#[no_mangle]
pub extern fn _start() {
    let hello = b"hello_world!";
    ...
}
编译后,我发现在rust对象文件中有一个
.rodata
部分和一个
.text
部分,并且有使用rip相对寻址的说明来寻址
.rodata
部分中的“hello world”字符串:

//section .text
...
  let hello = b"hello world!";
2017e7: 48 8d 05 2e ea ff ff    lea    -0x15d2(%rip),%rax
2017ee: 48 89 44 24 30          mov    %rax,0x30(%rsp)
...
我在程序集中还有
.rodata
部分和
.text
部分。但是,在链接链接器期间,只需将两个
.rodata
节和两个
.text
节合并为一个
.rodata
节和一个
.text
节,这会影响rip相对寻址,导致我的程序无法工作。 有人能告诉我如何修改以解决问题吗? 这是我的链接器脚本,我应该修改它吗

ENTRY(start)

SECTIONS {
    . = 0x00100000;

    .boot :
    {
        *(.multiboot_header)
    }

    .text :
    {
        *(.text)
    }
}

谢谢你的帮助

我明白了。感谢您提供的提示。原因是我将我的rust代码编译为可执行文件,而不是库。我应该把它编译成一个库(一个
.a
文件),这样链接器就可以在链接期间计算rip相对寻址的偏移量。如果它被编译成一个可执行文件,那么偏移量已经被计算出来了,所以链接器不能重新计算它,我提到的问题就会出现。请原谅我,这是一个愚蠢的错误(你怎么能将一个可执行文件与其他文件链接起来),我以前没有认真研究过这些事情。

我发现了。感谢您提供的提示。原因是我将我的rust代码编译为可执行文件,而不是库。我应该把它编译成一个库(一个
.a
文件),这样链接器就可以在链接期间计算rip相对寻址的偏移量。如果它被编译成一个可执行文件,那么偏移量已经被计算出来了,所以链接器不能重新计算它,我提到的问题就会出现。请原谅我这是一个愚蠢的错误(你怎么能将一个可执行文件与其他文件链接起来),我以前没有认真研究过这些问题。

.o
对象文件有重定位元数据,这些元数据在链接时得到解决。直到链接时间,才计算RIP相对偏移量;在
.o
中,它通常只是一个
00
占位符,用于在链接时填充rel32(基于目标符号)。使用
objdump-drwC
拆解
.o
,以显示重新定位。TL:DR:这部分应该可以正常工作;你遇到的任何问题都可能不是由这个引起的。您的链接器脚本是否需要
.rodata
部分以及
.data
和/或
.bss
的条目?对此我不确定。我遵循的是一个从零开始构建操作系统的过程。对于上一个问题,我分别编译了rust和assembly,并手动将它们链接起来。rust不知道它将与另一个对象链接,所以我认为它不会在这里留下占位符。我检查了对象中的指令,它在链接前后都是相同的。你可以检查我的电脑,看看是不是其他东西导致了这个错误。谢谢。像LLVM这样的编译器后端总是使用重定位项,即使是对同一编译单元中定义的标签的引用,除非它在同一节中。e、 g.用于跳跃目标。我并没有具体研究过Rust编译器的输出,但它和clang使用的LLVM是一样的。此外,如果工具链存在您担心的问题,Rust也无法正常工作,即使是用多个源文件(不仅仅是内核)构建正常的可执行文件。不过,我并不真正了解链接器脚本,只是知道默认情况下链接器是如何工作的,因此IDK(如果是您的)可能是个问题。
.o
对象文件具有重定位元数据,这些元数据在链接时得到解决。直到链接时间,才计算RIP相对偏移量;在
.o
中,它通常只是一个
00
占位符,用于在链接时填充rel32(基于目标符号)。使用
objdump-drwC
拆解
.o
,以显示重新定位。TL:DR:这部分应该可以正常工作;你遇到的任何问题都可能不是由这个引起的。您的链接器脚本是否需要
.rodata
部分以及
.data
和/或
.bss
的条目?对此我不确定。我遵循的是一个从零开始构建操作系统的过程。对于上一个问题,我分别编译了rust和assembly,并手动将它们链接起来。rust不知道它将与另一个对象链接,所以我认为它不会在这里留下占位符。我检查了对象中的指令,它在链接前后都是相同的。你可以检查我的电脑,看看是不是其他东西导致了这个错误。谢谢。像LLVM这样的编译器后端总是使用重定位项,即使是对同一编译单元中定义的标签的引用,除非它在同一节中。e、 g.用于跳跃目标。我并没有具体研究过Rust编译器的输出,但它和clang使用的LLVM是一样的。此外,如果工具链存在您担心的问题,Rust也无法正常工作,即使是用多个源文件(不仅仅是内核)构建正常的可执行文件。不过,我并不真正了解链接器脚本,只是默认情况下链接器是如何工作的,因此,如果你的链接器有问题,我会问。我很惊讶任何链接器甚至会接受一个可执行文件作为输入,而不是
.o
文件(松散的或在
.a
中)。但是显然,您可以
gcc-o hello.c
然后
gcc-nostartfiles-o foo hello
生成一个中断的可执行文件。有趣。我很惊讶任何链接器甚至会接受可执行文件作为输入,而不是
.o
文件(松散的或在
.a
中)。但是显然你可以
gcc-o hello.c