Skip to main content

rucc_object/
source.rs

1//! An object file written from what a file of assembly says, rather than from a compilation.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.1, the paragraph that says we also accept
4//! assembly as input.
5//!
6//! # Why this is not [`crate::Text`] and [`crate::Data`]
7//!
8//! Those two are the compiler's view of a file and they are the right view of one. A function is a
9//! run of bytes with a name and a length, a variable is an image with a name and a place worked out
10//! from what the variable is, and neither carries a section name because where a thing goes is an
11//! answer rather than a question. That is exactly what makes them the wrong shape for assembly.
12//!
13//! A file of assembly says the section, so the place is a question again, and it may say a section
14//! this compiler would never have chosen and flags that go with it. It puts names at offsets rather
15//! than around images, so `.long 0` followed by `foo:` is four bytes belonging to nothing with a
16//! name after them, which no list of named variables can hold. It defines names that are not at any
17//! offset at all, which is what `.set` and `.equ` produce. And it may name a symbol in the middle of
18//! a section, with a size the program stated rather than one worked out from the bytes.
19//!
20//! So this is the assembler's view: a list of sections that each know their own name, flags and
21//! bytes, and a list of names that point into them. Bending one into the other would mean deciding
22//! here what a program already said, and a wrong answer about which section something is in is not
23//! visible until a link or a load.
24//!
25//! The two views meet at the [`object`] crate's writer, which is what both call, and at the short
26//! list of format opinions beside it, which is what both ask where the formats differ. So
27//! there is one place that knows how an object file is laid out and one that knows what each format
28//! calls the things in it.
29
30use object::write::{Object as Writer, Relocation, SectionId, Symbol, SymbolSection};
31use object::{
32    Architecture, Endianness, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, elf,
33    pe,
34};
35use rucc_base::hash::{Map, Set};
36use rucc_target::aarch64::Fixup;
37use rucc_target::{ObjectFormat, TargetInfo};
38use rucc_tuple::Arch;
39
40use crate::file::{Error, Flavour};
41use crate::section::{Array, Binding, Compress, Info, Reloc, Visibility};
42
43/// One section, as a file of assembly describes one.
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct Part {
46    /// What it is called, with the leading dot the source wrote.
47    pub name: String,
48    /// Its bytes, which are empty for a section that says how big it is and holds none of them.
49    pub bytes: Vec<u8>,
50    /// How long it is. The same as the length of the bytes for every section that has any, and the
51    /// whole of what a `@nobits` section says about itself.
52    pub size: u64,
53    /// The boundary it starts on, which is the largest any directive in it asked for.
54    pub align: u64,
55    /// The flags and the type, which the source states and this does not work out.
56    pub shape: Shape,
57    /// Every place in it that names something, counted from the start of the section.
58    pub relocs: Vec<Reloc>,
59    /// The COMDAT it is, on COFF, where `.section name,"flags",discard,symbol` makes a section one
60    /// the linker keeps a single copy of out of every object that has one about the same symbol,
61    /// and the section group it is in on ELF, where `.section name,"axG",@progbits,symbol,comdat`
62    /// says the same. [`None`] for every other section and on Mach-O.
63    pub group: Option<Group>,
64    /// The name whose section this one goes with, on ELF, which is what the `o` flag and the
65    /// operand after the type say. The linker keeps or drops the two together and lays this one out
66    /// in the order of the other, which is what a record of where a patcher's room is in front of
67    /// a function needs. [`None`] for every other section.
68    pub link: Option<String>,
69}
70
71/// A section the linker keeps one copy of, which is what the third and fourth operands of
72/// `.section` say on COFF and the `G` flag and the operands after the type say on ELF. Every
73/// section of one ELF object that names the same symbol is in the one group.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct Group {
76    /// The name the group is about, which the file defines in the section.
77    pub symbol: String,
78    /// Which copy the linker keeps.
79    pub keep: Keep,
80}
81
82/// How the linker picks the copy of a [`Group`] it keeps, in the words gas and llvm-mc take for
83/// each of COFF's selection numbers.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum Keep {
86    /// `one_only`: there must be only one, and two is an error.
87    One,
88    /// `discard`: any one, and the rest are dropped. What a `.refptr.` pointer is.
89    Any,
90    /// `same_size`: any one, and two of different sizes are an error.
91    SameSize,
92    /// `same_contents`: any one, and two with different bytes are an error.
93    SameContents,
94    /// `largest`: the biggest one.
95    Largest,
96    /// `newest`: the newest one, which no toolchain writes and link.exe does not implement.
97    Newest,
98    /// A group that is not a COMDAT, on ELF, which is `G` without `comdat` after the symbol. The
99    /// linker keeps or drops its sections together and keeps every copy of them.
100    Together,
101}
102
103impl Keep {
104    /// The word for it, as `.section` spells it.
105    #[must_use]
106    pub fn of(word: &str) -> Option<Keep> {
107        Some(match word {
108            "one_only" => Keep::One,
109            "discard" => Keep::Any,
110            "same_size" => Keep::SameSize,
111            "same_contents" => Keep::SameContents,
112            "largest" => Keep::Largest,
113            "newest" => Keep::Newest,
114            _ => return None,
115        })
116    }
117
118    pub(crate) const fn kind(self) -> object::ComdatKind {
119        match self {
120            Keep::One => object::ComdatKind::NoDuplicates,
121            Keep::Any => object::ComdatKind::Any,
122            Keep::SameSize => object::ComdatKind::SameSize,
123            Keep::SameContents => object::ComdatKind::ExactMatch,
124            Keep::Largest => object::ComdatKind::Largest,
125            Keep::Newest => object::ComdatKind::Newest,
126            // The writer underneath writes every ELF group as a COMDAT, and the word that says so
127            // is cleared afterwards. See [`together`].
128            Keep::Together => object::ComdatKind::Any,
129        }
130    }
131}
132
133/// What a section is, which on ELF is a handful of flag letters and a type.
134///
135/// Held as the separate facts rather than as one of a fixed list of kinds, because the list is not
136/// fixed: a program may write `.section .init.text,"ax",@progbits` and mean a section this compiler
137/// has no name for, and the letters are the whole of what it said about it. The writer underneath
138/// takes a [`SectionKind`], so `Shape::kind` is the one place that turns these back into one, and
139/// the cases it cannot say are written as flags directly.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
141pub struct Shape {
142    /// `a`: the section takes space in the loaded image. A section without this is for a debugger
143    /// or a linker to read and is not in the program at run time.
144    pub alloc: bool,
145    /// `w`: the program may write to it.
146    pub write: bool,
147    /// `x`: the processor may execute it.
148    pub exec: bool,
149    /// `T`: one copy per thread rather than one copy per program.
150    pub thread: bool,
151    /// Whether the file carries the bytes. False is `@nobits`, which is what `.bss` is.
152    pub bits: bool,
153    /// Which kind of table of function addresses this is, for the three ELF has a type for.
154    pub array: Option<Array>,
155    /// `M`: how long each entry is in a section of constants the linker may keep one copy of
156    /// wherever two objects hold the same one, and zero for a section that is not one of those.
157    /// gcc puts a `double` it loads from memory in `.rodata.cst8`, which is one of these.
158    pub merge: u64,
159    /// `S`: the entries are strings ended by a zero rather than all of one length, which is where
160    /// gcc puts every string literal. Only means anything beside `merge`.
161    pub strings: bool,
162    /// `@note`: the bytes are notes for a loader or a tool to find by type, such as the build ID
163    /// and the entry point notes a boot loader reads out of the kernel. Only means anything beside
164    /// `bits`, and only on ELF, whose section type says it.
165    pub note: bool,
166    /// `R`: the linker keeps the section when it drops the ones nothing refers to, which is what
167    /// `__attribute__((retain))` asks for on ELF.
168    pub retain: bool,
169    /// The type and attributes of a Mach-O section, in the one word the format keeps them in,
170    /// which is what [`Shape::mach`] works out. Zero on the other two formats, where the fields
171    /// above are the whole answer, and zero is also an ordinary Mach-O section with nothing said.
172    pub mach: u32,
173    /// The characteristics of a COFF section whose `.section` gave COFF's own letters, which is
174    /// what [`Shape::coff`] works out. Zero when it gave none, and then the writer works them out
175    /// from the name and the kind, as it does for a section this compiler made.
176    pub coff: u32,
177}
178
179impl Shape {
180    /// What a section of this name is when the source named it and said nothing else.
181    ///
182    /// `.text`, `.data` and the rest are names an assembler already knows the flags of, which is
183    /// why a program may write `.data` on its own and why `.section .data` without letters is the
184    /// same section rather than an unallocated one. A name nothing here knows gets the flags of an
185    /// ordinary allocated writable section. An ELF `.section` in a file of assembly reads
186    /// [`Shape::unflagged`] instead, which is gas's table.
187    #[must_use]
188    pub fn of(name: &str) -> Shape {
189        let base = Shape { alloc: true, bits: true, ..Shape::default() };
190        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
191        let head = head.split_once('.').map_or(head, |(first, _)| first);
192        match head {
193            "text" | "init" | "fini" => Shape { exec: true, ..base },
194            "rodata" | "eh_frame_hdr" => base,
195            "bss" => Shape { write: true, bits: false, ..base },
196            "tbss" => Shape { write: true, thread: true, bits: false, ..base },
197            "tdata" => Shape { write: true, thread: true, ..base },
198            // The three the linker gathers and the startup code walks. The type is what makes one
199            // of them that, rather than the name: a section of the ordinary type under the same
200            // name is gathered into the same run and called by nobody.
201            _ if Array::of(name).is_some() => Shape { write: true, array: Array::of(name), ..base },
202            // Not allocated, because nothing in the running program reads it. A debugger reads it
203            // out of the file, and a section marked allocated would take space in every process.
204            "debug_info" | "debug_abbrev" | "debug_line" | "debug_str" | "comment" => {
205                Shape { alloc: false, bits: true, ..Shape::default() }
206            }
207            _ => Shape { write: true, ..base },
208        }
209    }
210
211    /// What gas makes a section of this name on ELF when `.section` gives it no letters, and the
212    /// type it gives it when the letters come with no type.
213    ///
214    /// The names are binutils' table of special sections. Most of them hold for the name and for
215    /// any name that adds a dot and more to it, so `.text.foo` is code and `.textfoo` is not, a
216    /// few hold only for the name itself, and `.note` holds for anything that starts with it.
217    /// Everything else is bytes with no flags at all, which is what the kernel's
218    /// `.pushsection .discard.ibt_endbr_noseal` and the like are, and which [`Shape::of`] used to
219    /// make writable data.
220    #[must_use]
221    pub fn unflagged(name: &str) -> Shape {
222        let bits = Shape { bits: true, ..Shape::default() };
223        let family = |head: &str| {
224            name.strip_prefix(head).is_some_and(|rest| rest.is_empty() || rest.starts_with('.'))
225        };
226        let alloc = Shape { alloc: true, ..bits };
227        if family(".text") || name == ".init" || name == ".fini" {
228            Shape { exec: true, ..alloc }
229        } else if family(".data") || name == ".data1" {
230            Shape { write: true, ..alloc }
231        } else if family(".rodata") || name == ".rodata1" {
232            alloc
233        } else if family(".bss") || family(".gnu.linkonce.b") {
234            Shape { write: true, bits: false, ..alloc }
235        } else if family(".tbss") {
236            Shape { write: true, thread: true, bits: false, ..alloc }
237        } else if family(".tdata") {
238            Shape { write: true, thread: true, ..alloc }
239        } else if let Some(array) = Array::of(name) {
240            Shape { write: true, array: Some(array), ..alloc }
241        } else if name.starts_with(".note") && name != ".note.GNU-stack" {
242            Shape { note: true, ..bits }
243        } else {
244            bits
245        }
246    }
247
248    /// The flags a section of this name has whatever letters the source gave it.
249    ///
250    /// gas adds these to the letters rather than taking the letters alone, so
251    /// `.section .data.rel.ro.local,"a"` is writable all the same. GMP names its jump tables that
252    /// way, and a linker making a position independent program refuses an address it would have to
253    /// fix up in a section it may not write. Only the names gas treats as a family are here, which
254    /// is fewer than [`Shape::of`] knows: `.init.data` is not executable just because `.init` is.
255    #[must_use]
256    pub fn implied(name: &str) -> Shape {
257        let base = Shape { alloc: true, ..Shape::default() };
258        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
259        let head = head.split_once('.').map_or(head, |(first, _)| first);
260        match head {
261            "text" => Shape { exec: true, ..base },
262            "rodata" => base,
263            "data" | "bss" => Shape { write: true, ..base },
264            "tdata" | "tbss" => Shape { write: true, thread: true, ..base },
265            _ if Array::of(name).is_some() => Shape { write: true, ..base },
266            _ => Shape::default(),
267        }
268    }
269
270    /// What a Mach-O section is, from its segment, its section and the type and attributes a
271    /// `.section` directive gave after them.
272    ///
273    /// The word the format keeps is the answer and the fields beside it are filled in from it, so
274    /// that what reads a shape without knowing the format still sees code as code and a zero
275    /// filled section as one that holds no bytes.
276    ///
277    /// # Errors
278    ///
279    /// A type or an attribute Apple's assembler does not take, as a sentence.
280    pub fn mach(
281        segment: &str,
282        section: &str,
283        kind: Option<&str>,
284        attributes: &[&str],
285    ) -> Result<Shape, String> {
286        let mach = crate::macho::section_flags(segment, section, kind, attributes)?;
287        let exec = mach & object::macho::S_ATTR_PURE_INSTRUCTIONS.0 != 0;
288        let typ = object::macho::SectionFlags(mach).typ();
289        let thread = matches!(
290            typ,
291            object::macho::S_THREAD_LOCAL_REGULAR | object::macho::S_THREAD_LOCAL_ZEROFILL
292        );
293        Ok(Shape {
294            alloc: true,
295            write: segment != "__TEXT",
296            exec,
297            thread,
298            bits: !crate::macho::zero_filled(mach),
299            mach,
300            ..Shape::default()
301        })
302    }
303
304    /// What a COFF section is, from the letters after its name in `.section`.
305    ///
306    /// COFF's letters are not ELF's, and `d`, `r` and `n` mean nothing to ELF at all. These are
307    /// read the way llvm-mc reads them, which is also how gas reads them: `x` is code, `d` is data,
308    /// `b` is zero filled, `r` takes away writing and `w` gives it back, `n` is a section the
309    /// linker drops, `i` holds options for the linker, `y` is not readable, `D` may be discarded,
310    /// `s` is shared, and `a` is taken and means nothing. A section with no letters at all is
311    /// readable and writable data. `.drectve,"yni"` is the one a DLL's exports are said in.
312    ///
313    /// # Errors
314    ///
315    /// A letter that is not one of those, as the letter.
316    pub fn coff(letters: &str) -> Result<Shape, char> {
317        let (mut code, mut data, mut zero, mut drop, mut info) =
318            (false, false, false, false, false);
319        let (mut read, mut write, mut shared, mut discard) = (true, true, false, false);
320        let mut writable = false;
321        for letter in letters.chars() {
322            match letter {
323                'a' => {}
324                'b' => zero = true,
325                'd' => {
326                    data = true;
327                    write = true;
328                }
329                'n' => drop = true,
330                'D' => discard = true,
331                'r' => {
332                    writable = false;
333                    write = false;
334                    data |= !code;
335                }
336                's' => {
337                    shared = true;
338                    data = true;
339                    write = true;
340                }
341                'w' => {
342                    write = true;
343                    writable = true;
344                }
345                'x' => {
346                    code = true;
347                    write &= writable;
348                }
349                'y' => {
350                    read = false;
351                    write = false;
352                }
353                'i' => info = true,
354                other => return Err(other),
355            }
356        }
357        let mut flags = 0;
358        if code {
359            flags |= pe::IMAGE_SCN_CNT_CODE.0 | pe::IMAGE_SCN_MEM_EXECUTE.0;
360        }
361        if data {
362            flags |= pe::IMAGE_SCN_CNT_INITIALIZED_DATA.0;
363        }
364        if zero && !data {
365            flags |= pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA.0;
366        }
367        if drop {
368            flags |= pe::IMAGE_SCN_LNK_REMOVE.0;
369        }
370        if read {
371            flags |= pe::IMAGE_SCN_MEM_READ.0;
372        }
373        if write {
374            flags |= pe::IMAGE_SCN_MEM_WRITE.0;
375        }
376        if discard {
377            flags |= pe::IMAGE_SCN_MEM_DISCARDABLE.0;
378        }
379        if shared {
380            flags |= pe::IMAGE_SCN_MEM_SHARED.0;
381        }
382        if info {
383            flags |= pe::IMAGE_SCN_LNK_INFO.0;
384        }
385        Ok(Shape {
386            alloc: !drop && !info,
387            write,
388            exec: code,
389            bits: !(zero && !data),
390            coff: flags,
391            ..Shape::default()
392        })
393    }
394
395    /// The flag word ELF holds these in.
396    ///
397    /// Not public, and neither are the two below it. The fields above are the whole of what a
398    /// caller says about a section, and how ELF spells them is this crate's business: a reader that
399    /// had to name an ELF constant to describe an executable section would be one that could not
400    /// describe one for any other format.
401    pub(crate) fn sh_flags(self) -> elf::SectionFlags {
402        let mut flags = 0;
403        if self.alloc {
404            flags |= elf::SHF_ALLOC.0;
405        }
406        if self.write {
407            flags |= elf::SHF_WRITE.0;
408        }
409        if self.exec {
410            flags |= elf::SHF_EXECINSTR.0;
411        }
412        if self.thread {
413            flags |= elf::SHF_TLS.0;
414        }
415        if self.retain {
416            flags |= elf::SHF_GNU_RETAIN.0;
417        }
418        if self.merge != 0 {
419            flags |= elf::SHF_MERGE.0;
420            if self.strings {
421                flags |= elf::SHF_STRINGS.0;
422            }
423        }
424        elf::SectionFlags(flags)
425    }
426
427    /// The type ELF holds in the header beside those flags.
428    pub(crate) fn sh_type(self) -> elf::SectionType {
429        match self.array {
430            _ if !self.bits => elf::SHT_NOBITS,
431            _ if self.note => elf::SHT_NOTE,
432            Some(Array::Init) => elf::SHT_INIT_ARRAY,
433            Some(Array::Fini) => elf::SHT_FINI_ARRAY,
434            Some(Array::Preinit) => elf::SHT_PREINIT_ARRAY,
435            None => elf::SHT_PROGBITS,
436        }
437    }
438
439    /// What the writer underneath calls the nearest thing to this.
440    ///
441    /// It is told the flags in full afterwards, so this only has to be close enough that nothing
442    /// else the writer decides from the kind comes out wrong, which is the default alignment and
443    /// whether it appends bytes or counts them.
444    pub(crate) const fn kind(self) -> SectionKind {
445        match self {
446            Shape { bits: false, thread: true, .. } => SectionKind::UninitializedTls,
447            Shape { bits: false, .. } => SectionKind::UninitializedData,
448            Shape { thread: true, .. } => SectionKind::Tls,
449            Shape { exec: true, .. } => SectionKind::Text,
450            Shape { alloc: false, .. } => SectionKind::Other,
451            Shape { write: false, .. } => SectionKind::ReadOnlyData,
452            Shape { .. } => SectionKind::Data,
453        }
454    }
455}
456
457/// One name in the symbol table, as a file of assembly defines one.
458#[derive(Debug, Clone, PartialEq, Eq)]
459pub struct Name {
460    /// The name, spelled as the source spelled it.
461    pub name: String,
462    /// Where it is.
463    pub at: Held,
464    /// How long the thing it names is, which is what `.size` said and is zero when nothing did.
465    pub size: u64,
466    /// What kind of thing it names, which is what `.type` said.
467    pub sort: Sort,
468    /// Who can see it.
469    pub binding: Binding,
470    /// How far outside a shared library it reaches.
471    pub visibility: Visibility,
472}
473
474/// Where a name is, which is four different things and not an offset with special cases.
475#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub enum Held {
477    /// At an offset into one of the sections, which is what a label is.
478    In {
479        /// Which section, as an index into the list given alongside.
480        part: usize,
481        /// How far into it.
482        offset: u64,
483    },
484    /// A number rather than a place, which is what `.set` and `.equ` produce. The linker resolves
485    /// a reference to one to the number itself and there is nothing for it to be relative to.
486    Absolute(u64),
487    /// That much zeroed space asked of the linker under this name, which is `.comm` and `.lcomm`.
488    /// Every definition of the name across every object is merged into one.
489    Common {
490        /// How much space.
491        size: u64,
492        /// What boundary it has to start on. ELF records this where an ordinary symbol records its
493        /// address, which is why the two cannot both be said.
494        align: u64,
495    },
496    /// Named and not defined here, which the linker has to find somewhere else.
497    Undefined,
498}
499
500/// What kind of thing a name names, which is what `.type` says.
501#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
502pub enum Sort {
503    /// `@function`. A call through the procedure linkage table may be made to it.
504    Func,
505    /// `@object`. Data.
506    Object,
507    /// `@tls_object`. A thread-local variable, which a linker checks relocations against.
508    Thread,
509    /// `@gnu_indirect_function`, ELF's `STT_GNU_IFUNC`. The name is at a resolver rather than at
510    /// the function: the dynamic loader calls what is there once, before anything else can, and
511    /// the address it hands back is what every call and every pointer to the name then reaches.
512    ///
513    /// That makes it a name nothing in this file may be worked out against, because the place it
514    /// marks is not where a call to it goes. A reference to one stays a relocation against the
515    /// name even when the name is local and in the same section, where any other name would have
516    /// been turned into the section and an offset. See `moved`.
517    Ifunc,
518    /// `.file`, which names the source this was assembled from rather than anything in it.
519    ///
520    /// Not a thing `.type` can say, and here because it is a symbol and there is nowhere else for
521    /// it. A debugger reads it and so does `nm`, and gas writes one for every file that says its
522    /// own name, which is every file gcc produces.
523    File,
524    /// Nothing was said, which is what a plain label gets and is a real answer rather than a
525    /// missing one: gas writes `STT_NOTYPE` for a label nobody stated a type for.
526    #[default]
527    Untyped,
528}
529
530/// Everything an assembled file holds: its sections, and the names that point into them.
531#[derive(Debug, Clone, Default, PartialEq, Eq)]
532pub struct Assembled {
533    /// The sections, in the order the file first mentioned each of them.
534    pub parts: Vec<Part>,
535    /// The names, in the order the file defined or first referred to each of them.
536    pub names: Vec<Name>,
537    /// Whether the file said `.subsections_via_symbols`, which tells a Mach-O linker it may cut
538    /// every section at every symbol in it. Nothing on the other two formats.
539    pub subsections: bool,
540}
541
542/// That, as a relocatable object in whichever of the two formats the target wants.
543///
544/// Both formats, the same two the module that writes a compilation writes it into, and the
545/// differences between them are the same answers there. That is the whole reason this is not two
546/// functions: a file of assembly names its own sections and a compilation does not, but what a
547/// relocation is called and whether a symbol has anywhere to keep a visibility are facts about the
548/// format rather than about where the bytes came from, and a second set of answers to them would
549/// be a second set to get wrong.
550///
551/// What a [`Part`] carries is the section type and flags the source wrote in as many words. ELF has
552/// a field for each of them and they are written down as they stand. COFF has no field they map
553/// onto, so what the section is comes from the kind on the shape and the writer underneath turns it
554/// into the characteristics every other Windows assembler writes. A program that means a Windows
555/// section to be something other than what its name says is a program that has to say so some other
556/// way, which is what `.section` with COFF's own letters is for and what tamnd/rucc#1514 left open.
557///
558/// # Errors
559///
560/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for a
561/// relocation against a name the list does not hold or one this format has no relocation for.
562pub fn assembled(input: &Assembled, target: &TargetInfo) -> Result<Vec<u8>, Error> {
563    assembled_described(input, target, &Info::default())
564}
565
566/// The same object as [`assembled`], with the debug sections in `info` added to it.
567///
568/// For a compilation that went through a listing and asked for debug information, where the line
569/// table and the entries are built from the compilation rather than read from the file. A
570/// relocation in a chunk names another chunk or a name the file defines, and the second is written
571/// against the section the name is in, for the reason [`crate::write`] gives: a distance to a
572/// global name is not one a linker can work out.
573///
574/// # Errors
575///
576/// As for [`assembled`], and [`Error::Refused`] for a chunk that names something the file does
577/// not define.
578pub fn assembled_described(
579    input: &Assembled,
580    target: &TargetInfo,
581    info: &Info,
582) -> Result<Vec<u8>, Error> {
583    // x86-64, AArch64 and i386 on ELF and COFF, and AArch64 on Mach-O, which is a function
584    // of its own since what it answers differently is most of what is below.
585    let (flavour, machine) = match Flavour::of(target) {
586        Some(flavour) => match flavour.machine(target.tuple.arch()) {
587            Some(machine) => (flavour, machine),
588            None => return Err(Error::Format { triple: target.tuple.to_string() }),
589        },
590        None if target.tuple.arch() == Arch::Aarch64
591            && target.object_format == ObjectFormat::MachO =>
592        {
593            return crate::macho::write(input, target, info);
594        }
595        None => return Err(Error::Format { triple: target.tuple.to_string() }),
596    };
597    let flags_of = |kind, after| flavour.reloc(machine, kind, after);
598    // Whether the addend of a field of an instruction goes into the field rather than into the
599    // relocation, which is what a format without addends does. See [`crate::coff::carry`].
600    let carried = machine == Architecture::Aarch64 && flavour == Flavour::Coff;
601    let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
602    // A name in a file of assembly is already the name in the object, underscore and all, which the
603    // writer underneath would otherwise put a second one on for COFF on i386.
604    obj.set_mangling(object::write::Mangling::None);
605
606    // Every section first, because a symbol says which one it is in and a relocation says which one
607    // it is written into, so both need the whole list before either can be added.
608    let mut made = Vec::with_capacity(input.parts.len());
609    for part in &input.parts {
610        let id = obj.add_section(Vec::new(), part.name.clone().into_bytes(), part.shape.kind());
611        // The flags in full rather than whatever the kind implied, because the kind is a summary of
612        // them and the source said them exactly. A section the program wrote `"ax"` on is executable
613        // whether or not its name is one this compiler would have made executable. Only where the
614        // format has the fields: see [`Flavour::stated`].
615        if let Some(mut flags) = flavour.stated(part.shape) {
616            // A member of a group says so in its own flags on ELF, which the writer underneath
617            // leaves to whoever states them.
618            if let SectionFlags::Elf { sh_flags, .. } = &mut flags {
619                if part.group.is_some() {
620                    sh_flags.0 |= elf::SHF_GROUP.0;
621                }
622                if part.link.is_some() {
623                    sh_flags.0 |= elf::SHF_LINK_ORDER.0;
624                }
625            }
626            obj.section_mut(id).flags = flags;
627        }
628        let align = part.align.max(1);
629        if part.shape.bits {
630            obj.append_section_data(id, &part.bytes, align);
631        } else {
632            obj.append_section_bss(id, part.size, align);
633        }
634        // A COMDAT's section symbol comes before the symbol the group is about, which is the
635        // order the COFF writer wants the two in, so it is asked for here and not left to the
636        // first relocation that happens to need it.
637        if part.group.is_some() && flavour == Flavour::Coff {
638            obj.section_symbol(id);
639        }
640        made.push(id);
641    }
642
643    // Which relocations point at the section a name is in rather than at the name, and which names
644    // are then asked for by nothing and left out, before either is written down.
645    let defined: Map<&str, &Name> =
646        input.names.iter().map(|name| (name.name.as_str(), name)).collect();
647    // COFF for i386 has temporary names of its own. See [`unseen`].
648    let pe32 = flavour == Flavour::Coff && machine == Architecture::I386;
649    let onto = |reloc: &Reloc| moved(flavour, pe32, input, &defined, reloc);
650    let wanted: Set<&str> = input
651        .parts
652        .iter()
653        .flat_map(|part| &part.relocs)
654        .filter(|reloc| onto(reloc).is_none())
655        .map(|reloc| reloc.symbol.as_str())
656        .chain(
657            input
658                .parts
659                .iter()
660                .filter_map(|part| part.group.as_ref())
661                .map(|group| group.symbol.as_str()),
662        )
663        .collect();
664
665    // Then every name. A relocation names one, and the writer wants the symbol before the
666    // relocation that points at it, so this whole pass is in front of the one below.
667    let mut symbols = std::collections::BTreeMap::new();
668    for name in &input.names {
669        let dropped = flavour == Flavour::Elf || pe32;
670        if dropped && unseen(name, pe32) && !wanted.contains(name.name.as_str()) {
671            continue;
672        }
673        let (section, value, size) = match name.at {
674            Held::In { part, offset } => {
675                let Some(id) = made.get(part) else {
676                    let why = format!(
677                        "'{}' is in section {part} and there is no such section",
678                        name.name
679                    );
680                    return Err(Error::Refused { why });
681                };
682                (SymbolSection::Section(*id), offset, name.size)
683            }
684            Held::Absolute(value) => (SymbolSection::Absolute, value, name.size),
685            // A common symbol says what it wants rather than where it is, and ELF records the
686            // boundary it wants where an ordinary symbol records its address.
687            Held::Common { size, align } => (SymbolSection::Common, align, size),
688            Held::Undefined => (SymbolSection::Undefined, 0, 0),
689        };
690        // A local number on COFF is a static symbol, which is what gas and clang write for a `.set`
691        // such as `@feat.00`. The writer underneath would make it a label otherwise.
692        let kind = match (flavour, name.at, name.sort) {
693            (Flavour::Coff, Held::Absolute(_), Sort::Untyped) => SymbolKind::Data,
694            _ => flavour.sort(name.sort, name.binding),
695        };
696        let id = obj.add_symbol(Symbol {
697            name: name.name.clone().into_bytes(),
698            value,
699            size,
700            kind,
701            scope: crate::file::scope_of(name.binding),
702            weak: name.binding == Binding::Weak,
703            section,
704            flags: SymbolFlags::None,
705        });
706        flavour.see(&mut obj, id, name.binding, name.visibility);
707        if name.sort == Sort::Ifunc {
708            // Only ELF has the type. COFF and Mach-O reach a function chosen at load time through
709            // a pointer the program fills in itself, which is a different program rather than a
710            // different symbol, so a listing asking for one there is refused rather than written
711            // as the ordinary function it is not.
712            if flavour != Flavour::Elf {
713                let why = format!("'{}' is an indirect function, which only ELF has", name.name);
714                return Err(Error::Refused { why });
715            }
716            crate::elf::indirect(&mut obj, id);
717        }
718        // The writer underneath records a common symbol as `STT_COMMON` and gas records the same
719        // symbol as `STT_OBJECT`. Both are a request for storage and a linker reads either, and the
720        // one gas writes is written here, because an object that says the same thing a different
721        // way is the kind of difference that turns up years later in a tool that only ever saw the
722        // other one. A common symbol is global by definition, so there is no binding to preserve.
723        if matches!(name.at, Held::Common { .. }) {
724            if let SymbolFlags::Elf { st_info, .. } = obj.symbol_flags_mut(id) {
725                *st_info = elf::STB_GLOBAL | elf::STT_OBJECT;
726            }
727        }
728        symbols.insert(name.name.clone(), id);
729    }
730
731    // One group for every symbol on ELF, holding each section that named it, and one for every
732    // section on COFF, where a COMDAT is a section and the symbol it is about.
733    let mut groups: Vec<(&Group, Vec<SectionId>)> = Vec::new();
734    let mut grouped: Map<&str, usize> = Map::default();
735    for (part, id) in input.parts.iter().zip(&made) {
736        let Some(group) = &part.group else { continue };
737        match grouped.get(group.symbol.as_str()) {
738            Some(&at) if flavour == Flavour::Elf => groups[at].1.push(*id),
739            _ => {
740                grouped.insert(group.symbol.as_str(), groups.len());
741                groups.push((group, vec![*id]));
742            }
743        }
744    }
745    let mut together = Vec::new();
746    for (group, sections) in groups {
747        let symbol = match symbols.get(&group.symbol) {
748            Some(&symbol) => symbol,
749            // ELF only wants the name of a group, and a group about a name the file does not
750            // define gets a local one of its own. llvm-mc puts it in the group's own section,
751            // which the writer underneath has no handle on, and the first member is as good a
752            // place, since nothing reads where it is.
753            None if flavour == Flavour::Elf => obj.add_symbol(Symbol {
754                name: group.symbol.clone().into_bytes(),
755                value: 0,
756                size: 0,
757                kind: SymbolKind::Label,
758                scope: SymbolScope::Compilation,
759                weak: false,
760                section: SymbolSection::Section(sections[0]),
761                flags: SymbolFlags::None,
762            }),
763            None => {
764                let why = format!(
765                    "section '{}' is a COMDAT about '{}', which the file does not define",
766                    obj.section(sections[0]).name().unwrap_or_default(),
767                    group.symbol
768                );
769                return Err(Error::Refused { why });
770            }
771        };
772        together.push(group.keep == Keep::Together);
773        obj.add_comdat(object::write::Comdat { kind: group.keep.kind(), symbol, sections });
774    }
775
776    for (part, id) in input.parts.iter().zip(&made) {
777        for reloc in &part.relocs {
778            let (symbol, addend) = match onto(reloc) {
779                Some((part, offset)) => {
780                    (obj.section_symbol(made[part]), reloc.addend + offset as i64)
781                }
782                None => {
783                    let Some(&symbol) = symbols.get(&reloc.symbol) else {
784                        let why = format!(
785                            "'{}' is named by a relocation and by nothing else",
786                            reloc.symbol
787                        );
788                        return Err(Error::Refused { why });
789                    };
790                    (symbol, reloc.addend)
791                }
792            };
793            let flags = flags_of(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
794                why: format!("no relocation is {:?}", reloc.kind),
795            })?;
796            let addend = match reloc.kind {
797                crate::section::Reference::Field(fixup) if carried => {
798                    let data = obj.section_mut(*id).data_mut();
799                    let Some(bytes) = data.get_mut(reloc.at..reloc.at + 4) else {
800                        let why = format!("a field at {} is past the end of its section", reloc.at);
801                        return Err(Error::Refused { why });
802                    };
803                    let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
804                    let word = crate::coff::carry(fixup, word, addend)
805                        .map_err(|why| Error::Refused { why })?;
806                    bytes.copy_from_slice(&word.to_le_bytes());
807                    0
808                }
809                _ => addend,
810            };
811            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
812            crate::file::relocate(&mut obj, *id, record)?;
813        }
814    }
815
816    // The debug information, every section before any relocation because a relocation in one of
817    // them names another as often as it names a function.
818    let mut named = Map::default();
819    for chunk in &info.chunks {
820        // An i386 file keeps each addend in the bytes of its section, which a compressed section
821        // no longer holds, so its debug sections are left as they are for now.
822        let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
823            info.compress
824        } else {
825            Compress::None
826        };
827        let id = crate::zlib::debug_section(&mut obj, chunk, how);
828        named.insert(chunk.name.as_str(), id);
829    }
830    for chunk in &info.chunks {
831        let section = named[chunk.name.as_str()];
832        for reloc in &chunk.relocs {
833            // The debug information names a function by its C name, and on i386 COFF the listing
834            // gave it the underscore every C name has there.
835            let spelled =
836                if pe32 { crate::coff::decorate(&reloc.symbol) } else { reloc.symbol.clone() };
837            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
838                Some(&id) => (obj.section_symbol(id), reloc.addend),
839                None => match defined.get(spelled.as_str()).map(|name| name.at) {
840                    Some(Held::In { part, offset }) => {
841                        (obj.section_symbol(made[part]), reloc.addend + offset as i64)
842                    }
843                    _ => match symbols.get(&spelled) {
844                        Some(&symbol) => (symbol, reloc.addend),
845                        None => {
846                            let why = format!(
847                                "'{}' is named by the debug information and is not defined here",
848                                reloc.symbol
849                            );
850                            return Err(Error::Refused { why });
851                        }
852                    },
853                },
854            };
855            let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
856            let flags = flags_of(kind, reloc.after)
857                .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
858            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
859            crate::file::relocate(&mut obj, section, record)?;
860        }
861    }
862
863    // No marker of its own. A file of assembly has the stack it says it has, which is the
864    // `.note.GNU-stack` it wrote or the one `--noexecstack` had the reader add, as with gas.
865
866    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
867    if flavour == Flavour::Elf {
868        for part in input.parts.iter().filter(|part| part.shape.merge != 0) {
869            entry_size(&mut bytes, &part.name, part.shape.merge);
870        }
871        // A table of function addresses is a run of pointers, and gas says so in its header.
872        let pointer = crate::elf::Headers::read(&bytes).entry_size().1 as u64;
873        for part in input.parts.iter().filter(|part| part.shape.array.is_some()) {
874            entry_size(&mut bytes, &part.name, pointer);
875        }
876        together_groups(&mut bytes, &together);
877        linked(&mut bytes, input, &defined)?;
878    }
879    Ok(bytes)
880}
881
882/// Write the section each `o` section goes with into its `sh_link`, which the writer underneath has
883/// no field for. See [`crate::elf::link`], which does the same for the records the compiler writes
884/// itself.
885///
886/// What the source named is a symbol, and the section is the one the symbol is in, or a section of
887/// that name when no symbol has it, which is what gas takes as well. Two sections may share a name
888/// here, so a part's header is found by how many parts of the same name come before it, which is
889/// the order the writer puts them in.
890///
891/// # Errors
892///
893/// [`Error::Refused`] for a name that is in no section of the file, which gas refuses too: a
894/// section that goes with nothing is one a linker reads as an error.
895fn linked(bytes: &mut [u8], input: &Assembled, defined: &Map<&str, &Name>) -> Result<(), Error> {
896    if input.parts.iter().all(|part| part.link.is_none()) {
897        return Ok(());
898    }
899    let headers = crate::elf::Headers::read(bytes);
900    let index = |part: usize| {
901        let name = &input.parts[part].name;
902        let nth = input.parts[..part].iter().filter(|other| other.name == *name).count();
903        headers.list.iter().enumerate().filter(|(_, header)| header.name == *name).nth(nth)
904    };
905    let mut writes = Vec::new();
906    for (at, part) in input.parts.iter().enumerate() {
907        let Some(link) = &part.link else { continue };
908        let target = match defined.get(link.as_str()).map(|name| name.at) {
909            Some(Held::In { part, .. }) => Some(part),
910            _ => input.parts.iter().position(|other| other.name == *link),
911        };
912        let Some(target) = target.and_then(index) else {
913            let why = format!("section '{}' goes with '{link}', which is in no section", part.name);
914            return Err(Error::Refused { why });
915        };
916        let (_, header) = index(at).expect("a header for every section");
917        let target = u32::try_from(target.0).expect("a file with this many sections in it");
918        writes.push((header.at + headers.link(), target));
919    }
920    for (at, target) in writes {
921        bytes[at..at + 4].copy_from_slice(&target.to_le_bytes());
922    }
923    Ok(())
924}
925
926/// Write how long an entry of a mergeable section is into its header, which the linker needs and
927/// the writer underneath has no field for. It writes one only for a section of strings it made
928/// itself. The section is found by its name, which is unique because the assembler gave every name
929/// one section.
930fn entry_size(bytes: &mut [u8], name: &str, size: u64) {
931    let headers = crate::elf::Headers::read(bytes);
932    let (field, width) = headers.entry_size();
933    for header in headers.list.iter().filter(|header| header.name == name) {
934        let at = header.at + field;
935        bytes[at..at + width].copy_from_slice(&size.to_le_bytes()[..width]);
936    }
937}
938
939/// Clear the word that makes a group a COMDAT in each group of an ELF file that is not one, which
940/// the writer underneath writes into every group it makes. `together` holds a flag for each group
941/// in the order they were added, which is the order the writer puts their headers in.
942fn together_groups(bytes: &mut [u8], together: &[bool]) {
943    if !together.contains(&true) {
944        return;
945    }
946    let headers = crate::elf::Headers::read(bytes);
947    let groups = headers.list.iter().filter(|header| header.sh_type == elf::SHT_GROUP.0);
948    for (header, _) in groups.zip(together).filter(|(_, together)| **together) {
949        bytes[header.offset..header.offset + 4].copy_from_slice(&0u32.to_le_bytes());
950    }
951}
952
953/// The section and the offset into it a relocation is written against in place of the name it
954/// gave, when gas would do the same.
955///
956/// A name only this file can see is a place in a section and nothing more, so gas writes the
957/// section's own symbol and how far into it the place is, and a `.L` label then has no reason to be
958/// in the table at all. It keeps the name where the linker has to see it: a call, which may go
959/// through a stub the linker makes for that name, a slot of the global offset table, and a place in
960/// a section the linker may merge, where the offset into the section is not an offset into the
961/// merged one. The last of those is only a problem for a distance, or for an address with
962/// something added to it, since the address of the start of a string is what the linker follows.
963///
964/// COFF for i386, `pe32`, does the same for a temporary name, whatever refers to it, since it has
965/// no stubs or tables of that kind to go through. gas goes further there and writes the section for
966/// every name the file defines, `_main` included. Naming the symbol instead is what the other COFF
967/// machines here do, and the linker lands on the same address either way.
968fn moved(
969    flavour: Flavour,
970    pe32: bool,
971    input: &Assembled,
972    defined: &Map<&str, &Name>,
973    reloc: &Reloc,
974) -> Option<(usize, u64)> {
975    use crate::section::Reference;
976    let name = defined.get(reloc.symbol.as_str())?;
977    let Held::In { part, offset } = name.at else { return None };
978    // A reference to an indirect function is to whatever its resolver picks, and the place the
979    // name marks is the resolver. The section and an offset would be the resolver itself, so the
980    // name stays, which is what gas leaves for one too.
981    if name.sort == Sort::Ifunc {
982        return None;
983    }
984    if pe32 && unseen(name, pe32) {
985        return Some((part, offset));
986    }
987    if flavour != Flavour::Elf || name.binding != Binding::Local {
988        return None;
989    }
990    let near = matches!(
991        reloc.kind,
992        Reference::Data
993            | Reference::Away
994            | Reference::AwayWide
995            | Reference::Short
996            | Reference::Tiny
997    );
998    let fixed = match reloc.kind {
999        Reference::Call
1000        | Reference::Got
1001        | Reference::GotBare
1002        | Reference::GotKept
1003        | Reference::Slot
1004        | Reference::SlotKept
1005        | Reference::GotFront
1006        | Reference::Thread
1007        | Reference::Tls(_) => false,
1008        // The same for a field of an instruction that goes through a stub or a table slot, or that
1009        // says where a thread-local variable is, which a linker checks against the name's type.
1010        Reference::Field(
1011            Fixup::Call26
1012            | Fixup::Jump26
1013            | Fixup::GotPage21
1014            | Fixup::GotLo12
1015            | Fixup::GotTprelPage21
1016            | Fixup::GotTprelLo12Nc
1017            | Fixup::TprelHi12
1018            | Fixup::TprelLo12Nc,
1019        ) => false,
1020        _ if input.parts.get(part)?.shape.merge != 0 => !near && reloc.addend == 0,
1021        _ => true,
1022    };
1023    fixed.then_some((part, offset))
1024}
1025
1026/// Whether a name is one the assembler made up or a label only it sees, which gas leaves out of the
1027/// table unless a relocation still names it. `.L` is the prefix for those that ELF assemblers agree
1028/// on, and a name with a `\u{1}` in it is one this assembler made for a numbered label or a frame.
1029///
1030/// COFF for i386, `pe32`, adds a bare `L`, which is what gcc and clang for that target start their
1031/// own labels with, `L3` for a block and `LC0` for a string. No C name can start that way there,
1032/// since every one of them has an underscore in front, and gas for `pe-i386` leaves them out too.
1033fn unseen(name: &Name, pe32: bool) -> bool {
1034    name.binding == Binding::Local
1035        && (name.name.starts_with(".L")
1036            || name.name.starts_with("..")
1037            || name.name.contains('\u{1}')
1038            || (pe32 && name.name.starts_with('L')))
1039}
1040
1041/// Every name in it a linker can find, which is what an archive's symbol index is built from.
1042///
1043/// The same rule as [`crate::defines`]: a local is left out, because a name the static link has
1044/// already finished with is not one an archive may offer, and an undefined one is left out because
1045/// this file does not have it.
1046#[must_use]
1047pub fn assembled_defines(input: &Assembled) -> Vec<String> {
1048    input
1049        .names
1050        .iter()
1051        .filter(|name| name.binding != Binding::Local && name.at != Held::Undefined)
1052        .map(|name| name.name.clone())
1053        .collect()
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058    use super::*;
1059
1060    use object::read::elf::{FileHeader as _, SectionHeader as _, Sym as _};
1061    use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1062    use object::{RelocationFlags, SectionFlags};
1063    use rucc_target::{Arch as TargetArch, Env, Os, Triple};
1064
1065    use crate::section::{Reference, Tls};
1066
1067    /// A linux x86-64 target, which is the one most of these are written against.
1068    fn target() -> TargetInfo {
1069        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Linux, Env::Gnu))
1070    }
1071
1072    /// The same machine under mingw-w64, which is the target the COFF cases below are about.
1073    fn windows() -> TargetInfo {
1074        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Windows, Env::Gnu))
1075    }
1076
1077    /// One section of that name holding those bytes, with the flags the name implies.
1078    fn part(name: &str, bytes: Vec<u8>) -> Part {
1079        Part {
1080            name: name.to_owned(),
1081            size: bytes.len() as u64,
1082            bytes,
1083            align: 1,
1084            shape: Shape::of(name),
1085            relocs: Vec::new(),
1086            group: None,
1087            link: None,
1088        }
1089    }
1090
1091    /// One name at an offset into the first section.
1092    fn at(name: &str, offset: u64, sort: Sort, binding: Binding) -> Name {
1093        Name {
1094            name: name.to_owned(),
1095            at: Held::In { part: 0, offset },
1096            size: 0,
1097            sort,
1098            binding,
1099            visibility: Visibility::Default,
1100        }
1101    }
1102
1103    /// The raw `st_info` and `st_value` of a symbol, as the file holds them.
1104    ///
1105    /// The reader's own `kind()`, `is_global()` and `address()` are a translation of these, and a
1106    /// translation is what several of the cases below are about, so they ask the file rather than
1107    /// the reading. A common symbol is the clearest of them: `address()` gives zero for one because
1108    /// it has no address, and the field an ordinary symbol keeps its address in is where a common
1109    /// one states the boundary it has to start on.
1110    fn raw(bytes: &[u8], want: &str) -> (u8, u64) {
1111        let header = elf::FileHeader64::<Endianness>::parse(bytes).expect("a header");
1112        let endian = header.endian().expect("an endianness");
1113        let table = header.sections(endian, bytes).expect("the sections");
1114        let symbols = table.symbols(endian, bytes, elf::SHT_SYMTAB).expect("a symbol table");
1115        for symbol in symbols.iter() {
1116            if symbols.symbol_name(endian, symbol).expect("a name") == want.as_bytes() {
1117                return (symbol.st_info().0, symbol.st_value(endian));
1118            }
1119        }
1120        panic!("there is no symbol called '{want}'");
1121    }
1122
1123    /// The first half of that.
1124    fn st_info(bytes: &[u8], want: &str) -> u8 {
1125        raw(bytes, want).0
1126    }
1127
1128    #[test]
1129    fn a_section_carries_the_flags_the_source_said_and_not_the_ones_its_name_suggests() {
1130        // The whole reason a shape is separate facts rather than a kind. A program may write
1131        // `.section .init.text,"ax"` and mean a section with a name this compiler has never heard
1132        // of, and what it said about it is the letters.
1133        let mut odd = part(".init.text", vec![0x90]);
1134        odd.shape = Shape { alloc: true, exec: true, bits: true, ..Shape::default() };
1135        let input = Assembled { parts: vec![odd], names: Vec::new(), subsections: false };
1136        let bytes = assembled(&input, &target()).expect("an object");
1137        let file = object::File::parse(&bytes[..]).expect("a readable object");
1138        let section = file.section_by_name(".init.text").expect("the section");
1139        assert_eq!(section.data().expect("the bytes"), &[0x90]);
1140        let SectionFlags::Elf { sh_flags, sh_type } = section.flags() else {
1141            panic!("this is an ELF file");
1142        };
1143        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_EXECINSTR.0);
1144        assert_eq!(sh_flags.0 & elf::SHF_WRITE.0, 0, "nothing said it was writable");
1145        assert_eq!(sh_type, elf::SHT_PROGBITS);
1146    }
1147
1148    #[test]
1149    fn a_section_that_holds_no_bytes_still_says_how_long_it_is() {
1150        // `.bss` is a length and no bytes, and a writer that appended its data would produce a file
1151        // with that much zero in it, which is the difference between an object and a big object.
1152        let mut room = part(".bss", Vec::new());
1153        room.size = 4096;
1154        room.align = 16;
1155        let input = Assembled { parts: vec![room], names: Vec::new(), subsections: false };
1156        let bytes = assembled(&input, &target()).expect("an object");
1157        assert!(bytes.len() < 4096, "the empty space was written out: {} bytes", bytes.len());
1158        let file = object::File::parse(&bytes[..]).expect("a readable object");
1159        let section = file.section_by_name(".bss").expect("the section");
1160        assert_eq!(section.size(), 4096);
1161        assert_eq!(section.align(), 16);
1162        let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("an ELF file") };
1163        assert_eq!(sh_type, elf::SHT_NOBITS);
1164    }
1165
1166    #[test]
1167    fn a_label_nobody_stated_a_type_for_is_a_symbol_with_no_type() {
1168        // `STT_NOTYPE` is what gas writes for one, and it is a real answer rather than a missing
1169        // one. The writer underneath refuses a defined symbol whose kind is `Unknown` outright, so
1170        // this is also the case that says the mapping went to `Label` and not there.
1171        let input = Assembled {
1172            parts: vec![part(".text", vec![0; 8])],
1173            names: vec![at("plain", 4, Sort::Untyped, Binding::Global)],
1174            subsections: false,
1175        };
1176        let bytes = assembled(&input, &target()).expect("an object");
1177        let file = object::File::parse(&bytes[..]).expect("a readable object");
1178        let plain = file.symbols().find(|s| s.name() == Ok("plain")).expect("the label");
1179        assert_eq!(plain.address(), 4);
1180        assert_eq!(st_info(&bytes, "plain") & 0xf, elf::STT_NOTYPE.0);
1181    }
1182
1183    #[test]
1184    fn what_type_said_is_what_the_symbol_gets() {
1185        let input = Assembled {
1186            parts: vec![part(".text", vec![0; 8])],
1187            names: vec![
1188                at("run", 0, Sort::Func, Binding::Global),
1189                at("held", 4, Sort::Object, Binding::Local),
1190            ],
1191            subsections: false,
1192        };
1193        let bytes = assembled(&input, &target()).expect("an object");
1194        assert_eq!(st_info(&bytes, "run") & 0xf, elf::STT_FUNC.0);
1195        assert_eq!(st_info(&bytes, "held") & 0xf, elf::STT_OBJECT.0);
1196        assert_eq!(st_info(&bytes, "run") >> 4, elf::STB_GLOBAL.0);
1197        assert_eq!(st_info(&bytes, "held") >> 4, elf::STB_LOCAL.0);
1198    }
1199
1200    #[test]
1201    fn a_common_symbol_is_written_the_way_gas_writes_one() {
1202        // The writer underneath records `STT_COMMON` and gas records `STT_OBJECT` for the same
1203        // `.comm`. Both are a request for storage and a linker takes either, and the one gas writes
1204        // is the one written here, so an object of ours and an object of theirs do not differ in a
1205        // field somebody's tool reads years from now.
1206        let input = Assembled {
1207            parts: Vec::new(),
1208            names: vec![Name {
1209                name: "shared".to_owned(),
1210                at: Held::Common { size: 8, align: 8 },
1211                size: 0,
1212                sort: Sort::Object,
1213                binding: Binding::Global,
1214                visibility: Visibility::Default,
1215            }],
1216            subsections: false,
1217        };
1218        let bytes = assembled(&input, &target()).expect("an object");
1219        assert_eq!(st_info(&bytes, "shared"), elf::STB_GLOBAL.0 << 4 | elf::STT_OBJECT.0);
1220        let file = object::File::parse(&bytes[..]).expect("a readable object");
1221        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the symbol");
1222        assert!(shared.is_common(), "the linker has to be asked for the space");
1223        assert_eq!(shared.size(), 8);
1224        // Where an ordinary symbol keeps its address, which is why the two cannot both be said.
1225        assert_eq!(raw(&bytes, "shared").1, 8, "the boundary it has to start on");
1226    }
1227
1228    #[test]
1229    fn a_set_is_a_number_rather_than_a_place() {
1230        let input = Assembled {
1231            parts: vec![part(".text", vec![0; 8])],
1232            names: vec![Name {
1233                name: "size_of_it".to_owned(),
1234                at: Held::Absolute(25),
1235                size: 0,
1236                sort: Sort::Untyped,
1237                binding: Binding::Global,
1238                visibility: Visibility::Default,
1239            }],
1240            subsections: false,
1241        };
1242        let bytes = assembled(&input, &target()).expect("an object");
1243        let file = object::File::parse(&bytes[..]).expect("a readable object");
1244        let sym = file.symbols().find(|s| s.name() == Ok("size_of_it")).expect("the symbol");
1245        assert_eq!(sym.address(), 25);
1246        assert_eq!(sym.section(), object::SymbolSection::Absolute, "it is not in any section");
1247    }
1248
1249    /// `@feat.00` as the i386 COFF listing sets it, which has to come out static the way clang
1250    /// writes it rather than as a label.
1251    #[test]
1252    fn a_local_set_on_coff_is_a_static_number() {
1253        let input = Assembled {
1254            parts: vec![part(".text", vec![0xc3])],
1255            names: vec![Name {
1256                name: "@feat.00".to_owned(),
1257                at: Held::Absolute(1),
1258                size: 0,
1259                sort: Sort::Untyped,
1260                binding: Binding::Local,
1261                visibility: Visibility::Default,
1262            }],
1263            subsections: false,
1264        };
1265        let bytes = assembled(&input, &windows()).expect("an object");
1266        let file = object::File::parse(&bytes[..]).expect("a readable object");
1267        let sym = file.symbols().find(|s| s.name() == Ok("@feat.00")).expect("the symbol");
1268        // The reader gives an absolute COFF symbol no address, so the value is read as written.
1269        let coff = object::read::coff::CoffFile::<&[u8]>::parse(&bytes[..]).expect("COFF");
1270        let raw = coff.symbol_by_name("@feat.00").expect("the symbol");
1271        assert_eq!(object::read::coff::Symbol::value(raw.coff_symbol()), 1);
1272        assert_eq!(sym.section(), object::SymbolSection::Absolute);
1273        assert!(sym.is_local());
1274        assert!(
1275            matches!(
1276                sym.flags(),
1277                SymbolFlags::Coff { storage_class: pe::IMAGE_SYM_CLASS_STATIC, .. }
1278            ),
1279            "{:?}",
1280            sym.flags()
1281        );
1282    }
1283
1284    #[test]
1285    fn a_relocation_names_a_symbol_and_lands_where_the_bytes_are() {
1286        let mut data = part(".data", vec![0; 8]);
1287        data.relocs.push(Reloc {
1288            at: 0,
1289            symbol: "message".to_owned(),
1290            kind: Reference::Address { bytes: 8 },
1291            addend: 0,
1292            after: 0,
1293        });
1294        let input = Assembled {
1295            parts: vec![data],
1296            names: vec![Name {
1297                name: "message".to_owned(),
1298                at: Held::Undefined,
1299                size: 0,
1300                sort: Sort::Untyped,
1301                binding: Binding::Global,
1302                visibility: Visibility::Default,
1303            }],
1304            subsections: false,
1305        };
1306        let bytes = assembled(&input, &target()).expect("an object");
1307        let file = object::File::parse(&bytes[..]).expect("a readable object");
1308        let section = file.section_by_name(".data").expect("the section");
1309        let (at, reloc) = section.relocations().next().expect("one relocation");
1310        assert_eq!(at, 0);
1311        assert_eq!(reloc.addend(), 0);
1312        let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("an ELF file") };
1313        assert_eq!(r_type, elf::R_X86_64_64);
1314    }
1315
1316    #[test]
1317    fn a_place_only_this_file_sees_is_reached_through_its_section_as_gas_does() {
1318        // The `.L` label goes, the static function stays in the table, and both relocations are
1319        // against `.text` at their offsets. A call keeps its name, since the linker may give it a
1320        // stub, and so does a name the linker is allowed to see.
1321        let mut text = part(".text", vec![0; 32]);
1322        for (at, symbol, kind) in [
1323            (0, ".L3", Reference::Data),
1324            (4, "helper", Reference::Data),
1325            (8, "helper", Reference::Call),
1326            (12, "shared", Reference::Data),
1327        ] {
1328            let symbol = symbol.to_owned();
1329            text.relocs.push(Reloc { at, symbol, kind, addend: -4, after: 0 });
1330        }
1331        let input = Assembled {
1332            parts: vec![text],
1333            names: vec![
1334                at(".L3", 20, Sort::Untyped, Binding::Local),
1335                at("helper", 24, Sort::Func, Binding::Local),
1336                at("shared", 28, Sort::Func, Binding::Global),
1337            ],
1338            subsections: false,
1339        };
1340        let bytes = assembled(&input, &target()).expect("an object");
1341        let file = object::File::parse(&bytes[..]).expect("a readable object");
1342        let names: Vec<_> = file.symbols().filter_map(|sym| sym.name().ok()).collect();
1343        assert!(!names.contains(&".L3") && names.contains(&"helper"), "{names:?}");
1344        let section = file.section_by_name(".text").expect("the section");
1345        let reached: Vec<_> = section
1346            .relocations()
1347            .map(|(at, reloc)| {
1348                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1349                    panic!("a symbol")
1350                };
1351                let symbol = file.symbol_by_index(index).expect("the symbol");
1352                let name = if symbol.kind() == SymbolKind::Section {
1353                    ".text"
1354                } else {
1355                    symbol.name().expect("a name")
1356                };
1357                (at, name, reloc.addend())
1358            })
1359            .collect();
1360        assert_eq!(
1361            reached,
1362            [(0, ".text", 16), (4, ".text", 20), (8, "helper", -4), (12, "shared", -4)]
1363        );
1364    }
1365
1366    #[test]
1367    fn a_section_of_constants_may_be_merged_and_a_distance_into_it_keeps_its_name() {
1368        let mut text = part(".text", vec![0; 8]);
1369        text.relocs.push(Reloc {
1370            at: 0,
1371            symbol: ".LC0".to_owned(),
1372            kind: Reference::Data,
1373            addend: -4,
1374            after: 0,
1375        });
1376        let strings = Part {
1377            shape: Shape { merge: 1, strings: true, ..Shape::of(".rodata") },
1378            ..part(".rodata.str1.1", b"hi\0".to_vec())
1379        };
1380        let mut name = at(".LC0", 0, Sort::Untyped, Binding::Local);
1381        name.at = Held::In { part: 1, offset: 0 };
1382        let input = Assembled { parts: vec![text, strings], names: vec![name], subsections: false };
1383        let bytes = assembled(&input, &target()).expect("an object");
1384        let file = object::File::parse(&bytes[..]).expect("a readable object");
1385        let section = file.section_by_name(".rodata.str1.1").expect("the section");
1386        let SectionFlags::Elf { sh_flags, .. } = section.flags() else { panic!("an ELF file") };
1387        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_MERGE.0 | elf::SHF_STRINGS.0);
1388        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
1389        let endian = header.endian().expect("an endianness");
1390        let table = header.sections(endian, &bytes[..]).expect("the sections");
1391        let (_, found) = table.section_by_name(endian, b".rodata.str1.1").expect("the section");
1392        assert_eq!(found.sh_entsize.get(endian), 1);
1393        let text = file.section_by_name(".text").expect("the section");
1394        let (_, reloc) = text.relocations().next().expect("one relocation");
1395        let object::RelocationTarget::Symbol(index) = reloc.target() else { panic!("a symbol") };
1396        assert_eq!(file.symbol_by_index(index).and_then(|sym| sym.name()), Ok(".LC0"));
1397    }
1398
1399    #[test]
1400    fn a_relocation_against_a_name_the_file_never_mentions_is_refused() {
1401        // Rather than written against symbol zero, which is a file that links and resolves the
1402        // reference to address zero. The list of names is the whole of what the reader found, so a
1403        // relocation naming something outside it is a mistake in this compiler.
1404        let mut data = part(".data", vec![0; 8]);
1405        data.relocs.push(Reloc {
1406            at: 0,
1407            symbol: "nowhere".to_owned(),
1408            kind: Reference::Address { bytes: 8 },
1409            addend: 0,
1410            after: 0,
1411        });
1412        let input = Assembled { parts: vec![data], names: Vec::new(), subsections: false };
1413        let why = assembled(&input, &target()).expect_err("this cannot be written");
1414        assert!(format!("{why}").contains("nowhere"), "{why}");
1415    }
1416
1417    #[test]
1418    fn the_stack_is_marked_only_by_the_file_and_only_once() {
1419        // gas adds no marker the file did not write unless `--noexecstack` asks, and that is the
1420        // reader's to do, so a file of parts gets exactly the ones it has.
1421        let bare = Assembled {
1422            parts: vec![part(".text", vec![0x90])],
1423            names: Vec::new(),
1424            subsections: false,
1425        };
1426        let bytes = assembled(&bare, &target()).expect("an object");
1427        let file = object::File::parse(&bytes[..]).expect("a readable object");
1428        assert!(file.section_by_name(".note.GNU-stack").is_none(), "a marker nobody wrote");
1429
1430        let said = Assembled {
1431            parts: vec![part(".text", vec![0x90]), part(".note.GNU-stack", Vec::new())],
1432            names: Vec::new(),
1433            subsections: false,
1434        };
1435        let bytes = assembled(&said, &target()).expect("an object");
1436        let file = object::File::parse(&bytes[..]).expect("a readable object");
1437        let marks = file.sections().filter(|s| s.name() == Ok(".note.GNU-stack")).count();
1438        assert_eq!(marks, 1, "the file said it and it was said again");
1439    }
1440
1441    #[test]
1442    fn only_the_names_a_linker_could_find_are_offered_to_an_archive() {
1443        let input = Assembled {
1444            parts: vec![part(".text", vec![0; 8])],
1445            names: vec![
1446                at("reachable", 0, Sort::Func, Binding::Global),
1447                at("mine", 4, Sort::Func, Binding::Local),
1448                Name {
1449                    name: "elsewhere".to_owned(),
1450                    at: Held::Undefined,
1451                    size: 0,
1452                    sort: Sort::Untyped,
1453                    binding: Binding::Global,
1454                    visibility: Visibility::Default,
1455                },
1456            ],
1457            subsections: false,
1458        };
1459        assert_eq!(assembled_defines(&input), vec!["reachable".to_owned()]);
1460    }
1461
1462    #[test]
1463    fn a_machine_this_does_not_write_is_refused_rather_than_written_wrong() {
1464        let input = Assembled {
1465            parts: vec![part(".text", vec![0x90])],
1466            names: Vec::new(),
1467            subsections: false,
1468        };
1469        let elsewhere = TargetInfo::new(Triple::new(TargetArch::Riscv64, Os::Linux, Env::Gnu));
1470        let why = assembled(&input, &elsewhere).expect_err("this cannot be written");
1471        assert!(format!("{why}").contains("riscv64"), "{why}");
1472    }
1473
1474    #[test]
1475    fn a_file_of_assembly_for_aarch64_is_written_with_that_machine_s_relocations() {
1476        // `adrp x0, table` and `add x0, x0, :lo12:table+8`, then `bl g`, then the address of
1477        // `table` in a table of its own. A field is its fixup's relocation and an address is the
1478        // AArch64 one of that width, and a label only this file sees is written against its
1479        // section, the way gas writes it.
1480        let mut text = part(".text", vec![0; 12]);
1481        let field = |at, symbol: &str, fixup, addend| Reloc {
1482            at,
1483            symbol: symbol.to_owned(),
1484            kind: Reference::Field(fixup),
1485            addend,
1486            after: 0,
1487        };
1488        text.relocs = vec![
1489            field(0, ".Ltable", Fixup::AdrPage21, 8),
1490            field(4, ".Ltable", Fixup::AddLo12, 8),
1491            field(8, "g", Fixup::Call26, 0),
1492        ];
1493        let mut data = part(".data", vec![0; 16]);
1494        data.relocs = vec![Reloc {
1495            at: 8,
1496            symbol: ".Ltable".to_owned(),
1497            kind: Reference::Address { bytes: 8 },
1498            addend: 0,
1499            after: 0,
1500        }];
1501        let mut table = at(".Ltable", 0, Sort::Object, Binding::Local);
1502        table.at = Held::In { part: 1, offset: 0 };
1503        let input = Assembled {
1504            parts: vec![text, data],
1505            names: vec![
1506                table,
1507                Name { at: Held::Undefined, ..at("g", 0, Sort::Untyped, Binding::Global) },
1508            ],
1509            subsections: false,
1510        };
1511        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Linux, Env::Gnu));
1512        let bytes = assembled(&input, &target).expect("an object");
1513        let file = object::File::parse(&bytes[..]).expect("a readable object");
1514        assert_eq!(file.architecture(), Architecture::Aarch64);
1515        let relocs = |name: &str| -> Vec<(u64, elf::RelocationType, i64)> {
1516            let section = file.section_by_name(name).expect("the section");
1517            section
1518                .relocations()
1519                .map(|(at, reloc)| {
1520                    let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1521                    (at, r_type, reloc.addend())
1522                })
1523                .collect()
1524        };
1525        assert_eq!(
1526            relocs(".text"),
1527            [
1528                (0, elf::R_AARCH64_ADR_PREL_PG_HI21, 8),
1529                (4, elf::R_AARCH64_ADD_ABS_LO12_NC, 8),
1530                (8, elf::R_AARCH64_CALL26, 0)
1531            ]
1532        );
1533        assert_eq!(relocs(".data"), [(8, elf::R_AARCH64_ABS64, 0)]);
1534        assert!(file.symbols().all(|s| s.name() != Ok(".Ltable")), "a label only this file sees");
1535    }
1536
1537    #[test]
1538    fn a_file_of_assembly_for_aarch64_windows_carries_its_addends_in_the_instructions() {
1539        // The same instructions as the ELF test above, and a load of the eighth byte of the table,
1540        // a `bl` to a name, then the two halves of an offset into `.tls`. COFF has no addend field,
1541        // so the eight goes into the page `adrp` names, into the low twelve bits `add` carries and,
1542        // divided by the size of the access, into the offset of the load. A distance written as
1543        // data is `REL32`, which counts from the end of its four bytes, so four more is in them.
1544        let adrp = 0x9000_0000u32;
1545        let add = 0x9100_0000u32;
1546        let ldr = 0xf940_0000u32;
1547        let bl = 0x9400_0000u32;
1548        let words = [adrp, add, ldr, bl, add | 1 << 22, add];
1549        let mut text = part(".text", words.iter().flat_map(|word| word.to_le_bytes()).collect());
1550        let field = |at, symbol: &str, fixup, addend| Reloc {
1551            at,
1552            symbol: symbol.to_owned(),
1553            kind: Reference::Field(fixup),
1554            addend,
1555            after: 0,
1556        };
1557        text.relocs = vec![
1558            field(0, "table", Fixup::AdrPage21, 8),
1559            field(4, "table", Fixup::AddLo12, 8),
1560            field(8, "table", Fixup::Ldst64Lo12, 8),
1561            field(12, "g", Fixup::Call26, 0),
1562            field(16, "counter", Fixup::SecrelHigh12A, 0),
1563            field(20, "counter", Fixup::SecrelLow12A, 0),
1564        ];
1565        let mut data = part(".data", vec![0; 16]);
1566        data.relocs = vec![
1567            Reloc {
1568                at: 0,
1569                symbol: "table".to_owned(),
1570                kind: Reference::Address { bytes: 8 },
1571                addend: 8,
1572                after: 0,
1573            },
1574            Reloc { at: 8, symbol: "g".to_owned(), kind: Reference::Data, addend: 0, after: 0 },
1575            Reloc {
1576                at: 12,
1577                symbol: "table".to_owned(),
1578                kind: Reference::Image,
1579                addend: 0,
1580                after: 0,
1581            },
1582        ];
1583        let mut table = at("table", 0, Sort::Object, Binding::Global);
1584        table.at = Held::In { part: 1, offset: 0 };
1585        let undefined =
1586            |name| Name { at: Held::Undefined, ..at(name, 0, Sort::Untyped, Binding::Global) };
1587        let input = Assembled {
1588            parts: vec![text, data],
1589            names: vec![table, undefined("g"), undefined("counter")],
1590            subsections: false,
1591        };
1592        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1593        let bytes = assembled(&input, &target).expect("an object");
1594        let file = object::File::parse(&bytes[..]).expect("a readable object");
1595        assert_eq!(file.format(), object::BinaryFormat::Coff);
1596        assert_eq!(file.architecture(), Architecture::Aarch64);
1597        let relocs = |name: &str| -> Vec<(u64, u16, String)> {
1598            let section = file.section_by_name(name).expect("the section");
1599            section
1600                .relocations()
1601                .map(|(at, reloc)| {
1602                    let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
1603                    let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
1604                        panic!("a symbol")
1605                    };
1606                    let symbol = file.symbol_by_index(symbol).expect("the symbol");
1607                    (at, typ.0, symbol.name().expect("a name").to_owned())
1608                })
1609                .collect()
1610        };
1611        let named = |at, typ: pe::RelocationType, name: &str| (at, typ.0, name.to_owned());
1612        assert_eq!(
1613            relocs(".text"),
1614            [
1615                named(0, pe::IMAGE_REL_ARM64_PAGEBASE_REL21, "table"),
1616                named(4, pe::IMAGE_REL_ARM64_PAGEOFFSET_12A, "table"),
1617                named(8, pe::IMAGE_REL_ARM64_PAGEOFFSET_12L, "table"),
1618                named(12, pe::IMAGE_REL_ARM64_BRANCH26, "g"),
1619                named(16, pe::IMAGE_REL_ARM64_SECREL_HIGH12A, "counter"),
1620                named(20, pe::IMAGE_REL_ARM64_SECREL_LOW12A, "counter"),
1621            ]
1622        );
1623        assert_eq!(
1624            relocs(".data"),
1625            [
1626                named(0, pe::IMAGE_REL_ARM64_ADDR64, "table"),
1627                named(8, pe::IMAGE_REL_ARM64_REL32, "g"),
1628                named(12, pe::IMAGE_REL_ARM64_ADDR32NB, "table"),
1629            ]
1630        );
1631        let text = file.section_by_name(".text").expect("the section");
1632        let text = text.data().expect("the bytes");
1633        let word = |nth: usize| u32::from_le_bytes(text[nth * 4..nth * 4 + 4].try_into().unwrap());
1634        assert_eq!(word(0), adrp | 2 << 5, "eight bytes is immhi two and immlo nothing");
1635        assert_eq!(word(1), add | 8 << 10);
1636        assert_eq!(word(2), ldr | 1 << 10, "eight bytes is one doubleword");
1637        assert_eq!([word(3), word(4), word(5)], [bl, add | 1 << 22, add]);
1638        let data = file.section_by_name(".data").expect("the section");
1639        let data = data.data().expect("the bytes");
1640        assert_eq!(data[..8], 8u64.to_le_bytes());
1641        assert_eq!(data[8..12], 4u32.to_le_bytes());
1642    }
1643
1644    #[test]
1645    fn an_addend_an_aarch64_coff_field_cannot_carry_is_refused() {
1646        // A load of four bytes cannot be told to start two bytes in, since its offset is counted in
1647        // fours, and a branch to a name and a number is not something the field can say for every
1648        // linker. Both are refused rather than written as something close.
1649        for (word, fixup, addend) in
1650            [(0xb940_0000u32, Fixup::Ldst32Lo12, 2), (0x9400_0000, Fixup::Call26, 4)]
1651        {
1652            let mut text = part(".text", word.to_le_bytes().to_vec());
1653            text.relocs = vec![Reloc {
1654                at: 0,
1655                symbol: "g".to_owned(),
1656                kind: Reference::Field(fixup),
1657                addend,
1658                after: 0,
1659            }];
1660            let input = Assembled {
1661                parts: vec![text],
1662                names: vec![Name {
1663                    at: Held::Undefined,
1664                    ..at("g", 0, Sort::Untyped, Binding::Global)
1665                }],
1666                subsections: false,
1667            };
1668            let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1669            let why = assembled(&input, &target).expect_err("a field that cannot say it");
1670            assert!(format!("{why}").contains(fixup.name()), "{why}");
1671        }
1672    }
1673
1674    #[test]
1675    fn a_file_of_assembly_for_windows_is_written_as_coff() {
1676        // What tamnd/rucc#1514 was about. `runtime/builtins/chkstk.S` is a file of assembly for a
1677        // Windows target, and until this it was refused with a message about there being no object
1678        // writer for the triple, which read as the whole back end being missing rather than this
1679        // one path through it.
1680        let input = Assembled {
1681            parts: vec![part(".text", vec![0xc3])],
1682            names: Vec::new(),
1683            subsections: false,
1684        };
1685        let bytes = assembled(&input, &windows()).expect("an object");
1686        let file = object::File::parse(&bytes[..]).expect("a readable object");
1687        assert_eq!(file.format(), object::BinaryFormat::Coff);
1688        let section = file.section_by_name(".text").expect("the section");
1689        assert_eq!(section.data().expect("the bytes"), &[0xc3]);
1690        assert_eq!(section.kind(), SectionKind::Text);
1691        assert!(
1692            file.section_by_name(".note.GNU-stack").is_none(),
1693            "a format with no marker got one anyway"
1694        );
1695    }
1696
1697    #[test]
1698    fn a_coff_section_keeps_the_letters_it_was_given_and_its_comdat() {
1699        // `.section .drectve,"yni"` in chkstk.S, and the `.refptr.` pointers mingw-w64 wants one
1700        // copy of across every object that has one.
1701        let drectve = Part {
1702            shape: Shape::coff("yni").expect("the letters"),
1703            ..part(".drectve", b" -exclude-symbols:f".to_vec())
1704        };
1705        let refptr = Part {
1706            shape: Shape::coff("dr").expect("the letters"),
1707            group: Some(Group { symbol: ".refptr.x".to_owned(), keep: Keep::Any }),
1708            ..part(".rdata$.refptr.x", vec![0; 8])
1709        };
1710        let input = Assembled {
1711            parts: vec![refptr, drectve],
1712            names: vec![at(".refptr.x", 0, Sort::Object, Binding::Global)],
1713            subsections: false,
1714        };
1715        let bytes = assembled(&input, &windows()).expect("an object");
1716        let file = object::File::parse(&bytes[..]).expect("a readable object");
1717        let flags = |name: &str| match file.section_by_name(name).expect("the section").flags() {
1718            SectionFlags::Coff { characteristics } => characteristics.0,
1719            other => panic!("{other:?}"),
1720        };
1721        let removed = pe::IMAGE_SCN_LNK_REMOVE.0 | pe::IMAGE_SCN_LNK_INFO.0;
1722        assert_eq!(flags(".drectve") & removed, removed);
1723        assert_eq!(flags(".drectve") & (pe::IMAGE_SCN_MEM_READ.0 | pe::IMAGE_SCN_MEM_WRITE.0), 0);
1724        let refptr = flags(".rdata$.refptr.x");
1725        assert_ne!(refptr & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "not a COMDAT");
1726        assert_eq!(refptr & pe::IMAGE_SCN_MEM_WRITE.0, 0, "`r` did not take writing away");
1727        let comdat = file.comdats().next().expect("the COMDAT");
1728        assert_eq!(comdat.kind(), object::ComdatKind::Any);
1729        assert_eq!(file.symbol_by_index(comdat.symbol()).unwrap().name(), Ok(".refptr.x"));
1730
1731        // And one about a name the file never defines is refused rather than written broken.
1732        let input = Assembled { names: Vec::new(), ..input };
1733        assert!(assembled(&input, &windows()).is_err());
1734    }
1735
1736    #[test]
1737    fn a_global_label_with_no_type_under_it_is_still_offered_on_coff() {
1738        // The case a `.globl` and a label is, which is most of what a hand written file says. On
1739        // ELF that is `STT_NOTYPE` and the binding is a separate field, so the name is global
1740        // whatever its type. COFF has no such split: what the writer underneath calls a label is
1741        // storage class `LABEL`, which is a name inside one file, and a symbol written that way is
1742        // one no linker resolves against. `___chkstk_ms` came out of the archive as a local under
1743        // that mapping and mingw-w64's own objects went on wanting it.
1744        let input = Assembled {
1745            parts: vec![part(".text", vec![0; 8])],
1746            names: vec![
1747                at("offered", 0, Sort::Untyped, Binding::Global),
1748                at("ours", 4, Sort::Untyped, Binding::Local),
1749            ],
1750            subsections: false,
1751        };
1752        let bytes = assembled(&input, &windows()).expect("an object");
1753        let file = object::File::parse(&bytes[..]).expect("a readable object");
1754        let offered = file.symbols().find(|s| s.name() == Ok("offered")).expect("the label");
1755        assert!(offered.is_global(), "a `.globl` label came out local");
1756        let ours = file.symbols().find(|s| s.name() == Ok("ours")).expect("the other label");
1757        assert!(!ours.is_global(), "a label nothing offered came out global");
1758        // And the same input on ELF is still what gas writes there, which is the half of this that
1759        // would otherwise have been changed to fix the other half.
1760        let bytes = assembled(&input, &target()).expect("an object");
1761        assert_eq!(st_info(&bytes, "offered") & 0xf, elf::STT_NOTYPE.0);
1762    }
1763
1764    #[test]
1765    fn a_relocation_on_coff_says_how_much_of_the_instruction_comes_after_it() {
1766        // The one real difference between the two formats' relocations. ELF folds the distance
1767        // between the hole and the end of the instruction into the addend and has one type. COFF
1768        // counts from the end of the instruction and has no addend field, so the count is in the
1769        // type: `IMAGE_REL_AMD64_REL32_4` is four bytes of immediate behind the displacement.
1770        let mut text = part(".text", vec![0; 16]);
1771        text.relocs.push(Reloc {
1772            at: 2,
1773            symbol: "elsewhere".to_owned(),
1774            kind: Reference::Data,
1775            addend: -8,
1776            after: 4,
1777        });
1778        let input = Assembled {
1779            parts: vec![text],
1780            names: vec![Name {
1781                name: "elsewhere".to_owned(),
1782                at: Held::Undefined,
1783                size: 0,
1784                sort: Sort::Untyped,
1785                binding: Binding::Global,
1786                visibility: Visibility::Default,
1787            }],
1788            subsections: false,
1789        };
1790        let bytes = assembled(&input, &windows()).expect("an object");
1791        let file = object::File::parse(&bytes[..]).expect("a readable object");
1792        let section = file.section_by_name(".text").expect("the section");
1793        let (at, reloc) = section.relocations().next().expect("the relocation");
1794        assert_eq!(at, 2);
1795        assert_eq!(
1796            reloc.flags(),
1797            RelocationFlags::Coff { typ: pe::RelocationType(pe::IMAGE_REL_AMD64_REL32.0 + 4) }
1798        );
1799    }
1800
1801    /// A linux i386 target, which is written as a 32 bit ELF file with REL relocations.
1802    fn i386() -> TargetInfo {
1803        TargetInfo::new(Triple::new(TargetArch::X86, Os::Linux, Env::Gnu))
1804    }
1805
1806    /// Every relocation of one section of an i386 file, as the offset it is at, its type, the
1807    /// symbol it names, and the addend the bytes it covers hold.
1808    ///
1809    /// The reader reports an addend of nothing for a REL file and says the real one is implicit,
1810    /// so what is added is read out of the section, which is where the linker reads it too.
1811    fn implicit(file: &object::File<'_>, section: &str) -> Vec<(u64, u32, String, i64)> {
1812        let section = file.section_by_name(section).expect("the section");
1813        let data = section.data().expect("the bytes");
1814        section
1815            .relocations()
1816            .map(|(at, reloc)| {
1817                assert!(reloc.has_implicit_addend(), "a REL file keeps the addend in the bytes");
1818                assert_eq!(reloc.addend(), 0);
1819                let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1820                let width = crate::elf::width_i386(r_type).expect("a width");
1821                let at_ = at as usize;
1822                let mut word = [0u8; 8];
1823                word[..width].copy_from_slice(&data[at_..at_ + width]);
1824                // Sign extended from however many bytes it is, since a distance is negative as
1825                // often as not.
1826                let shift = 64 - 8 * width as u32;
1827                let addend = (i64::from_le_bytes(word) << shift) >> shift;
1828                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1829                    panic!("a relocation against a symbol")
1830                };
1831                let symbol = file.symbol_by_index(index).expect("the symbol");
1832                let name = match symbol.kind() {
1833                    SymbolKind::Section => {
1834                        let section = symbol.section_index().expect("a section symbol's section");
1835                        file.section_by_index(section)
1836                            .expect("it")
1837                            .name()
1838                            .expect("a name")
1839                            .to_owned()
1840                    }
1841                    _ => symbol.name().expect("a name").to_owned(),
1842                };
1843                (at, r_type.0, name, addend)
1844            })
1845            .collect()
1846    }
1847
1848    /// The relocations gas writes for the same instructions and data, with the addends where gas
1849    /// puts them.
1850    ///
1851    /// The code is what `gcc -m32 -fPIC` makes of a function that calls through the PLT, finds the
1852    /// global offset table, reaches a string of its own and a variable of someone else's, plus a
1853    /// plain call and an absolute address, which is what code that is not position independent
1854    /// writes. The data is an address at each width and a distance.
1855    #[test]
1856    fn an_i386_object_is_32_bit_elf_with_the_addends_in_the_bytes() {
1857        let reloc = |at, symbol: &str, kind, addend| Reloc {
1858            at,
1859            symbol: symbol.to_owned(),
1860            kind,
1861            addend,
1862            after: 0,
1863        };
1864        let code = vec![
1865            0xe8, 0, 0, 0, 0, // call foo@PLT
1866            0xe8, 0, 0, 0, 0, // call bar
1867            0x81, 0xc3, 0, 0, 0, 0, // addl $_GLOBAL_OFFSET_TABLE_, %ebx
1868            0x8d, 0x83, 0, 0, 0, 0, // leal .LC0@GOTOFF(%ebx), %eax
1869            0x8b, 0x83, 0, 0, 0, 0, // movl foo@GOT(%ebx), %eax
1870            0x89, 0x83, 0, 0, 0, 0, // movl %eax, foo@GOT(%ebx)
1871            0xa1, 0, 0, 0, 0, // movl counter+8, %eax
1872        ];
1873        let mut text = part(".text", code);
1874        text.relocs = vec![
1875            reloc(1, "foo", Reference::Call, -4),
1876            reloc(6, "bar", Reference::Data, -4),
1877            reloc(12, "_GLOBAL_OFFSET_TABLE_", Reference::GotFront, 2),
1878            reloc(18, ".LC0", Reference::GotOffset, 0),
1879            reloc(24, "foo", Reference::Slot, 0),
1880            reloc(30, "foo", Reference::SlotKept, 0),
1881            reloc(35, "counter", Reference::Signed, 8),
1882        ];
1883        let rodata = part(".rodata", b"abc\0hi\0\0".to_vec());
1884        let mut data = part(".data", vec![0; 11]);
1885        data.relocs = vec![
1886            reloc(0, "foo", Reference::Address { bytes: 4 }, 16),
1887            reloc(4, "bar", Reference::Away, 0),
1888            reloc(8, "foo", Reference::Address { bytes: 2 }, 0),
1889            reloc(10, "foo", Reference::Address { bytes: 1 }, 0),
1890        ];
1891        let undefined = |name: &str| Name {
1892            at: Held::Undefined,
1893            ..at(name, 0, Sort::Untyped, Binding::Global)
1894        };
1895        let names = vec![
1896            at("f", 0, Sort::Func, Binding::Global),
1897            Name {
1898                at: Held::In { part: 1, offset: 4 },
1899                ..at(".LC0", 0, Sort::Untyped, Binding::Local)
1900            },
1901            undefined("foo"),
1902            undefined("bar"),
1903            undefined("counter"),
1904            undefined("_GLOBAL_OFFSET_TABLE_"),
1905        ];
1906        let input = Assembled { parts: vec![text, rodata, data], names, subsections: false };
1907        let bytes = assembled(&input, &i386()).expect("an object");
1908
1909        let header = elf::FileHeader32::<Endianness>::parse(&bytes[..]).expect("a 32 bit header");
1910        let endian = header.endian().expect("an endianness");
1911        assert_eq!(bytes[4], elf::ELFCLASS32.0);
1912        assert_eq!(header.e_machine(endian), elf::EM_386);
1913        assert_eq!(header.e_type(endian), elf::ET_REL);
1914
1915        let file = object::File::parse(&bytes[..]).expect("a readable object");
1916        assert!(!file.is_64());
1917        assert_eq!(file.architecture(), Architecture::I386);
1918        for name in [".rel.text", ".rel.data"] {
1919            let section = file.section_by_name(name).expect("a REL section");
1920            let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("ELF") };
1921            assert_eq!(sh_type, elf::SHT_REL, "{name}");
1922        }
1923        assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
1924
1925        let text = implicit(&file, ".text");
1926        let want = [
1927            (1, elf::R_386_PLT32, "foo", -4),
1928            (6, elf::R_386_PC32, "bar", -4),
1929            (12, elf::R_386_GOTPC, "_GLOBAL_OFFSET_TABLE_", 2),
1930            // A label only this file sees is its section and how far into it, as gas writes it.
1931            (18, elf::R_386_GOTOFF, ".rodata", 4),
1932            (24, elf::R_386_GOT32X, "foo", 0),
1933            (30, elf::R_386_GOT32, "foo", 0),
1934            (35, elf::R_386_32, "counter", 8),
1935        ];
1936        let want: Vec<_> = want
1937            .into_iter()
1938            .map(|(at, r_type, name, addend)| (at, r_type.0, name.to_owned(), addend))
1939            .collect();
1940        assert_eq!(text, want);
1941
1942        let data = implicit(&file, ".data");
1943        let want = [
1944            (0, elf::R_386_32, "foo", 16),
1945            (4, elf::R_386_PC32, "bar", 0),
1946            (8, elf::R_386_16, "foo", 0),
1947            (10, elf::R_386_8, "foo", 0),
1948        ];
1949        let want: Vec<_> = want
1950            .into_iter()
1951            .map(|(at, r_type, name, addend)| (at, r_type.0, name.to_owned(), addend))
1952            .collect();
1953        assert_eq!(data, want);
1954    }
1955
1956    /// Each way i386 reaches a thread-local variable is the relocation gas writes for its suffix,
1957    /// with what is added in the bytes and the variable's own name kept even though only this file
1958    /// sees it, which is what gas does for these where it would have named the section for any
1959    /// other reference.
1960    #[test]
1961    fn the_i386_thread_local_references_are_the_relocations_gas_writes() {
1962        let kinds = [
1963            (Tls::General, elf::R_386_TLS_GD),
1964            (Tls::Module, elf::R_386_TLS_LDM),
1965            (Tls::InModule, elf::R_386_TLS_LDO_32),
1966            (Tls::Slot, elf::R_386_TLS_GOTIE),
1967            (Tls::SlotAddress, elf::R_386_TLS_IE),
1968            (Tls::SlotNegated, elf::R_386_TLS_IE_32),
1969            (Tls::Offset, elf::R_386_TLS_LE),
1970            (Tls::Negated, elf::R_386_TLS_LE_32),
1971        ];
1972        let mut text = part(".text", vec![0; 4 * kinds.len()]);
1973        text.relocs = kinds
1974            .iter()
1975            .enumerate()
1976            .map(|(n, &(tls, _))| Reloc {
1977                at: 4 * n,
1978                symbol: "x".to_owned(),
1979                kind: Reference::Tls(tls),
1980                addend: n as i64,
1981                after: 0,
1982            })
1983            .collect();
1984        let tdata = part(".tdata", vec![0; 8]);
1985        let names = vec![
1986            at("f", 0, Sort::Func, Binding::Global),
1987            Name {
1988                at: Held::In { part: 1, offset: 4 },
1989                ..at("x", 0, Sort::Thread, Binding::Local)
1990            },
1991        ];
1992        let input = Assembled { parts: vec![text, tdata], names, subsections: false };
1993        let bytes = assembled(&input, &i386()).expect("an object");
1994        let file = object::File::parse(&bytes[..]).expect("a readable object");
1995        let want: Vec<_> = kinds
1996            .iter()
1997            .enumerate()
1998            .map(|(n, &(_, r_type))| (4 * n as u64, r_type.0, "x".to_owned(), n as i64))
1999            .collect();
2000        assert_eq!(implicit(&file, ".text"), want);
2001    }
2002
2003    /// A file of assembly for i386 on Windows is COFF with the i386 relocations, and its names are
2004    /// what the file spelled: a listing for that platform already has the underscore on every C
2005    /// name, so nothing is put in front of `_main`, `_puts` or a `__fastcall` `@f@8`.
2006    #[test]
2007    fn an_i386_windows_file_of_assembly_keeps_its_names_as_written() {
2008        use object::pe::{
2009            IMAGE_REL_I386_DIR32, IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_REL32,
2010            IMAGE_REL_I386_SECREL,
2011        };
2012        let reloc = |at, symbol: &str, kind, addend| Reloc {
2013            at,
2014            symbol: symbol.to_owned(),
2015            kind,
2016            addend,
2017            after: 0,
2018        };
2019        let code = vec![
2020            0xe8, 0, 0, 0, 0, // call _puts
2021            0xe8, 0, 0, 0, 0, // call @f@8
2022            0xa1, 0, 0, 0, 0, // movl _counter+8, %eax
2023            0xc3,
2024        ];
2025        let mut text = part(".text", code);
2026        text.relocs = vec![
2027            reloc(1, "_puts", Reference::Call, -4),
2028            reloc(6, "@f@8", Reference::Call, -4),
2029            reloc(11, "_counter", Reference::Address { bytes: 4 }, 8),
2030        ];
2031        let mut data = part(".data", vec![0; 12]);
2032        data.relocs = vec![
2033            reloc(0, "_main", Reference::Image, 0),
2034            reloc(4, "_main", Reference::Section, 0),
2035            reloc(8, "_puts", Reference::Away, 0),
2036        ];
2037        let undefined = |name: &str| Name {
2038            at: Held::Undefined,
2039            ..at(name, 0, Sort::Untyped, Binding::Global)
2040        };
2041        let names = vec![
2042            at("_main", 0, Sort::Func, Binding::Global),
2043            undefined("_puts"),
2044            undefined("@f@8"),
2045            undefined("_counter"),
2046        ];
2047        let input = Assembled { parts: vec![text, data], names, subsections: false };
2048        let target = TargetInfo::new(Triple::new(TargetArch::X86, Os::Windows, Env::Gnu));
2049        let bytes = assembled(&input, &target).expect("an object");
2050        let file = object::File::parse(&bytes[..]).expect("a readable object");
2051        assert_eq!(file.format(), object::BinaryFormat::Coff);
2052        assert_eq!(file.architecture(), Architecture::I386);
2053        for name in ["_main", "_puts", "@f@8", "_counter"] {
2054            assert!(file.symbol_by_name(name).is_some(), "{name}");
2055        }
2056        assert!(file.symbol_by_name("__main").is_none());
2057        let types = |name: &str| -> Vec<(u64, u16, String)> {
2058            let section = file.section_by_name(name).expect("a section");
2059            section
2060                .relocations()
2061                .map(|(at, reloc)| {
2062                    let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
2063                    let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
2064                        panic!("a symbol")
2065                    };
2066                    let symbol = file.symbol_by_index(symbol).expect("a symbol");
2067                    (at, typ.0, symbol.name().expect("a name").to_owned())
2068                })
2069                .collect()
2070        };
2071        let want = |list: &[(u64, pe::RelocationType, &str)]| -> Vec<(u64, u16, String)> {
2072            list.iter().map(|&(at, typ, name)| (at, typ.0, name.to_owned())).collect()
2073        };
2074        assert_eq!(
2075            types(".text"),
2076            want(&[
2077                (1, IMAGE_REL_I386_REL32, "_puts"),
2078                (6, IMAGE_REL_I386_REL32, "@f@8"),
2079                (11, IMAGE_REL_I386_DIR32, "_counter"),
2080            ])
2081        );
2082        assert_eq!(
2083            types(".data"),
2084            want(&[
2085                (0, IMAGE_REL_I386_DIR32NB, "_main"),
2086                (4, IMAGE_REL_I386_SECREL, "_main"),
2087                (8, IMAGE_REL_I386_REL32, "_puts"),
2088            ])
2089        );
2090        let code = file.section_by_name(".text").expect("a text section");
2091        let code = code.data().expect("the bytes");
2092        assert_eq!(&code[1..5], &0i32.to_le_bytes(), "a call counts from the end of its bytes");
2093        assert_eq!(&code[11..15], &8i32.to_le_bytes());
2094        let image = file.section_by_name(".data").expect("a data section");
2095        assert_eq!(&image.data().expect("the bytes")[8..12], &4i32.to_le_bytes());
2096    }
2097
2098    /// The labels gcc writes for i686 Windows start with a bare `L`, and they are left out of the
2099    /// table the way gas leaves them out, with whatever pointed at one pointing at its section and
2100    /// the distance into it added in the bytes.
2101    #[test]
2102    fn an_i386_windows_label_of_gccs_own_is_its_section_and_not_a_name() {
2103        use object::pe::{IMAGE_REL_I386_DIR32, IMAGE_REL_I386_REL32};
2104        // movl $LC1, (%esp) ; jmp L3 ; L3: ret
2105        let mut text = part(".text", vec![0xc7, 0x04, 0x24, 0, 0, 0, 0, 0xe9, 0, 0, 0, 0, 0xc3]);
2106        let reloc = |at, symbol: &str, kind, addend| Reloc {
2107            at,
2108            symbol: symbol.to_owned(),
2109            kind,
2110            addend,
2111            after: 0,
2112        };
2113        text.relocs = vec![
2114            reloc(3, "LC1", Reference::Address { bytes: 4 }, 0),
2115            reloc(8, "Lfar", Reference::Data, -4),
2116        ];
2117        let rdata = part(".rdata", vec![0; 8]);
2118        let names = vec![
2119            at("_f", 0, Sort::Func, Binding::Global),
2120            at("L3", 12, Sort::Untyped, Binding::Local),
2121            Name {
2122                at: Held::In { part: 1, offset: 4 },
2123                ..at("LC1", 0, Sort::Untyped, Binding::Local)
2124            },
2125            Name {
2126                at: Held::In { part: 1, offset: 0 },
2127                ..at("Lfar", 0, Sort::Untyped, Binding::Local)
2128            },
2129        ];
2130        let input = Assembled { parts: vec![text, rdata], names, subsections: false };
2131        let target = TargetInfo::new(Triple::new(TargetArch::X86, Os::Windows, Env::Gnu));
2132        let bytes = assembled(&input, &target).expect("an object");
2133        let file = object::File::parse(&bytes[..]).expect("a readable object");
2134        assert!(file.symbol_by_name("_f").is_some());
2135        for name in ["L3", "LC1", "Lfar"] {
2136            assert!(file.symbol_by_name(name).is_none(), "{name}");
2137        }
2138        let section = file.section_by_name(".text").expect("a section");
2139        let relocs: Vec<_> = section
2140            .relocations()
2141            .map(|(at, reloc)| {
2142                let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
2143                let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
2144                    panic!("a symbol")
2145                };
2146                let symbol = file.symbol_by_index(symbol).expect("a symbol");
2147                (at, typ.0, symbol.name().expect("a name").to_owned())
2148            })
2149            .collect();
2150        assert_eq!(
2151            relocs,
2152            vec![
2153                (3, IMAGE_REL_I386_DIR32.0, ".rdata".to_owned()),
2154                (8, IMAGE_REL_I386_REL32.0, ".rdata".to_owned())
2155            ]
2156        );
2157        let code = section.data().expect("the bytes");
2158        assert_eq!(&code[3..7], &4i32.to_le_bytes(), "the distance into .rdata");
2159        assert_eq!(&code[8..12], &0i32.to_le_bytes(), "the start of .rdata, counted from the end");
2160    }
2161
2162    /// What i386 has no relocation for is refused rather than written as the nearest thing.
2163    ///
2164    /// An address in eight bytes and a distance in eight are wider than anything this machine
2165    /// relocates, a load from the instruction pointer is something it cannot do, and the x86-64 slot
2166    /// of a thread-local variable is not how this machine reaches one.
2167    #[test]
2168    fn a_reference_i386_has_no_relocation_for_is_refused() {
2169        for kind in [
2170            Reference::Address { bytes: 8 },
2171            Reference::AwayWide,
2172            Reference::Got,
2173            Reference::GotBare,
2174            Reference::GotKept,
2175            Reference::Thread,
2176            Reference::Image,
2177            Reference::Section,
2178        ] {
2179            let mut text = part(".text", vec![0; 8]);
2180            text.relocs =
2181                vec![Reloc { at: 0, symbol: "foo".to_owned(), kind, addend: 0, after: 0 }];
2182            let names =
2183                vec![Name { at: Held::Undefined, ..at("foo", 0, Sort::Untyped, Binding::Global) }];
2184            let input = Assembled { parts: vec![text], names, subsections: false };
2185            let Err(Error::Refused { .. }) = assembled(&input, &i386()) else {
2186                panic!("{kind:?} was written for i386");
2187            };
2188        }
2189    }
2190
2191    /// An addend that does not fit in the bytes that have to hold it is refused, since a REL file
2192    /// has nowhere else to put the rest of it.
2193    #[test]
2194    fn an_i386_addend_too_wide_for_its_bytes_is_refused() {
2195        let mut data = part(".data", vec![0; 2]);
2196        let kind = Reference::Address { bytes: 1 };
2197        data.relocs = vec![Reloc { at: 0, symbol: "foo".to_owned(), kind, addend: 300, after: 0 }];
2198        let names =
2199            vec![Name { at: Held::Undefined, ..at("foo", 0, Sort::Untyped, Binding::Global) }];
2200        let input = Assembled { parts: vec![data], names, subsections: false };
2201        let Err(Error::Refused { why }) = assembled(&input, &i386()) else {
2202            panic!("an addend of 300 went into one byte");
2203        };
2204        assert!(why.contains("300"), "{why}");
2205    }
2206
2207    /// The header fields written into the finished bytes land where a 32 bit file keeps them,
2208    /// which is not where a 64 bit one does.
2209    #[test]
2210    fn a_32_bit_file_gets_its_entry_size_and_groups_in_the_right_place() {
2211        let mut strings = part(".rodata.str1.1", b"hi\0".to_vec());
2212        strings.shape.merge = 1;
2213        strings.shape.strings = true;
2214        let mut kept = part(".text.f", vec![0xc3]);
2215        kept.group = Some(Group { symbol: "f".to_owned(), keep: Keep::Together });
2216        let names = vec![Name {
2217            at: Held::In { part: 1, offset: 0 },
2218            ..at("f", 0, Sort::Func, Binding::Global)
2219        }];
2220        let input = Assembled { parts: vec![strings, kept], names, subsections: false };
2221        let bytes = assembled(&input, &i386()).expect("an object");
2222        let header = elf::FileHeader32::<Endianness>::parse(&bytes[..]).expect("a 32 bit header");
2223        let endian = header.endian().expect("an endianness");
2224        let sections = header.sections(endian, &bytes[..]).expect("the sections");
2225        let find = |name: &str| {
2226            sections
2227                .iter()
2228                .find(|section| sections.section_name(endian, section) == Ok(name.as_bytes()))
2229                .expect("the section")
2230        };
2231        assert_eq!(find(".rodata.str1.1").sh_entsize(endian), 1);
2232        let group = find(".group");
2233        let at = group.sh_offset(endian) as usize;
2234        assert_eq!(&bytes[at..at + 4], &[0; 4], "a group that is not a COMDAT says so");
2235    }
2236
2237    /// Two records of the same name, each going with the text of the function it is about. The
2238    /// linker refuses to put a section that goes with something beside one of the same name that
2239    /// does not, so both have to say it, and each has to say the right section.
2240    #[test]
2241    fn a_section_that_goes_with_a_name_points_at_the_section_the_name_is_in() {
2242        let record = |link: &str| Part {
2243            shape: Shape { write: true, ..Shape::of("__patchable_function_entries") },
2244            link: Some(link.to_owned()),
2245            ..part("__patchable_function_entries", vec![0; 8])
2246        };
2247        let parts = vec![
2248            part(".text", vec![0xc3]),
2249            part(".init.text", vec![0xc3]),
2250            record("f"),
2251            record("g"),
2252        ];
2253        let names = vec![
2254            Name { at: Held::In { part: 1, offset: 0 }, ..at("f", 0, Sort::Func, Binding::Global) },
2255            at("g", 0, Sort::Func, Binding::Global),
2256        ];
2257        let bytes = assembled(&Assembled { parts, names, subsections: false }, &target())
2258            .expect("an object");
2259        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
2260        let endian = header.endian().expect("an endianness");
2261        let sections = header.sections(endian, &bytes[..]).expect("the sections");
2262        let index = |name: &str| {
2263            sections
2264                .iter()
2265                .position(|section| sections.section_name(endian, section) == Ok(name.as_bytes()))
2266                .expect("the section") as u32
2267        };
2268        let records: Vec<_> = sections
2269            .iter()
2270            .filter(|section| {
2271                sections.section_name(endian, section) == Ok(&b"__patchable_function_entries"[..])
2272            })
2273            .collect();
2274        assert_eq!(records.len(), 2);
2275        for record in &records {
2276            assert!(
2277                record.sh_flags(endian).contains(elf::SHF_LINK_ORDER),
2278                "a record that goes with nothing"
2279            );
2280        }
2281        assert_eq!(records[0].sh_link(endian), index(".init.text"));
2282        assert_eq!(records[1].sh_link(endian), index(".text"));
2283    }
2284
2285    #[test]
2286    fn a_section_that_goes_with_a_name_in_no_section_is_refused() {
2287        let record = Part {
2288            link: Some("nowhere".to_owned()),
2289            ..part("__patchable_function_entries", vec![0; 8])
2290        };
2291        let input = Assembled { parts: vec![record], names: Vec::new(), subsections: false };
2292        assert!(assembled(&input, &target()).is_err());
2293    }
2294}