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