Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aarch64 manylinux wheel and wrong pagesize #16677

Closed
frenzymadness opened this issue Jun 24, 2020 · 28 comments
Closed

aarch64 manylinux wheel and wrong pagesize #16677

frenzymadness opened this issue Jun 24, 2020 · 28 comments

Comments

@frenzymadness
Copy link
Contributor

Hello.

It seems that numpy-1.19.0-cp38-cp38-manylinux2014_aarch64.whl is built with 4kB pagesize which means that when I install it and import numpy on a system with 64kB, I get:

Traceback (most recent call last):
  File "/opt/app-root/lib64/python3.8/site-packages/numpy/core/__init__.py", line 22, in <module>
    from . import multiarray
  File "/opt/app-root/lib64/python3.8/site-packages/numpy/core/multiarray.py", line 12, in <module>
    from . import overrides
  File "/opt/app-root/lib64/python3.8/site-packages/numpy/core/overrides.py", line 7, in <module>
    from numpy.core._multiarray_umath import (
ImportError: /opt/app-root/lib64/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so: ELF load command alignment not page-aligned

According to readelf, the _multiarray_umath.cpython-38-aarch64-linux-gnu.so extension has 4 LOAD sections. The first two are aligned on 64 KiB (0x10000), the two last are aligned on 4 KiB (0x1000).

$ readelf -l ./numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so

Elf file type is DYN (Shared object file)
Entry point 0x299e0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000038b288 0x000000000038b288  R E    0x10000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  NOTE           0x0000000000000200 0x0000000000000200 0x0000000000000200
                 0x0000000000000024 0x0000000000000024  R      0x4
  GNU_EH_FRAME   0x0000000000330d40 0x0000000000330d40 0x0000000000330d40
                 0x000000000000bd4c 0x000000000000bd4c  R      0x4
  LOAD           0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x000000000001e1d4 0x000000000003e7f8  RW     0x10000
  TLS            0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000000000 0x0000000000000140  R      0x8
  GNU_RELRO      0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000001a00 0x0000000000001a00  R      0x1
  LOAD           0x00000000003f7000 0x00000000003dd000 0x00000000003dd000
                 0x0000000000003a30 0x0000000000003a30  RW     0x1000
  DYNAMIC        0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000000220 0x0000000000000220  RW     0x8
  LOAD           0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000002eb8 0x0000000000002eb8  RW     0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .dynsym .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   01     
   02     
   03     .eh_frame_hdr 
   04     .init_array .fini_array .data.rel.ro .got .got.plt .data .bss 
   05     .tbss 
   06     .init_array .fini_array .data.rel.ro .got 
   07     .gnu.hash .note.gnu.build-id 
   08     .dynamic 
   09     .dynamic .dynstr 

Is there anything you can do about it or should I take a look on recent changes in manylinux project?

Thank you.

@charris
Copy link
Member

charris commented Jun 24, 2020

Thanks for the report. I don't know what we can do about it unless there is a compiler flag we should using. Opening an issue in the manylinux project would be a good thing to do in any case.

@charris
Copy link
Member

charris commented Jun 24, 2020

Can you provide more specific information about the processor/OS you have?

@vstinner
Copy link
Contributor

Can you provide more specific information about the processor/OS you have?

This issue was noticed on RHEL8/AArch64 which uses 64 KiB page size. Fedora/AArch64 and Ubuntu/AArch64 use 4 KiB page size.

To debug the issue, you can get your page size using:

$ python3 -c 'import os; print(os.sysconf("SC_PAGESIZE"))'
4096

or:

$ python3 -c 'import mmap; print(mmap.PAGESIZE)'
4096

See also the getpagesize() function.

The page size is part of the toolchain ABI. On my x86-64, the value is provided by the dynamic linker (ld.so). It cannot by changed at runtime.

I don't know what we can do about it unless there is a compiler flag we should using. Opening an issue in the manylinux project would be a good thing to do in any case.

It's more a linker issue rather than a C compiler issue. -z max-page-size=0x10000 link option can be tried. If you can pass it as an option to the C compiler, you can use -Wl,-z,max-page-size=0x10000. I didn't try these options.

Note: If LOAD sections are aligned to 64 KiB, numpy will remain compatible with 4 KiB pages.

See also a similar issue in Julia which chose to use the patchelf command to change the section alignment:

@mattip
Copy link
Member

mattip commented Jun 24, 2020

which chose to use the patchelf command ...

Thanks for the detailed info. Is this ever a problem on ppc64 or x86? If it can be fixed with patchelf, I think this could be made part of the manywheel standard, as part of auditwheel repair.

@vstinner
Copy link
Contributor

It seems like only AArch64 arch is affected.

On ppc64, the page size is always 64 KiB. On x86 and x86-64, I think that 4 KiB is always fine.

@frenzymadness
Copy link
Contributor Author

Thank you @mattip for reporting this to auditwheel!

@vstinner
Copy link
Contributor

@mattip reported pypa/auditwheel#251 to auditwheel.

@mattip
Copy link
Member

mattip commented Jun 25, 2020

@frenzymadness could you confirm that running patchelf --page-size=65536 fixes your issue?

@frenzymadness
Copy link
Contributor Author

Unfortunately, it seems that patchelf has not effect:

# readelf -l ./venv/lib64/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so

Elf file type is DYN (Shared object file)
Entry point 0x299e0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000038b288 0x000000000038b288  R E    0x10000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  NOTE           0x0000000000000200 0x0000000000000200 0x0000000000000200
                 0x0000000000000024 0x0000000000000024  R      0x4
  GNU_EH_FRAME   0x0000000000330d40 0x0000000000330d40 0x0000000000330d40
                 0x000000000000bd4c 0x000000000000bd4c  R      0x4
  LOAD           0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x000000000001e1d4 0x000000000003e7f8  RW     0x10000
  TLS            0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000000000 0x0000000000000140  R      0x8
  GNU_RELRO      0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000001a00 0x0000000000001a00  R      0x1
  LOAD           0x00000000003f7000 0x00000000003dd000 0x00000000003dd000
                 0x0000000000003a30 0x0000000000003a30  RW     0x1000
  DYNAMIC        0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000000220 0x0000000000000220  RW     0x8
  LOAD           0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000002eb8 0x0000000000002eb8  RW     0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .dynsym .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   01     
   02     
   03     .eh_frame_hdr 
   04     .init_array .fini_array .data.rel.ro .got .got.plt .data .bss 
   05     .tbss 
   06     .init_array .fini_array .data.rel.ro .got 
   07     .gnu.hash .note.gnu.build-id 
   08     .dynamic 
   09     .dynamic .dynstr 

# patchelf --debug --page-size 65536 ./venv/lib64/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so
patching ELF file './venv/lib64/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so'
Kernel page size is 65536 bytes

# readelf -l ./venv/lib64/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so

Elf file type is DYN (Shared object file)
Entry point 0x299e0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000038b288 0x000000000038b288  R E    0x10000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  NOTE           0x0000000000000200 0x0000000000000200 0x0000000000000200
                 0x0000000000000024 0x0000000000000024  R      0x4
  GNU_EH_FRAME   0x0000000000330d40 0x0000000000330d40 0x0000000000330d40
                 0x000000000000bd4c 0x000000000000bd4c  R      0x4
  LOAD           0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x000000000001e1d4 0x000000000003e7f8  RW     0x10000
  TLS            0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000000000 0x0000000000000140  R      0x8
  GNU_RELRO      0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000001a00 0x0000000000001a00  R      0x1
  LOAD           0x00000000003f7000 0x00000000003dd000 0x00000000003dd000
                 0x0000000000003a30 0x0000000000003a30  RW     0x1000
  DYNAMIC        0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000000220 0x0000000000000220  RW     0x8
  LOAD           0x00000000003fb000 0x00000000003e1000 0x00000000003e1000
                 0x0000000000002eb8 0x0000000000002eb8  RW     0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .dynsym .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   01     
   02     
   03     .eh_frame_hdr 
   04     .init_array .fini_array .data.rel.ro .got .got.plt .data .bss 
   05     .tbss 
   06     .init_array .fini_array .data.rel.ro .got 
   07     .gnu.hash .note.gnu.build-id 
   08     .dynamic 
   09     .dynamic .dynstr

It seems that the file stays untouched.

@vstinner
Copy link
Contributor

Unfortunately, it seems that patchelf has not effect:

Which command did you try?

@codonell
Copy link

I'm talking in generalities here, and as a toolchain developer I wanted to comment on the general concepts that impact this issue.

You cannot change the alignment of a PT_LOAD segment to be larger than required by the segment. If the kernel is using larger page sizes then the kernel will only have protections on larger page sized boundaries, while the object may need such protection on smaller page boundaries e.g. two pages may be too close together with different protections (and must stay that way to allow constant offsets in generated code to work). Therefore the dynamic loader explicitly checks that the PT_LOAD segment alignment requirement and fails the to load the segment if the requirements are not met.

There is also the matter of the address congruence relationship required for the mappings. The offset must be congruent to the address modulo the alignment. If you increase the alignment the offset and address are no longer congruent. For example, take the last PT_LOAD which is offset 0x3fb000 virtual address 0x3e1000 and alignment 0x1000. The "offset - virtual address" is divisible by the alignment. If you raise the alignment to 0x10000, then 0x1a000 (the difference) is no longer divisible by the alignment. This congruence is critical to offsets, alignments, and the algorithms employed the static and dynamic loader.

In summary:

  • You cannot safely raise the PT_LOAD alignment even if patchelf could do it.
  • Built binaries must agree to a kernel page size maximum and compile for that maximum e.g. 64KiB, or kernels must agree to a runtime page size.
  • Binaries with alignments smaller than the running kernel's page size are incompatible with that running kernel ("ELF load command alignment not page-aligned" / "ELF load command address/offset not properly aligned")
  • Binaries with alignments larger than the running kernel's page size are compatible with that running kernel.

@mattip
Copy link
Member

mattip commented Jun 25, 2020

My takeaway from that is the problem must be solved at compile time.

The docker image used to build the manylinux2014 wheels is based on "arm64v8/centos:7". I wonder why they have 4k pages when redhat8 has 64k pages.

@charris
Copy link
Member

charris commented Jun 25, 2020

We should try adding flags to the nightly wheels.

@hrw
Copy link
Contributor

hrw commented Jun 25, 2020

Page size on aarch64 can be 4/16/64k...

@vstinner
Copy link
Contributor

Page size on aarch64 can be 4/16/64k...

If the linker is configured for 64k page size, the binary will work on any other smaller page size.

@AGSaidi
Copy link

AGSaidi commented Jul 3, 2020

If memory serves there was a bug with gold and alignment https://bugzilla.redhat.com/show_bug.cgi?id=1225156

Maybe manylinux2014 has this issue?

@hrw
Copy link
Contributor

hrw commented Aug 7, 2020

One solution would be to use CentOS 8 as OS on CI instead of Ubuntu (which is on 4K page size).

@mattip
Copy link
Member

mattip commented Aug 7, 2020

@hrw The problem is not CI it is in building wheels for relase. When building manylinux wheels, the environment is very tightly controlled. The manylinux2014 standard, laid out in PEP 599 must use a base image based on CentOS 7. Adopting PEP 600, which would allow us to use a more advanced base OS, has stalled, here is the tracking issue.

@ianw
Copy link

ianw commented Aug 19, 2020

My takeaway from that is the problem must be solved at compile time.

The docker image used to build the manylinux2014 wheels is based on "arm64v8/centos:7". I wonder why they have 4k pages when redhat8 has 64k pages.

The page size will come from the underlying system, not the container. So if you build on top of CentOS 7/8 as the base platform you should get things 64k aligned by default and those wheels would be compatible.

I feel like we should run a few ideas around and see if we can find a default fix for the container; because explaining to everyone who creates a arm64 wheel using the standard container on travis that they have built something by default broken on the very reference platform it's building for is a hard sell :) I've raised pypa/manylinux#735 which is probably a good place to talk about it

@geoffreyblake
Copy link

So the issue is coming from Manylinux2014_aarch64 and its version of patchelf being slightly too old.

NixOS/patchelf@0470d69
This patch was submitted June 20th, and fixes the issues with AArch64. ManyLinux2014 is pulling the 0.11 tag release which is 3 weeks older. Updating ManyLinux2014 should fix this.

@mattip
Copy link
Member

mattip commented Aug 19, 2020

Thanks. Let's continue the discussion on the manylinux issue, since I think we have localized the problem to patchelf breaking binaries that are properly compiled with 64k pages, and patchelf is part of manylinux.

@radarhere
Copy link
Contributor

manylinux2014 now has patchelf 0.12 - pypa/manylinux#741

@charris
Copy link
Member

charris commented Sep 8, 2020

@mattip Do I need to link in different OpenBLAS libraries for the 1.19.2 release to get this fix?

@mattip
Copy link
Member

mattip commented Sep 8, 2020

No. The fix is an adjustment to the patchelf tool itself. The so files are correctly produced but then patchelf, as used in auditwheel when fixing up the wheel, would break them.

@frenzymadness
Copy link
Contributor Author

@mattip Do I need to link in different OpenBLAS libraries for the 1.19.2 release to get this fix?

It would be great to have a possibility to test new wheels before you upload them to PyPI to confirm that they are fixed. Because if the problem will still be there, we would have to wait for the next release because the wheels cannot be reuploaded.

@mattip
Copy link
Member

mattip commented Sep 9, 2020

That's what https://anaconda.org/multibuild-wheels-staging/numpy is for.

@matthew-brett
Copy link
Contributor

You can always re-upload the wheels with a new build tag : https://github.com/MacPython/wiki/wiki/Build-Tags

@frenzymadness
Copy link
Contributor Author

I've tested the latest numpy wheels for Python 3.6 and 3.8 on aarch64 machine and everything seems to work well.

LOAD sections also seem to be aligned correctly.

# readelf -l venv/lib/python3.8/site-packages/numpy/core/_multiarray_umath.cpython-38-aarch64-linux-gnu.so 

Elf file type is DYN (Shared object file)
Entry point 0x298a0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000038ab08 0x000000000038ab08  R E    0x10000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_EH_FRAME   0x0000000000330830 0x0000000000330830 0x0000000000330830
                 0x000000000000bd44 0x000000000000bd44  R      0x4
  LOAD           0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x000000000001e194 0x000000000003e7b8  RW     0x10000
  TLS            0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000000000 0x0000000000000140  R      0x8
  GNU_RELRO      0x000000000038e600 0x000000000039e600 0x000000000039e600
                 0x0000000000001a00 0x0000000000001a00  R      0x1
  LOAD           0x0000000000400000 0x00000000003e0000 0x00000000003e0000
                 0x00000000000039e8 0x00000000000039e8  RW     0x10000
  NOTE           0x00000000004039c0 0x00000000003e39c0 0x00000000003e39c0
                 0x0000000000000024 0x0000000000000024  R      0x4
  DYNAMIC        0x0000000000410000 0x00000000003f0000 0x00000000003f0000
                 0x0000000000000220 0x0000000000000220  RW     0x8
  LOAD           0x0000000000410000 0x00000000003f0000 0x00000000003f0000
                 0x0000000000002e70 0x0000000000002e70  RW     0x10000

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests