This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [PATCH 0/6] ld: Add "-z textonly" option to ELF linker
On Tue, Nov 14, 2017 at 4:55 AM, Michael Matz <matz@suse.de> wrote:
> Hi,
>
> On Mon, 13 Nov 2017, H.J. Lu wrote:
>
>> Text-only LOAD segment has the same requirement for segment alignment
>> and page sizes as GNU_RELRO segment. But for GNU_RELRO segment, the
>> segment may not end at the same address of the end of data segment.
>> But for text-only LOAD segment, it is exactly the same as text LOAD
>> segment.
>>
>> The new "-z textonly" option will turn on both text-only LOAD segment
>> and GNU_RELRO segment. The new "-z notextonly" option will turn off
>> only text-only LOAD segment. "-z relro" is updated not to turn off
>> text-only LOAD segment. "-z norelro" is updated to turn off both
>> GNU_RELRO segment and text-only LOAD segment.
>>
>> When there is a text-only LOAD segment, create a new LOAD segment if the
>> previous section contains text and the current section doesn't or vice
>> versa:
>>
>> Elf file type is DYN (Shared object file)
>> Entry point 0x200ff0
>> There are 7 program headers, starting at offset 52
>>
>> Program Headers:
>> Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
>> LOAD 0x000000 0x00000000 0x00000000 0x00200 0x00200 R 0x200000
>> LOAD 0x000fd0 0x00200fd0 0x00200fd0 0x0002b 0x0002b R E 0x200000
>> LOAD 0x001000 0x00201000 0x00201000 0x00058 0x00058 R 0x200000
>> LOAD 0x200f80 0x00400f80 0x00400f80 0x000a0 0x000a0 RW 0x200000
>
> This seems strange. The (file)offset of the fourth segment is much larger
> than than the added sizes of the individual segments, which isn't
> necessary.
>
>> Since there are more than 2 LOAD segments, the minimum file size is
>> bigger than the maximum page size which is 2MB (0x200000):
>>
>> -rwxr-xr-x 1 hjl hjl 2104892 Nov 12 11:53 libfoo.so
>
> This is the result of the above problem, but it's not necessary. Like in
> a traditional two-LOAD-segment file, which also isn't larger than 2MB,
> just because the page size is 2MB. It achieves this by mapping the same
> file bytes multiple times, and you could do the same. E.g. for the file
> above the better layout would be:
>
> LOAD 0x000000 0x00000000 0x00000000 0x00200 0x00200 R 0x200000
> LOAD 0x000fd0 0x00200fd0 0x00200fd0 0x0002b 0x0002b R E 0x200000
> LOAD 0x001000 0x00201000 0x00201000 0x00058 0x00058 R 0x200000
> LOAD 0x001060 0x00401060 0x00401060 0x000a0 0x000a0 RW 0x200000
>
> But even then something is wrong: The RE and the second R page are both
> mapped onto 0x0020xxxx, i.e. the same 2MB page, so can't have different
> protections. (You'd need 4k pages again, which defeats the whole purpose
> of having 2MB pages in the file to start with).
You have found out yourself that it is impossible. In elf.c, there are
static file_ptr
vma_page_aligned_bias (bfd_vma vma, ufile_ptr off, bfd_vma maxpagesize)
{
/* PR binutils/16199: Handle an alignment of zero. */
if (maxpagesize == 0)
maxpagesize = 1;
return ((vma - off) % maxpagesize);
}
It is used to adjust file offset to align to the maximum page size. It can
be quite large, up to the maximum page size.
>>
>> "-z max-page-size=0x1000" can be used to reduce the maximum page size to
>> 4KB (0x1000):
>>
>> -rwxr-xr-x 1 hjl hjl 11836 Nov 12 13:22 libfoo.so
>>
>> Elf file type is DYN (Shared object file)
>> Entry point 0x1ff0
>> There are 7 program headers, starting at offset 52
>>
>> Program Headers:
>> Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
>> LOAD 0x000000 0x00000000 0x00000000 0x00200 0x00200 R 0x1000
>> LOAD 0x000fd0 0x00001fd0 0x00001fd0 0x0002b 0x0002b R E 0x1000
>> LOAD 0x001000 0x00002000 0x00002000 0x00058 0x00058 R 0x1000
>> LOAD 0x001f80 0x00002f80 0x00002f80 0x000a0 0x000a0 RW 0x1000
>
> Also this shows strangeness. The second R and the RW page are both mapped
> to 0x2xxx, the same 4k page, which can't have different protections. Also
> the offsets are somehow wrong. The second R page starts at 0x1000 (in
> file), and is 0x58 bytes long (memsize is 0x58 as well). So the next
> thing in file should start at 0x1060, not 0x1f80 (and should be mapped to
> 0x3060, not 0x2f80).
>
You need to look at the whole picture:
LOAD 0x000000 0x00000000 0x00000000 0x00200 0x00200 R 0x1000
LOAD 0x000fd0 0x00001fd0 0x00001fd0 0x0002b 0x0002b R E 0x1000
LOAD 0x001000 0x00002000 0x00002000 0x00058 0x00058 R 0x1000
LOAD 0x001f80 0x00002f80 0x00002f80 0x000a0 0x000a0 RW 0x1000
DYNAMIC 0x001f80 0x00002f80 0x00002f80 0x00080 0x00080 RW 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x001f80 0x00002f80 0x00002f80 0x00080 0x00080 R 0x1
Section to Segment mapping:
Segment Sections...
00 .hash .gnu.hash .dynsym .dynstr .rela.plt
01 .plt .text
02 .rodata .eh_frame
03 .dynamic .got.plt
04 .dynamic
05
06 .dynamic
Part of the 4th segment is in GNU_RELRO segment which becomes read-only after
relocation and is merged with the 3rd R page:
[ 8] .rodata PROGBITS 00002000 001000 000006 01 AMS 0 0 1
[ 9] .eh_frame PROGBITS 00002008 001008 000050 00 A 0 0 4
[10] .dynamic DYNAMIC 00002f80 001f80 000080 08 WA 4 0 4
[11] .got.plt PROGBITS 00003000 002000 000020 08 WA 0 0 8
After relocation, the 3rd page has .rodata, .eh_frame and .dynamic.
--
H.J.