xv6-public
xv6-public copied to clipboard
Fix for binutils >= 2.36
As-is trying to build with binutils 2.36 and a recent gcc is problematic in 2 ways:
- It fails to boot in qemu, getting stuck in a bootloop at 'Booting from Hard Disk..'
- fs.img fails to build due to an overzealous -Wstringop-overflow error in usertests.c combined with -Werror
This PR resolves both issues, firstly by stripping the newly introduced '.note.gnu.property' section introduced by binutils 2.36 which appears to be the source of the problem and secondly suppressing the -Wstringop-overflow warning in usertests.c.
The PR also strips some trailing space in the Makefile as trailing space is indeed, the devil.
Thanks a lot! However I don’t know what happened. What should I do to understand the differences?
It looks like the ELF loader is assuming that this section is not present, from binutils 2.36 on it gets added by default which causes the system to fail to be able to correctly load userland binaries. The solution is to simply remove the section altogether. Of course an alternative might be to adjust the loader code.
I left this PR here not only in case a maintainer (though I realise the x86 version is no longer maintained) decided to merge but more so that somebody else who found xv6 broken could use this PR to fix things locally :)
Yeah! I encounter the problem today when I compile this project in my Arch-Linux with binutils 2.36.1-3.
arch is the key motivator for this change as it is the distro I use. If you try this it should sort out the issue.
Yes!Thanks a lot again! Actually the version which I compiled is the rev9. Today I compile it with "make qemu-nox" in the arch-linux rather than the ubuntu 18.04 which I used to use. I deleted the "Werror" in CFLAGS to deal with the "-Werror=stringop-overflow=". Then it compiles well. However it hang over in the "Booting From Hard Disk“. After I revising the Makefile according to your's file and the #115.,it works well!
I left this PR here not only in case a maintainer (though I realise the x86 version is no longer maintained) decided to merge but more so that somebody else who found xv6 broken could use this PR to fix things locally :)
Thanks for the PR. I fetched from your branch to make it work on Arch Linux. :)
Thank You!!! hopefully they merge this PR soon!
I think I understand why this is necessary. First, a small isolated testcase:
$ cat initcode.c
void start() {
return;
}
$ cat Makefile
CFLAGS = -m32 -march=i386
LDFLAGS = -m elf_i386
initcode: initcode.o
$(LD) $(LDFLAGS) -N -e start -o $@ $<
initcode.o: initcode.c
$(CC) $(CFLAGS) -c $<
In this case where we leave the addresses as their defaults, we can see that ld places .note.gnu.property before .text:
$ readelf -S initcode
There are 9 section headers, starting at offset 0x2a4:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.gnu.pr[...] NOTE 080480b4 0000b4 000028 00 A 0 0 4
[ 2] .text PROGBITS 080480dc 0000dc 000021 00 WAX 0 0 1
[ 3] .eh_frame PROGBITS 08048100 000100 00004c 00 A 0 0 4
[ 4] .got.plt PROGBITS 0804814c 00014c 00000c 04 WA 0 0 4
[ 5] .comment PROGBITS 00000000 000158 00001b 01 MS 0 0 1
[ 6] .symtab SYMTAB 00000000 000174 000090 10 7 4 4
[ 7] .strtab STRTAB 00000000 000204 000050 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 000254 000050 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
The sections start around 0x08048000, which is the arbitrary location used as the default start location for .text. [1]
When we do specify the address of the .text section, only it and sections after it will be placed at the beginning of the output file. Therefore, .note.gnu.property will remain wherever it was before. [2]
$ ld -m elf_i386 -N -e start -Ttext 0 -o initcode initcode.o
$ readelf -S initcode
There are 9 section headers, starting at offset 0x2b4:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.gnu.pr[...] NOTE 080480d4 000140 000028 00 A 0 0 4
[ 2] .text PROGBITS 00000000 0000d4 000014 00 WAX 0 0 1
[ 3] .eh_frame PROGBITS 00000014 0000e8 00004c 00 A 0 0 4
[ 4] .got.plt PROGBITS 00000060 000134 00000c 04 WA 0 0 4
[ 5] .comment PROGBITS 00000000 000168 00001b 01 MS 0 0 1
[ 6] .symtab SYMTAB 00000000 000184 000090 10 7 4 4
[ 7] .strtab STRTAB 00000000 000214 000050 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 000264 000050 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
Then, because of that far-out section, when we convert the initcode to a binary, rather than getting a nice and small 44 (!) byte binary, we get a 16+ MiB binary which would take ages for the ELF loader to load from the image. To get an idea of how long it takes, I modified the bootloader to output progress over serial, and the answer is long.
Understanding this, let's explore our options for how to fix it. Deleting the .note.gnu.property section is a satisfactory solution, but I am interested in alternative solutions since the issue is not the existence of that section, but rather the location of it.
Unfortunately, the ld command line does not allow us to specify a "general" start address for our binary. It will only let us fix the start addresses of specific sections, such as .text or .note.gnu.property. Of course, fixing the address of the latter would not be a good solution because it would not be compatible with earlier versions of binutils, and does not clearly express our intent.
I think that the cleanest solution is to write a super small linker script that fixes .text to the beginning of the output file. While we are at it, we can also link directly into the binary format (this should also be applicable for earleir binutils).
Makefile
...
initcode: initcode.S
$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
$(LD) $(LDFLAGS) -T initcode.ld -o initcode -N initcode.o
$(OBJDUMP) -S initcode.o > initcode.asm
...
initcode.ld
OUTPUT_FORMAT("binary")
ENTRY(start)
SECTIONS
{
.text : {
*(.text)
}
}