| 1 | // Derived from code in LLVM, which is: |
| 2 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 3 | // See https://llvm.org/LICENSE.txt for license information. |
| 4 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 5 | |
| 6 | //! Default implementation of [crate::ObjectReader] that uses the `object` crate. |
| 7 | |
| 8 | use std::{io, mem::offset_of}; |
| 9 | |
| 10 | use object::{pe::ImportObjectHeader, xcoff, Object, ObjectSymbol}; |
| 11 | |
| 12 | use crate::coff_import_file; |
| 13 | |
| 14 | fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool { |
| 15 | // FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific |
| 16 | if sym.kind() == object::SymbolKind::File || sym.kind() == object::SymbolKind::Section { |
| 17 | return false; |
| 18 | } |
| 19 | if !sym.is_global() { |
| 20 | return false; |
| 21 | } |
| 22 | if sym.is_undefined() { |
| 23 | return false; |
| 24 | } |
| 25 | true |
| 26 | } |
| 27 | |
| 28 | pub fn get_native_object_symbols( |
| 29 | buf: &[u8], |
| 30 | f: &mut dyn FnMut(&[u8]) -> io::Result<()>, |
| 31 | ) -> io::Result<bool> { |
| 32 | // FIXME match what LLVM does |
| 33 | |
| 34 | match object::File::parse(data:buf) { |
| 35 | Ok(file: File<'_>) => { |
| 36 | for sym: Symbol<'_, '_> in file.symbols() { |
| 37 | if !is_archive_symbol(&sym) { |
| 38 | continue; |
| 39 | } |
| 40 | f(sym.name_bytes().expect(msg:"FIXME" ))?; |
| 41 | } |
| 42 | Ok(true) |
| 43 | } |
| 44 | Err(_) => { |
| 45 | let mut offset: u64 = 0; |
| 46 | // Try to handle this as a COFF import library. |
| 47 | if ImportObjectHeader::parse(data:buf, &mut offset).is_ok() { |
| 48 | coff_import_file::get_short_import_symbol(buf, f).or(res:Ok(false)) |
| 49 | } else { |
| 50 | Ok(false) |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | pub fn is_ec_object(obj: &[u8]) -> bool { |
| 57 | match object::FileKind::parse(data:obj) { |
| 58 | Ok(object::FileKind::Coff) => { |
| 59 | u16::from_le_bytes([obj[0], obj[1]]) != object::pe::IMAGE_FILE_MACHINE_ARM64 |
| 60 | } |
| 61 | Ok(object::FileKind::CoffImport) => { |
| 62 | // COFF Import Header is: |
| 63 | // sig1: u16 |
| 64 | // sig2: u16 |
| 65 | // version: u16 |
| 66 | // machine: u16 |
| 67 | u16::from_le_bytes([obj[6], obj[7]]) != object::pe::IMAGE_FILE_MACHINE_ARM64 |
| 68 | } |
| 69 | _ => false, |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | pub fn is_64_bit_symbolic_file(obj: &[u8]) -> bool { |
| 74 | object::FileKind::parse(data:obj).is_ok_and(|kind: FileKind| match kind { |
| 75 | object::FileKind::Elf64 |
| 76 | | object::FileKind::MachO64 |
| 77 | | object::FileKind::Pe64 |
| 78 | | object::FileKind::Xcoff64 |
| 79 | | object::FileKind::MachOFat64 => true, |
| 80 | object::FileKind::Elf32 |
| 81 | | object::FileKind::MachO32 |
| 82 | | object::FileKind::Pe32 |
| 83 | | object::FileKind::Xcoff32 |
| 84 | | object::FileKind::MachOFat32 |
| 85 | | object::FileKind::Coff |
| 86 | | object::FileKind::CoffBig |
| 87 | | object::FileKind::CoffImport => false, |
| 88 | _ => panic!("Unexpected file kind" ), |
| 89 | }) |
| 90 | } |
| 91 | |
| 92 | // Log2 of PAGESIZE(4096) on an AIX system. |
| 93 | const LOG2_OF_AIXPAGE_SIZE: u32 = 12; |
| 94 | |
| 95 | // In the AIX big archive format, since the data content follows the member file |
| 96 | // name, if the name ends on an odd byte, an extra byte will be added for |
| 97 | // padding. This ensures that the data within the member file starts at an even |
| 98 | // byte. |
| 99 | const MIN_BIG_ARCHIVE_MEM_DATA_ALIGN: u32 = 2; |
| 100 | |
| 101 | fn get_aux_max_alignment<AuxiliaryHeader: object::read::xcoff::AuxHeader>( |
| 102 | aux_header_size: u16, |
| 103 | aux_header: Option<&AuxiliaryHeader>, |
| 104 | log_2_of_max_align: u32, |
| 105 | offset_of_modtype: u16, |
| 106 | ) -> u32 { |
| 107 | // If the member doesn't have an auxiliary header, it isn't a loadable object |
| 108 | // and so it just needs aligning at the minimum value. |
| 109 | let Some(aux_header) = aux_header else { |
| 110 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 111 | }; |
| 112 | |
| 113 | // If the auxiliary header does not have both MaxAlignOfData and |
| 114 | // MaxAlignOfText field, it is not a loadable shared object file, so align at |
| 115 | // the minimum value. The 'ModuleType' member is located right after |
| 116 | // 'MaxAlignOfData' in the AuxiliaryHeader. |
| 117 | if aux_header_size < offset_of_modtype { |
| 118 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 119 | } |
| 120 | |
| 121 | // If the XCOFF object file does not have a loader section, it is not |
| 122 | // loadable, so align at the minimum value. |
| 123 | if aux_header.o_snloader() == 0 { |
| 124 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 125 | } |
| 126 | |
| 127 | // The content of the loadable member file needs to be aligned at MAX(maximum |
| 128 | // alignment of .text, maximum alignment of .data) if there are both fields. |
| 129 | // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a |
| 130 | // word boundary, while 64-bit members are aligned on a PAGESIZE(2^12=4096) |
| 131 | // boundary. |
| 132 | let log_2_of_align = u32::from(std::cmp::max( |
| 133 | aux_header.o_algntext(), |
| 134 | aux_header.o_algndata(), |
| 135 | )); |
| 136 | 1 << (if log_2_of_align > LOG2_OF_AIXPAGE_SIZE { |
| 137 | log_2_of_max_align |
| 138 | } else { |
| 139 | log_2_of_align |
| 140 | }) |
| 141 | } |
| 142 | |
| 143 | // AIX big archives may contain shared object members. The AIX OS requires these |
| 144 | // members to be aligned if they are 64-bit and recommends it for 32-bit |
| 145 | // members. This ensures that when these members are loaded they are aligned in |
| 146 | // memory. |
| 147 | pub fn get_member_alignment(obj: &[u8]) -> u32 { |
| 148 | use object::read::xcoff::FileHeader; |
| 149 | |
| 150 | // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a |
| 151 | // word boundary, while 64-bit members are aligned on a PAGESIZE boundary. |
| 152 | match object::FileKind::parse(obj) { |
| 153 | Ok(object::FileKind::Xcoff64) => { |
| 154 | let mut offset = 0; |
| 155 | let Ok(header) = xcoff::FileHeader64::parse(obj, &mut offset) else { |
| 156 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 157 | }; |
| 158 | let Ok(aux_header) = header.aux_header(obj, &mut offset) else { |
| 159 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 160 | }; |
| 161 | get_aux_max_alignment( |
| 162 | header.f_opthdr(), |
| 163 | aux_header, |
| 164 | LOG2_OF_AIXPAGE_SIZE, |
| 165 | offset_of!(object::xcoff::AuxHeader64, o_modtype) |
| 166 | .try_into() |
| 167 | .unwrap(), |
| 168 | ) |
| 169 | } |
| 170 | Ok(object::FileKind::Xcoff32) => { |
| 171 | let mut offset = 0; |
| 172 | let Ok(header) = object::xcoff::FileHeader32::parse(obj, &mut offset) else { |
| 173 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 174 | }; |
| 175 | let Ok(aux_header) = header.aux_header(obj, &mut offset) else { |
| 176 | return MIN_BIG_ARCHIVE_MEM_DATA_ALIGN; |
| 177 | }; |
| 178 | get_aux_max_alignment( |
| 179 | header.f_opthdr(), |
| 180 | aux_header, |
| 181 | 2, |
| 182 | offset_of!(object::xcoff::AuxHeader32, o_modtype) |
| 183 | .try_into() |
| 184 | .unwrap(), |
| 185 | ) |
| 186 | } |
| 187 | _ => MIN_BIG_ARCHIVE_MEM_DATA_ALIGN, |
| 188 | } |
| 189 | } |
| 190 | |