diff --git a/src/auditor/dynamic_linkage.jl b/src/auditor/dynamic_linkage.jl index b1fa3aa69..e47da428b 100644 --- a/src/auditor/dynamic_linkage.jl +++ b/src/auditor/dynamic_linkage.jl @@ -1,5 +1,30 @@ using ObjectFile.ELF +function os_from_elf_note(oh::ELFHandle) + for section in Sections(oh) + section_handle(section) == ELF.SHT_NOTE || continue + seek(oh, section_offset(section)) + name_length = read(oh, UInt32) + iszero(name_length) && continue + descriptor_length = read(oh, UInt32) + note_type = read(oh, UInt32) + name = String(read(oh, name_length - 1)) # skip trailing NUL + if note_type == 1 + # Technically it's part of the Linux specification that any executable should + # have an ELF note with type 1, name GNU, and descriptor length ≥4, but in + # practice I haven't observed that consistently, especially on musl. So for + # now, only bother checking FreeBSD, which uses an ELF note rather than OS/ABI + # to identify itself on AArch64 and RISC-V. + if name == "FreeBSD" && descriptor_length == 4 + return name + end + end + end + return nothing +end + +os_from_elf_note(::ObjectHandle) = nothing + """ platform_for_object(oh::ObjectHandle) @@ -39,7 +64,9 @@ function platform_for_object(oh::ObjectHandle) end end - if oh.ei.osabi == ELF.ELFOSABI_LINUX || oh.ei.osabi == ELF.ELFOSABI_NONE + if oh.ei.osabi == ELF.ELFOSABI_NONE + return Platform(arch, os_from_elf_note(oh) == "FreeBSD" ? "freebsd" : "linux") + elseif oh.ei.osabi == ELF.ELFOSABI_LINUX return Platform(arch, "linux") elseif oh.ei.osabi == ELF.ELFOSABI_FREEBSD return Platform(arch, "freebsd") @@ -108,6 +135,13 @@ function is_for_platform(h::ObjectHandle, platform::AbstractPlatform) else error("Unknown OS ABI type $(typeof(platform))") end + else + # If no OSABI, check whether it has a matching ELF note + if Sys.isfreebsd(platform) + if os_from_elf_note(h) != "FreeBSD" + return false + end + end end # Check that the ELF arch matches our own m = h.header.e_machine diff --git a/src/auditor/extra_checks.jl b/src/auditor/extra_checks.jl index e8935d6ad..319f5b269 100644 --- a/src/auditor/extra_checks.jl +++ b/src/auditor/extra_checks.jl @@ -5,7 +5,19 @@ using Dates: DateTime, datetime2unix function check_os_abi(oh::ObjectHandle, p::AbstractPlatform, rest...; verbose::Bool = false, kwargs...) if Sys.isfreebsd(p) - if oh.ei.osabi != ELF.ELFOSABI_FREEBSD + # On AArch64 and RISC-V, FreeBSD uses an ELF note section to identify itself rather + # than OS/ABI in the ELF header. In that case, the OS/ABI will be generic Unix (NONE). + # See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=252490 and + # https://github.com/freebsd/freebsd-src/blob/main/lib/csu/common/crtbrand.S + if oh.ei.osabi == ELF.ELFOSABI_NONE + if os_from_elf_note(oh) != "FreeBSD" + if verbose + @warn("$(basename(path(oh))) does not have a FreeBSD-branded ELF note " * + "and may be unrecognized or unusable on $p") + end + return false + end + elseif oh.ei.osabi != ELF.ELFOSABI_FREEBSD # The dynamic loader should not have problems in this case, but the # linker may not appreciate. Let the user know about this. if verbose @@ -17,7 +29,8 @@ function check_os_abi(oh::ObjectHandle, p::AbstractPlatform, rest...; verbose::B end return false end - elseif call_abi(p) == "eabihf" + end + if call_abi(p) == "eabihf" # Make sure the object file has the hard-float ABI. See Table 4-2 of # "ELF for the ARM Architecture" document # (https://developer.arm.com/documentation/ihi0044/e/). Note: `0x000`