Summary: | File offsets for PT_LOAD segments and resulting inequivalent memory aliases | ||
---|---|---|---|
Product: | binutils | Reporter: | John David Anglin <danglin> |
Component: | ld | Assignee: | unassigned |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | amodra, ian |
Priority: | P2 | ||
Version: | unspecified | ||
Target Milestone: | --- | ||
Host: | Target: | hppa-linux | |
Build: | Last reconfirmed: |
Description
John David Anglin
2011-01-08 19:22:26 UTC
This might also be a glibc bug: $ strace /lib/ld-2.11.2.so ./xxx3execve("/lib/ld-2.11.2.so", ["/lib/ld-2.11.2.so", "./xxx3"], [/* 17 vars */]) = 0 brk(0) = 0x4119a000 open("./xxx3", O_RDONLY) = 3 read(3, "\177ELF\1\2\1\3\0\0\0\0\0\0\0\0\0\2\0\17\0\0\0\1\0\1\3T\0\0\0004"..., 512) = 512 fstat64(3, {st_mode=0, st_size=4380866642020, ...}) = 0 getcwd("/home2/dave/inequiv", 128) = 20 mmap(0x10000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x10000 mmap(0x11000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x11000 close(3) = 0 newuname({sys="Linux", node="gsyprf11", ...}) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=0, st_size=0, ...}) = 0 mmap(NULL, 62087, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40192000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\2\1\3\0\0\0\0\0\0\0\0\0\3\0\17\0\0\0\1\0\2\0\350\0\0\0004"..., 512) = 512 fstat64(3, {st_mode=0, st_size=0, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40000000 mmap(NULL, 1449240, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x403ee000 mmap(0x40547000, 28672, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x158000) = 0x40547000 mmap(0x4054e000, 7448, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x4054e000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40001000 mprotect(0x403ee000, 1413120, PROT_READ|PROT_WRITE) = 0 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40002000 mprotect(0x403ee000, 1413120, PROT_READ|PROT_EXEC) = 0 munmap(0x40192000, 62087) = 0 exit_group(0) = ? The same fd (3) is used for all mmap calls. Possibly, different physical memory pages would be allocated if different file descriptors were used. (In reply to comment #0) > 2) Align the PT_LOAD segments in the file. I'm not sure how to do this > at the moment. Currently, maxpagesize is 0x1000, so this might not be > a too onerous increase in file size. On the otherhand, maxpagesize > probably should be 0x10000. It can changed by the maximum alignment of sections in the segment. 1) Adjust hppalinux.sh to modify TEXT_ADDR and SHLIB_TEXT_ADDR. I need to add 0x400000 (4 MB) to make the addresses equivalent. This rather chews up the virtual address space for shared libraries. The breaks glibc build. My sense is there is no reasonable binutils fix but I'm not sure. > These in fact point to the same page in physical memory.
Really? 00010000-00011000 and 00011000-00012000 are not different pages?
On Mon, 14 Feb 2011, amodra at gmail dot com wrote:
> > These in fact point to the same page in physical memory.
>
> Really? 00010000-00011000 and 00011000-00012000 are not different pages?
They map to the same page as far as I can tell (both maps appear in the
list iterated using vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap,
pgoff, pgoff)). This can also be seen by looking at /proc/$PID/maps.
When multiple shared writeable mappings exist, I believed they are COWed.
So, effectively only one map is writeable.
Non equivalent aliases are a problem for architectures such as PA8800/PA8900.
They don't support non equivalent aliases in the sense that a write doesn't
invalidate non equivalent aliases. The only thing that saves us is the
former address range is write protected, and it's rare to try to read using
the text map. It seems possible that the text map could be corrupted
via the data map. So, this might be a security issue.
The V-Class machines are even worse than PA8800/PA8900 because they don't
support non equivalent aliases regardless of whether they are read-only
or not.
These non equivalent aliases occur typically on the boundary page between
text and data. The linux dynamic loader mmaps these regions as MAP_FIXED.
They are not mapped with MAP_SHARED but it seems the maps are shared
for shared libraries. So far, it seems the hppa linux dynamic loader
always maps shared pages with equivalent aliases except for the boundary
page.
I think this is potentially an issue for certain MIPS and ARM cpus but
I don't know the details on whether they support non equivalent aliases
or not. As far as I can tell, the same occurs for x86, etc, but I don't
think the non equivalent aliases matter, at least on linux. On the
other hand, it looks like windows starts sections on page boundaries.
Probably, it would be best if load segments were aranged in executables
to optionally start on a file page boundary. This would avoid the double
flush and having two non equivalent address ranges map to the same page.
Don't really want to start all sections on a page boundary as this would
waste a lot of file space.
I have looked a bit at trying to do this, but don't have a solution at
the moment.
Dave
If they are the same page, doesn't that mean your maxpagesize is wildly incorrect? You must have maxpagesize at least as large as a memory page. > If they are the same page, doesn't that mean your maxpagesize is wildly
> incorrect? You must have maxpagesize at least as large as a memory page.
maxpagesize is set to 0x1000 which is the standard page size for parisc
linux.
The issue is not the virtual addresses of the page but the placement
of the loadable segments in the file. These segments are mmap'd from
the file to physical memory. Although the pages could be different
in memory, they are not.
The issue can be seen by looking at the mappings for a trivial program like:
int main () { return 0 }
For the the main executable, there is only one file page for text
and data. Run program under gdb with a break on main. Then inspect
the mappings.
The two mappings could be made equivalent, but this messes up shared
library support.
The hardware can support larger page sizes, but as far as I know nobody
uses them on linux. There was some effort to provide support for larger
page sizes in the linux kernel but I don't believe the cache flush and
TLB support is complete.
Possibly, ELF_COMMONPAGESIZE should be defined and ELF_MAXPAGESIZE
increased to the linux kernel maximum.
Dave
It sounds like you are saying that the PA does not operate as most other processors do. It seems very odd to me that the PA Linux kernel can not map the same file page to two different locations in virtual memory. That is what the Linux kernel does on other processors. You are talking about two mappings to the same physical page, but that is not what the ELF executable is requesting. It is requesting two different virtual pages mapping to the same page in the file. If that is indeed a limitation of PA Linux, then the only fix is to change the default linker script so that the first page of the data segment does not overlap with the last page of the text segment. The way to do that is to set DATA_ADDR in the appropriate ld/emulparams file. For example, DATA_ADDR="ALIGN(${MAXPAGESIZE})" That should always force the data segment to start on a new page. On Mon, 14 Feb 2011, ian at airs dot com wrote:
> If that is indeed a limitation of PA Linux, then the only fix is to change the
> default linker script so that the first page of the data segment does not
> overlap with the last page of the text segment. The way to do that is to set
> DATA_ADDR in the appropriate ld/emulparams file. For example,
>
> DATA_ADDR="ALIGN(${MAXPAGESIZE})"
>
> That should always force the data segment to start on a new page.
I'll take another look but I think this only changes the virtual address
of the data segment and not the underlying file organization.
Looking at a typical file compiled for hppa64-hpux with readelf, we have:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x4000000000000000 0x0000000000000000
0x0000000000024bc8 0x0000000000024bc8 R E 8
LOAD 0x0000000000025000 0x8000000100000000 0x0000000000000000
0x0000000000002ef0 0x0000000000003550 RW 8
Note the file offset for the data segment starts on a page boundary.
I think that I need to achieve the same on hppa-linux.
Dave
If you arrange for the data section to start on a new page, then the linker will always put that data section on a new page in the file. It has to, because ELF specifies that the file offset is always equal to the page offset modulo the page size (unless you link with -N or -n). On Mon, 14 Feb 2011, ian at airs dot com wrote:
> If you arrange for the data section to start on a new page, then the linker
> will always put that data section on a new page in the file. It has to,
> because ELF specifies that the file offset is always equal to the page offset
> modulo the page size (unless you link with -N or -n).
Ah, that's the clue! Testing fix.
Dave
CVSROOT: /cvs/src Module name: src Changes by: danglin@sourceware.org 2011-02-18 18:20:29 Modified files: ld : ChangeLog ld/emulparams : hppalinux.sh Log message: PR ld/12376 emulparams/hppalinux.sh (DATA_ADDR): Define. (SHLIB_DATA_ADDR): Likewise. Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/ChangeLog.diff?cvsroot=src&r1=1.2283&r2=1.2284 http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/emulparams/hppalinux.sh.diff?cvsroot=src&r1=1.14&r2=1.15 Fixed. CVSROOT: /cvs/src Module name: src Branch: binutils-2_21-branch Changes by: danglin@sourceware.org 2011-03-14 02:35:08 Modified files: ld : ChangeLog ld/emulparams : hppalinux.sh Log message: Backport from mainline: 2011-02-18 John David Anglin <dave.anglin@nrc-cnnrc.gc.ca> PR ld/12376 emulparams/hppalinux.sh (DATA_ADDR): Define. (SHLIB_DATA_ADDR): Likewise. Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/ChangeLog.diff?cvsroot=src&only_with_tag=binutils-2_21-branch&r1=1.2222.2.15&r2=1.2222.2.16 http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/emulparams/hppalinux.sh.diff?cvsroot=src&only_with_tag=binutils-2_21-branch&r1=1.14&r2=1.14.10.1 The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7bcc503f3ef52fcac0d9be31f1b82440ec7ed2ff commit 7bcc503f3ef52fcac0d9be31f1b82440ec7ed2ff Author: H.J. Lu <hjl.tools@gmail.com> Date: Wed Mar 2 19:07:01 2016 -0800 Skip ld-elf/pr19162.d for hppa-*-* ld-elf/pr19162.d fails for hppa-*-* since Dave Anglin's fix for PR 12376 makes the data segment always start on a page boundary. * testsuite/ld-elf/pr19162.d: Skip hppa-*-*. |