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