Reputation: 151
Firstly, my native system is amd64, Windows, using cygwin, and the GNU toolchain and binutils.
I am writing an x86 bootloader but can't get ld
to generate correct relative addresses. I've prepared this minimum reproducible example:
main.s
.code16
.global _start
.section .text
_start:
call other
hlt
other.s
.code16
.global other
.section .text
other:
mov $0xFFFF, %ax
ret
script.ld
ENTRY(_start);
SECTIONS {
.text : {
*(.text);
}
}
To reproduce, execute:
$ as main.s -o a.o
$ as other.s -o b.o
$ ld -T script.ld *.o -o c.o
Then when you examine c.o
using:
$ objdump -sD -m i8086 c.o
c.o: file format pei-x86-64
Contents of section .text:
200000000 e80b00f4 90909090 90909090 90909090 ................
200000010 b8ffffc3 90909090 90909090 90909090 ................
Disassembly of section .text:
00000000 <_start>:
0: e8 0b 00 call e <__major_subsystem_version__+0x9>
3: f4 hlt
4: 90 nop
5: 90 nop
6: 90 nop
7: 90 nop
8: 90 nop
9: 90 nop
a: 90 nop
b: 90 nop
c: 90 nop
d: 90 nop
e: 90 nop
f: 90 nop
00000010 <other>:
10: b8 ff ff mov $0xffff,%ax
13: c3 ret
14: 90 nop
15: 90 nop
16: 90 nop
17: 90 nop
18: 90 nop
19: 90 nop
1a: 90 nop
1b: 90 nop
1c: 90 nop
1d: 90 nop
1e: 90 nop
1f: 90 nop
Notice how the relative address for the call
instruction at address 0
points to 0xE
instead of 0x10
where it should.
While the object files are in pe[i]-x86-64
format the instructions are still 16-bit (hence the -m i8086
option for proper disassembly).
The reason why I think the address is wrong is because ld
believes the code is 64-bit trusting the file format and resolves wrong addresses. This theory is on thin ice however because the relocation information in a.o
says:
$ objdump -sDr -m i8086 a.o
a.o: file format pe-x86-64
Contents of section .text:
0000 e80000f4 90909090 90909090 90909090 ................
Disassembly of section .text:
00000000 <_start>:
0: e8 00 00 call 3 <_start+0x3>
1: R_X86_64_PC16 other
3: f4 hlt
[...]
where the relocation type is R_X86_64_PC16
which truncates the address down to 16-bits as far as I can tell, and should work.
In my actual project I use ld
to combine object files just like above then use objcopy
to convert it into a flat binary image in order to boot as a floppy disk using an emulator. I do it this way because ld
simply cannot convert object files into flat binaries.
I've tried to change the object formats of a.o
and b.o
before linking but my system does not support anything other than 32 and 64 bit object formats i.e. I can't (or don't think I can) use objcopy
to do it.
Upvotes: 0
Views: 327
Reputation: 151
As @NateEldredge and @MichaelPetch have pointed out this is not a problem with any of the tools or the code but with my toolchain. I have since compiled a GCC cross-compiler and binutils for an OS-agnostic generic x86-32 (i686) target platform.
For others who find this answer while searching the net: https://wiki.osdev.org/GCC_Cross-Compiler
Upvotes: 2