weird elf header issues, is it binutils or my linker script?
Hi all,
We're running into an problem with a (somewhat complicated) 32-bit powerpc binary. When we try to run it, it hits the null-terminated interpreter test in the kernel and bails out with ENOEXEC.
Looking at the binary with readelf, the program headers contain the following:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0xf2000034 0xf2000034 0x00120 0x00120 R 0x4 INTERP 0x000154 0xf2000154 0xf2000154 0x00030 0x0002d R 0x4
[Requesting program interpreter: /lib/ld.so.1]
Notice the unusually large size of the INTERP header--instead of 0xd it's 0x30. This causes problems when we try to run, because while the string "/lib/ld.so.1" is null-terminated it just so happens that the byte at 0x000154+0x00030-1 (which corresponds to elf_interpreter[elf_ppnt->p_filesz - 1] in the kernel code) is not, and so it fails the kernel test.
We do use a custom linker script when building this binary. I can only find two entries related to the interpreter:
PHDRS
{
headers PT_PHDR PHDRS ;
interp PT_INTERP ;
<snip>
}
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0xf2000000); . = 0xf2000000 + SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
<snip>
}
So I'm wondering...is this something wrong with our linker script, or is there a bug in our binutils? I'm no linker expert, but the interpreter sections in the script seem to match the binutils documentation that I found and I don't see anything that would be messing with the length.
Any suggestions on where to look?
Thanks,
Chris
Signature · 8 lines
Chris Friesen Software Designer 500 Palladium Drive, Suite 2100 Ottawa, Ontario K2N 1C2, Canada www.genband.com office:+1.343.883.2717 [email protected]
Quoted 16 lines
PHDRS
{
headersPT_PHDRPHDRS ;
interpPT_INTERP;
<snip>
}SECTIONS
{
/* Read-only sections, merged into text segment: */ PROVIDE (__executable_start=0xf2000000); . =0xf2000000+SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
<snip>
}
Quoted 6 lines
So I'm wondering...is this something wrong with our linker script, or is there a bug in our binutils? I'm no linker expert, but the interpreter sections in the script seem to match the binutils documentation that I found and I don't see anything that would be messing with the length.
Any suggestions on where to look?
It looks like your .interp input section lacks the required zero-termination.
Segher
Quoted 22 lines · Segher Boessenkool
Quoted 22 lines
PHDRS
{
headersPT_PHDRPHDRS ;
interpPT_INTERP;
<snip>
}SECTIONS
{
/* Read-only sections, merged into text segment: */ PROVIDE (__executable_start=0xf2000000); . =0xf2000000+SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
<snip>
}So I'm wondering...is this something wrong with our linker script, or is there a bug in our binutils? I'm no linker expert, but the interpreter sections in the script seem to match the binutils documentation that I found and I don't see anything that would be messing with the length.
Quoted 4 lines
Quoted 1 line
Any suggestions on where to look?
It looks like your .interp input section lacks the required zero-termination.
That's the weird thing....the actual interpreter string "/lib/ld.so.1" is in fact null-terminated, but the length in the elf headers is incorrect (0x30 instead of 0xd) and so when the kernel checks the last character in the array it sees a nonzero value.
What I don't understand is where the "/lib/ld.so.1" string is coming from and how the length gets set to the invalid value.
Chris
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
SECTIONS
{
/* Read-only sections, merged into text segment: */ PROVIDE (__executable_start=0xf2000000); . =0xf2000000+SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
<snip>
}So I'm wondering...is this something wrong with our linker script, or is there a bug in our binutils? I'm no linker expert, but the interpreter sections in the script seem to match the binutils documentation that I found and I don't see anything that would be messing with the length.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Any suggestions on where to look?
It looks like your .interp input section lacks the required zero-termination.
That's the weird thing....the actual interpreter string "
/lib/ld.so.1" is in fact null-terminated, but the length in the elf headers is incorrect (0x30instead of0xd) and so when the kernel checks the last character in the array it sees a nonzero value.What I don't understand is where the "
/lib/ld.so.1" string is coming from and how the length gets set to the invalid value.
It comes from the .interp input sections, i.e. the .interp sections in the .o files you linked together. Perhaps you have more than one of those?
Run ld with the -M option to tell you what it does; run readelf -xN where N is the section number of .interp to figure out what is actually in there.
Your PT_INTERP has a memsz < filesz, which is curious to say the least. I suspect there are multiple zero bytes in there; this is not valid in an ELF file. It would be good if the linker complained about that. The kernel loader is correct in refusing it (if that is what it is doing :-) )
Segher
Quoted 6 lines · Segher Boessenkool
Quoted 2 lines
What I don't understand is where the "
/lib/ld.so.1" string is coming from and how the length gets set to the invalid value.It comes from the .interp input sections, i.e. the .interp sections in the .o files you linked together. Perhaps you have more than one of those?
It turns out that the problem was in the linker script. The linker script was originally written for an older binutils. With the newly compiled object files there were some sections that were not explicitly specified in the linker script so they were added automatically to the interpreter section, bumping up the length.
A new linker script with explicit mention of those sections made the interpreter section look as expected.
Chris