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 | |