Nhìn vào lịch sử các thế hệ ARM mà Raspberry Pi từng dùng thì thật bất ngờ vì đây là một con chip rất cũ
Ngay cả vào thời điểm Raspberry Pi B+ ra mắt năm 2014, nhân ARM1176 được dùng cũng là thiết kế từ năm 2003, tức đã 11 năm tuổi
Vì vậy, khi build trên nền tảng khác như Raspberry Pi đời mới hơn, việc có thể cần chỉ định cờ kiến trúc để tạo mã tương thích cũng không có gì lạ
Tuy nhiên, nếu ngay cả khi build trên chính Raspberry Pi B+ mà kiến trúc đúng cũng không được chọn làm mặc định, thì trông giống như lỗi cấu hình ở phía giá trị mặc định của bản phân phối
Tôi nhớ Pi đời đầu dùng chip còn dư từ các hộp TV
Những sản phẩm như vậy vì lý do giá thành nên hiếm khi nhét nhiều năng lực tính toán hơn mức cần thiết
Ngay từ đầu nó đã là sản phẩm định làm thành máy tính giá rẻ
Thực ra không phải build trên B+
Bài viết nói rằng “tôi lấy binary từ host build là Pi 4B nhanh hơn nhiều rồi thử chạy trên thiết bị cũ này thì gặp illegal instruction”
Việc này giống như cố chạy một file .EXE build bằng MSVC mới nhất trên Windows 11 ở một PC cũ cài Windows XP
Có lẽ toàn bộ bản phân phối Pi chạy trên Pi 4B cũng nhiều khả năng không chạy được trên B+, và cả kernel cũng có thể đã được biên dịch theo cùng cách
Có vẻ bài viết hay bài báo chưa nói rõ, nhưng đây chẳng phải là bug sao?
Tôi tìm bug của LLVM thì thấy có một mục trông gần như cùng vấn đề, nhưng đó là bug từ năm 2012 và đã bị đóng. Nhìn vài bình luận cuối thì có vẻ thực ra có thể vẫn chưa được sửa, nhưng tôi chỉ lướt qua nên cũng có thể hiểu nhầm https://github.com/llvm/llvm-project/issues/13989
Xem lại thì cuối bài có nói rằng nếu truyền đích một cách tường minh thì sẽ tạo ra chương trình chạy được. Vậy có vẻ giống một dạng lỗi cấu hình, và trên Unix tôi nghĩ đích mặc định sẽ là bộ xử lý hiện tại, nhưng không chắc
Bug được liên kết có vẻ là vấn đề vẫn tạo mã sai dù đã đặt đúng đích, và may là hiện giờ dường như không phải tình huống đó
Đúng vậy. Bug được liên kết là vấn đề dù đã bảo compiler nhắm tới armv6 nhưng nó vẫn phát ra lệnh armv7
Vấn đề của Rachel được giải quyết bằng cách báo cho compiler nhắm tới armv6, nên bug đó có vẻ đã được sửa và là chuyện riêng với vấn đề này
Rõ ràng là bug, nhưng tác giả có vẻ thay vì report thì viết một bài blog với tiêu đề hơi câu click rồi kết bằng “quá kỳ lạ”
Cơ sở dữ liệu tôi đang làm, ClickHouse, nỗ lực khá nhiều để duy trì khả năng tương thích với phần cứng rất cũ
Binary ARM tiêu chuẩn yêu cầu Armv8.2 từ năm 2016 và có thể dùng trên các mẫu từ Raspberry Pi 2 trở về sau. Binary x86 chạy trên phần cứng khoảng năm 2010 có SSE4.2 và các lệnh pclmul* để CRC nhanh
Chúng tôi cũng build binary cho các hệ thống chỉ có Armv8.0 và SSE2, nhưng không kiểm thử bằng CI. Script cài đặt nhanh sẽ tải xuống và giải nén binary phù hợp với host đích
Nhìn chung tôi thấy việc cân bằng giữa tương thích ngược và tận dụng các tính năng CPU của những thế hệ AArch64 mới là chuyện khó https://en.wikipedia.org/wiki/AArch64
Có nhiều tổ chức với ngân sách eo hẹp đến mức đáng ngạc nhiên, ví dụ các trường đại học ở nước mới nổi hoặc những người dùng theo sở thích không có khả năng nâng cấp phần cứng
Về mặt kỹ thuật, một điểm khá phiền là các cờ CPU trong /proc/cpuinfo không phải lúc nào cũng tương ứng với cờ -march= truyền cho compiler. Ví dụ chúng xuất hiện khác nhau như "lrcpc" và "rcpc"
Để làm cho việc này chạy đúng, thực tế phải quản lý hai tập cờ
Trong trường hợp như vậy, tôi nghĩ cung cấp nhiều bản build để khách hàng có thể chọn bản gần nhất với kiến trúc của họ sẽ có lợi cho tất cả
Vấn đề rất có khả năng là đích cấu hình đã thay đổi trong gói clang-13 hiện tại của bookworm
Cụ thể, trong bullseye và clang-11, đích mặc định là armv6k-unknown-linux-gnueabihf, còn trong bookworm và clang-13 là arm-unknown-linux-gnueabihf
Hoặc cũng có thể phía LLVM đã thay đổi giá trị mặc định của cấu hình build đó
Có lẽ đây không phải là thay đổi có chủ ý
Như các bình luận xung quanh đã nhắc đến /etc/env.d/gcc, khá có khả năng đây là cách đọc thông tin từ môi trường
Triple mặc định có lẽ sẽ là giá trị kiểu arm-unknown-linux nếu clang không tìm thấy hoặc không được truyền thông tin cụ thể hơn, nhưng có vẻ cơ chế cung cấp target cụ thể hơn đã bị hỏng
Điều này có thể có nghĩa là không có buildbot ARMv6, hoặc cũng có thể là có buildbot nhưng ở đó cấu hình ngầm định vẫn hoạt động tốt
LLVM là một cross compiler thật sự tốt. Có thể build từ hầu như bất kỳ target nào sang bất kỳ target nào mà không gặp vấn đề lớn
Clang thì kém hấp dẫn hơn một chút. Nếu nó được build kèm hỗ trợ target và bạn có thể cho nó biết chính xác cần build cho target nào, có lẽ nó sẽ xử lý đúng. Trong bài này, phỏng đoán ban đầu sai, nhưng khi cung cấp thêm thông tin thì nó đã hoạt động đúng
Tình hình với runtime library còn tệ hơn. Dù đã build cho một target như armv4, bạn vẫn phải tìm libc tương ứng, v.v., và có thể phải cho compiler biết vị trí của các thư viện và header đó; phần này đến nay các chi tiết vẫn chưa rõ ràng
Phần lớn distro và compiler trên thực tế đã bỏ hỗ trợ ARMv6 từ vài năm trước
Tôi từng gặp vấn đề tương tự khi build binary cho một NAS Synology cũ
Tại sao Clang lại phải đọc thông tin từ /etc/env.d/gcc?
clang/clang++ đọc các target flag và profile từ /etc/env.d/gcc, và việc giữ cho chúng đúng là trách nhiệm của hệ điều hành
Trên hệ điều hành này, có vẻ phần quản lý đó không được thực hiện đúng
Chiếc Gentoo ARM SBC của tôi dựa trên kiến trúc armv4 còn cũ hơn vẫn chạy ổn với các bản cập nhật gcc/clang mới nhất grep CTARGET /etc/env.d/gcc -r /etc/env.d/gcc/armv4tl-softfloat-linux-gnueabi-11.3.0:CTARGET="armv4tl-softfloat-linux-gnueabi"
/etc/env.d là thư mục riêng của Gentoo dùng để định nghĩa các biến môi trường mặc định cho phiên người dùng
Clang không có tính năng đọc thư mục đó, nên không nên giả định rằng các distro khác cũng có nó
Chỉ là cấu hình compiler của Gentoo đọc biến môi trường CTARGET để chọn target, và Gentoo dùng /etc/env.d để thiết lập giá trị đó
Bài viết không nói rõ đó là Debian hay Raspbian, và nếu là Debian thì là port armel hay armhf
Nếu không có thông tin đó thì việc tranh luận LLVM biên dịch cho tập lệnh nào cũng không có nhiều ý nghĩa. Đó là vấn đề phụ thuộc vào thiết lập target native của LLVM
Nhân tiện, llvm-toolchaim-snapshot của Debian vẫn hỗ trợ armel, vốn lấy ARMv5T làm baseline. Tuy nhiên hiện có một lỗi riêng trong thư viện OpenMP của LLVM khiến build không thành công
Điểm kỳ lạ là bản thân binary Clang được biên dịch với tập lệnh tương thích với Pi B+, nhưng lại không nhắm đến tập lệnh tương thích với Pi B+
Điều này thật sự lạ. Vì không phải là bảo dùng nó như một cross compiler, nên về lý thuyết host và target phải giống nhau
Có lẽ image đó là Raspbian. Có vẻ không có lý do gì để không giả định như vậy
Có lẽ sẽ hữu ích nếu biết output của lệnh dpkg-architecture và nội dung file /etc/os-release
Không có những thứ đó thì khó đưa ra bình luận hữu ích
Tiêu đề tiếc là hơi giật gân
Đây là thay đổi target mặc định, và clang vẫn có thể build binary cho Pi B+
Chỉ cần chỉ định rõ kiến trúc là được. Vì vậy có lẽ nên sửa tiêu đề một chút để thể hiện rõ hơn rằng đây là thay đổi cấu hình mặc định
Nếu build ngay trên chính target machine mà vẫn không tạo được binary cho target machine đó, thì tôi không thấy nó giật gân đến mức ấy
Thật thú vị vì có vẻ khi debug lý do một chương trình không chạy trên ARM, có thể tiếp cận đại khái theo cách này
Tôi có một bản build Unity Linux không chạy được trong container; ngay cả khi truyền flag amd64 khi chạy Docker, Unity mono vẫn cố gọi một system call không dùng được
Tôi đã tìm được cách обход qua nên chưa debug tiếp. Tôi bật development mode và đổi thiết lập build để không dùng mono
Một ngày nào đó tôi nên đào lại để học thêm
1 bình luận
Ý kiến trên Hacker News
Nhìn vào lịch sử các thế hệ ARM mà Raspberry Pi từng dùng thì thật bất ngờ vì đây là một con chip rất cũ
Ngay cả vào thời điểm Raspberry Pi B+ ra mắt năm 2014, nhân ARM1176 được dùng cũng là thiết kế từ năm 2003, tức đã 11 năm tuổi
Vì vậy, khi build trên nền tảng khác như Raspberry Pi đời mới hơn, việc có thể cần chỉ định cờ kiến trúc để tạo mã tương thích cũng không có gì lạ
Tuy nhiên, nếu ngay cả khi build trên chính Raspberry Pi B+ mà kiến trúc đúng cũng không được chọn làm mặc định, thì trông giống như lỗi cấu hình ở phía giá trị mặc định của bản phân phối
Những sản phẩm như vậy vì lý do giá thành nên hiếm khi nhét nhiều năng lực tính toán hơn mức cần thiết
Bài viết nói rằng “tôi lấy binary từ host build là Pi 4B nhanh hơn nhiều rồi thử chạy trên thiết bị cũ này thì gặp illegal instruction”
Việc này giống như cố chạy một file .EXE build bằng MSVC mới nhất trên Windows 11 ở một PC cũ cài Windows XP
Có lẽ toàn bộ bản phân phối Pi chạy trên Pi 4B cũng nhiều khả năng không chạy được trên B+, và cả kernel cũng có thể đã được biên dịch theo cùng cách
Có vẻ bài viết hay bài báo chưa nói rõ, nhưng đây chẳng phải là bug sao?
Tôi tìm bug của LLVM thì thấy có một mục trông gần như cùng vấn đề, nhưng đó là bug từ năm 2012 và đã bị đóng. Nhìn vài bình luận cuối thì có vẻ thực ra có thể vẫn chưa được sửa, nhưng tôi chỉ lướt qua nên cũng có thể hiểu nhầm
https://github.com/llvm/llvm-project/issues/13989
Xem lại thì cuối bài có nói rằng nếu truyền đích một cách tường minh thì sẽ tạo ra chương trình chạy được. Vậy có vẻ giống một dạng lỗi cấu hình, và trên Unix tôi nghĩ đích mặc định sẽ là bộ xử lý hiện tại, nhưng không chắc
Bug được liên kết có vẻ là vấn đề vẫn tạo mã sai dù đã đặt đúng đích, và may là hiện giờ dường như không phải tình huống đó
Vấn đề của Rachel được giải quyết bằng cách báo cho compiler nhắm tới armv6, nên bug đó có vẻ đã được sửa và là chuyện riêng với vấn đề này
Cơ sở dữ liệu tôi đang làm, ClickHouse, nỗ lực khá nhiều để duy trì khả năng tương thích với phần cứng rất cũ
Binary ARM tiêu chuẩn yêu cầu Armv8.2 từ năm 2016 và có thể dùng trên các mẫu từ Raspberry Pi 2 trở về sau. Binary x86 chạy trên phần cứng khoảng năm 2010 có SSE4.2 và các lệnh pclmul* để CRC nhanh
Chúng tôi cũng build binary cho các hệ thống chỉ có Armv8.0 và SSE2, nhưng không kiểm thử bằng CI. Script cài đặt nhanh sẽ tải xuống và giải nén binary phù hợp với host đích
Nhìn chung tôi thấy việc cân bằng giữa tương thích ngược và tận dụng các tính năng CPU của những thế hệ AArch64 mới là chuyện khó
https://en.wikipedia.org/wiki/AArch64
Có nhiều tổ chức với ngân sách eo hẹp đến mức đáng ngạc nhiên, ví dụ các trường đại học ở nước mới nổi hoặc những người dùng theo sở thích không có khả năng nâng cấp phần cứng
Về mặt kỹ thuật, một điểm khá phiền là các cờ CPU trong /proc/cpuinfo không phải lúc nào cũng tương ứng với cờ -march= truyền cho compiler. Ví dụ chúng xuất hiện khác nhau như "lrcpc" và "rcpc"
Để làm cho việc này chạy đúng, thực tế phải quản lý hai tập cờ
Vấn đề rất có khả năng là đích cấu hình đã thay đổi trong gói clang-13 hiện tại của bookworm
Cụ thể, trong bullseye và clang-11, đích mặc định là armv6k-unknown-linux-gnueabihf, còn trong bookworm và clang-13 là arm-unknown-linux-gnueabihf
Hoặc cũng có thể phía LLVM đã thay đổi giá trị mặc định của cấu hình build đó
Tuy nhiên, khi so sánh [1] và [2], trong file rules có một kiểm tra gọn gàng kiểu “nếu DEB_HOST_ARCH là armhf thì đặt LLVM_HOST_TRIPLE thành armv6k”, nên có vẻ nó xác nhận thay đổi cấu hình build
[1] http://raspbian.raspberrypi.org/raspbian/pool/main/l/llvm-to...
[2] http://raspbian.raspberrypi.org/raspbian/pool/main/l/llvm-to...
Có lẽ đây không phải là thay đổi có chủ ý
Như các bình luận xung quanh đã nhắc đến
/etc/env.d/gcc, khá có khả năng đây là cách đọc thông tin từ môi trườngTriple mặc định có lẽ sẽ là giá trị kiểu
arm-unknown-linuxnếu clang không tìm thấy hoặc không được truyền thông tin cụ thể hơn, nhưng có vẻ cơ chế cung cấp target cụ thể hơn đã bị hỏngĐiều này có thể có nghĩa là không có buildbot ARMv6, hoặc cũng có thể là có buildbot nhưng ở đó cấu hình ngầm định vẫn hoạt động tốt
LLVM là một cross compiler thật sự tốt. Có thể build từ hầu như bất kỳ target nào sang bất kỳ target nào mà không gặp vấn đề lớn
Clang thì kém hấp dẫn hơn một chút. Nếu nó được build kèm hỗ trợ target và bạn có thể cho nó biết chính xác cần build cho target nào, có lẽ nó sẽ xử lý đúng. Trong bài này, phỏng đoán ban đầu sai, nhưng khi cung cấp thêm thông tin thì nó đã hoạt động đúng
Tình hình với runtime library còn tệ hơn. Dù đã build cho một target như armv4, bạn vẫn phải tìm libc tương ứng, v.v., và có thể phải cho compiler biết vị trí của các thư viện và header đó; phần này đến nay các chi tiết vẫn chưa rõ ràng
Tôi từng gặp vấn đề tương tự khi build binary cho một NAS Synology cũ
/etc/env.d/gcc?clang/clang++đọc các target flag và profile từ/etc/env.d/gcc, và việc giữ cho chúng đúng là trách nhiệm của hệ điều hànhTrên hệ điều hành này, có vẻ phần quản lý đó không được thực hiện đúng
Chiếc Gentoo ARM SBC của tôi dựa trên kiến trúc armv4 còn cũ hơn vẫn chạy ổn với các bản cập nhật gcc/clang mới nhất
grep CTARGET /etc/env.d/gcc -r/etc/env.d/gcc/armv4tl-softfloat-linux-gnueabi-11.3.0:CTARGET="armv4tl-softfloat-linux-gnueabi"/etc/env.dlà thư mục riêng của Gentoo dùng để định nghĩa các biến môi trường mặc định cho phiên người dùngClang không có tính năng đọc thư mục đó, nên không nên giả định rằng các distro khác cũng có nó
Chỉ là cấu hình compiler của Gentoo đọc biến môi trường CTARGET để chọn target, và Gentoo dùng
/etc/env.dđể thiết lập giá trị đóBài viết không nói rõ đó là Debian hay Raspbian, và nếu là Debian thì là port armel hay armhf
Nếu không có thông tin đó thì việc tranh luận LLVM biên dịch cho tập lệnh nào cũng không có nhiều ý nghĩa. Đó là vấn đề phụ thuộc vào thiết lập target native của LLVM
Nhân tiện,
llvm-toolchaim-snapshotcủa Debian vẫn hỗ trợ armel, vốn lấy ARMv5T làm baseline. Tuy nhiên hiện có một lỗi riêng trong thư viện OpenMP của LLVM khiến build không thành côngĐiều này thật sự lạ. Vì không phải là bảo dùng nó như một cross compiler, nên về lý thuyết host và target phải giống nhau
Có lẽ image đó là Raspbian. Có vẻ không có lý do gì để không giả định như vậy
Có lẽ sẽ hữu ích nếu biết output của lệnh
dpkg-architecturevà nội dung file/etc/os-releaseKhông có những thứ đó thì khó đưa ra bình luận hữu ích
Tiêu đề tiếc là hơi giật gân
Đây là thay đổi target mặc định, và clang vẫn có thể build binary cho Pi B+
Chỉ cần chỉ định rõ kiến trúc là được. Vì vậy có lẽ nên sửa tiêu đề một chút để thể hiện rõ hơn rằng đây là thay đổi cấu hình mặc định
Thật thú vị vì có vẻ khi debug lý do một chương trình không chạy trên ARM, có thể tiếp cận đại khái theo cách này
Tôi có một bản build Unity Linux không chạy được trong container; ngay cả khi truyền flag amd64 khi chạy Docker, Unity mono vẫn cố gọi một system call không dùng được
Tôi đã tìm được cách обход qua nên chưa debug tiếp. Tôi bật development mode và đổi thiết lập build để không dùng mono
Một ngày nào đó tôi nên đào lại để học thêm