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        let id = obj.add_symbol(Symbol {
653            name: name.name.clone().into_bytes(),
654            value,
655            size,
656            kind: flavour.sort(name.sort, name.binding),
657            scope: crate::file::scope_of(name.binding),
658            weak: name.binding == Binding::Weak,
659            section,
660            flags: SymbolFlags::None,
661        });
662        flavour.see(&mut obj, id, name.binding, name.visibility);
663        if name.sort == Sort::Ifunc {
664            // Only ELF has the type. COFF and Mach-O reach a function chosen at load time through
665            // a pointer the program fills in itself, which is a different program rather than a
666            // different symbol, so a listing asking for one there is refused rather than written
667            // as the ordinary function it is not.
668            if flavour != Flavour::Elf {
669                let why = format!("'{}' is an indirect function, which only ELF has", name.name);
670                return Err(Error::Refused { why });
671            }
672            crate::elf::indirect(&mut obj, id);
673        }
674        // The writer underneath records a common symbol as `STT_COMMON` and gas records the same
675        // symbol as `STT_OBJECT`. Both are a request for storage and a linker reads either, and the
676        // one gas writes is written here, because an object that says the same thing a different
677        // way is the kind of difference that turns up years later in a tool that only ever saw the
678        // other one. A common symbol is global by definition, so there is no binding to preserve.
679        if matches!(name.at, Held::Common { .. }) {
680            if let SymbolFlags::Elf { st_info, .. } = obj.symbol_flags_mut(id) {
681                *st_info = elf::STB_GLOBAL | elf::STT_OBJECT;
682            }
683        }
684        symbols.insert(name.name.clone(), id);
685    }
686
687    // One group for every symbol on ELF, holding each section that named it, and one for every
688    // section on COFF, where a COMDAT is a section and the symbol it is about.
689    let mut groups: Vec<(&Group, Vec<SectionId>)> = Vec::new();
690    let mut grouped: Map<&str, usize> = Map::default();
691    for (part, id) in input.parts.iter().zip(&made) {
692        let Some(group) = &part.group else { continue };
693        match grouped.get(group.symbol.as_str()) {
694            Some(&at) if flavour == Flavour::Elf => groups[at].1.push(*id),
695            _ => {
696                grouped.insert(group.symbol.as_str(), groups.len());
697                groups.push((group, vec![*id]));
698            }
699        }
700    }
701    let mut together = Vec::new();
702    for (group, sections) in groups {
703        let symbol = match symbols.get(&group.symbol) {
704            Some(&symbol) => symbol,
705            // ELF only wants the name of a group, and a group about a name the file does not
706            // define gets a local one of its own. llvm-mc puts it in the group's own section,
707            // which the writer underneath has no handle on, and the first member is as good a
708            // place, since nothing reads where it is.
709            None if flavour == Flavour::Elf => obj.add_symbol(Symbol {
710                name: group.symbol.clone().into_bytes(),
711                value: 0,
712                size: 0,
713                kind: SymbolKind::Label,
714                scope: SymbolScope::Compilation,
715                weak: false,
716                section: SymbolSection::Section(sections[0]),
717                flags: SymbolFlags::None,
718            }),
719            None => {
720                let why = format!(
721                    "section '{}' is a COMDAT about '{}', which the file does not define",
722                    obj.section(sections[0]).name().unwrap_or_default(),
723                    group.symbol
724                );
725                return Err(Error::Refused { why });
726            }
727        };
728        together.push(group.keep == Keep::Together);
729        obj.add_comdat(object::write::Comdat { kind: group.keep.kind(), symbol, sections });
730    }
731
732    for (part, id) in input.parts.iter().zip(&made) {
733        for reloc in &part.relocs {
734            let (symbol, addend) = match onto(reloc) {
735                Some((part, offset)) => {
736                    (obj.section_symbol(made[part]), reloc.addend + offset as i64)
737                }
738                None => {
739                    let Some(&symbol) = symbols.get(&reloc.symbol) else {
740                        let why = format!(
741                            "'{}' is named by a relocation and by nothing else",
742                            reloc.symbol
743                        );
744                        return Err(Error::Refused { why });
745                    };
746                    (symbol, reloc.addend)
747                }
748            };
749            let flags = flags_of(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
750                why: format!("no relocation is {:?}", reloc.kind),
751            })?;
752            let addend = match reloc.kind {
753                crate::section::Reference::Field(fixup) if carried => {
754                    let data = obj.section_mut(*id).data_mut();
755                    let Some(bytes) = data.get_mut(reloc.at..reloc.at + 4) else {
756                        let why = format!("a field at {} is past the end of its section", reloc.at);
757                        return Err(Error::Refused { why });
758                    };
759                    let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
760                    let word = crate::coff::carry(fixup, word, addend)
761                        .map_err(|why| Error::Refused { why })?;
762                    bytes.copy_from_slice(&word.to_le_bytes());
763                    0
764                }
765                _ => addend,
766            };
767            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
768            crate::file::relocate(&mut obj, *id, record)?;
769        }
770    }
771
772    // The debug information, every section before any relocation because a relocation in one of
773    // them names another as often as it names a function.
774    let mut named = Map::default();
775    for chunk in &info.chunks {
776        // An i386 file keeps each addend in the bytes of its section, which a compressed section
777        // no longer holds, so its debug sections are left as they are for now.
778        let how = if flavour == Flavour::Elf && obj.architecture() != Architecture::I386 {
779            info.compress
780        } else {
781            Compress::None
782        };
783        let id = crate::zlib::debug_section(&mut obj, chunk, how);
784        named.insert(chunk.name.as_str(), id);
785    }
786    for chunk in &info.chunks {
787        let section = named[chunk.name.as_str()];
788        for reloc in &chunk.relocs {
789            // The debug information names a function by its C name, and on i386 COFF the listing
790            // gave it the underscore every C name has there.
791            let spelled =
792                if pe32 { crate::coff::decorate(&reloc.symbol) } else { reloc.symbol.clone() };
793            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
794                Some(&id) => (obj.section_symbol(id), reloc.addend),
795                None => match defined.get(spelled.as_str()).map(|name| name.at) {
796                    Some(Held::In { part, offset }) => {
797                        (obj.section_symbol(made[part]), reloc.addend + offset as i64)
798                    }
799                    _ => match symbols.get(&spelled) {
800                        Some(&symbol) => (symbol, reloc.addend),
801                        None => {
802                            let why = format!(
803                                "'{}' is named by the debug information and is not defined here",
804                                reloc.symbol
805                            );
806                            return Err(Error::Refused { why });
807                        }
808                    },
809                },
810            };
811            let kind = flavour.debug(reloc.kind, named.contains_key(reloc.symbol.as_str()));
812            let flags = flags_of(kind, reloc.after)
813                .ok_or_else(|| Error::Refused { why: format!("no relocation is {kind:?}") })?;
814            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
815            crate::file::relocate(&mut obj, section, record)?;
816        }
817    }
818
819    // No marker of its own. A file of assembly has the stack it says it has, which is the
820    // `.note.GNU-stack` it wrote or the one `--noexecstack` had the reader add, as with gas.
821
822    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
823    if flavour == Flavour::Elf {
824        for part in input.parts.iter().filter(|part| part.shape.merge != 0) {
825            entry_size(&mut bytes, &part.name, part.shape.merge);
826        }
827        together_groups(&mut bytes, &together);
828        linked(&mut bytes, input, &defined)?;
829    }
830    Ok(bytes)
831}
832
833/// Write the section each `o` section goes with into its `sh_link`, which the writer underneath has
834/// no field for. See [`crate::elf::link`], which does the same for the records the compiler writes
835/// itself.
836///
837/// What the source named is a symbol, and the section is the one the symbol is in, or a section of
838/// that name when no symbol has it, which is what gas takes as well. Two sections may share a name
839/// here, so a part's header is found by how many parts of the same name come before it, which is
840/// the order the writer puts them in.
841///
842/// # Errors
843///
844/// [`Error::Refused`] for a name that is in no section of the file, which gas refuses too: a
845/// section that goes with nothing is one a linker reads as an error.
846fn linked(bytes: &mut [u8], input: &Assembled, defined: &Map<&str, &Name>) -> Result<(), Error> {
847    if input.parts.iter().all(|part| part.link.is_none()) {
848        return Ok(());
849    }
850    let headers = crate::elf::Headers::read(bytes);
851    let index = |part: usize| {
852        let name = &input.parts[part].name;
853        let nth = input.parts[..part].iter().filter(|other| other.name == *name).count();
854        headers.list.iter().enumerate().filter(|(_, header)| header.name == *name).nth(nth)
855    };
856    let mut writes = Vec::new();
857    for (at, part) in input.parts.iter().enumerate() {
858        let Some(link) = &part.link else { continue };
859        let target = match defined.get(link.as_str()).map(|name| name.at) {
860            Some(Held::In { part, .. }) => Some(part),
861            _ => input.parts.iter().position(|other| other.name == *link),
862        };
863        let Some(target) = target.and_then(index) else {
864            let why = format!("section '{}' goes with '{link}', which is in no section", part.name);
865            return Err(Error::Refused { why });
866        };
867        let (_, header) = index(at).expect("a header for every section");
868        let target = u32::try_from(target.0).expect("a file with this many sections in it");
869        writes.push((header.at + headers.link(), target));
870    }
871    for (at, target) in writes {
872        bytes[at..at + 4].copy_from_slice(&target.to_le_bytes());
873    }
874    Ok(())
875}
876
877/// Write how long an entry of a mergeable section is into its header, which the linker needs and
878/// the writer underneath has no field for. It writes one only for a section of strings it made
879/// itself. The section is found by its name, which is unique because the assembler gave every name
880/// one section.
881fn entry_size(bytes: &mut [u8], name: &str, size: u64) {
882    let headers = crate::elf::Headers::read(bytes);
883    let (field, width) = headers.entry_size();
884    for header in headers.list.iter().filter(|header| header.name == name) {
885        let at = header.at + field;
886        bytes[at..at + width].copy_from_slice(&size.to_le_bytes()[..width]);
887    }
888}
889
890/// Clear the word that makes a group a COMDAT in each group of an ELF file that is not one, which
891/// the writer underneath writes into every group it makes. `together` holds a flag for each group
892/// in the order they were added, which is the order the writer puts their headers in.
893fn together_groups(bytes: &mut [u8], together: &[bool]) {
894    if !together.contains(&true) {
895        return;
896    }
897    let headers = crate::elf::Headers::read(bytes);
898    let groups = headers.list.iter().filter(|header| header.sh_type == elf::SHT_GROUP.0);
899    for (header, _) in groups.zip(together).filter(|(_, together)| **together) {
900        bytes[header.offset..header.offset + 4].copy_from_slice(&0u32.to_le_bytes());
901    }
902}
903
904/// The section and the offset into it a relocation is written against in place of the name it
905/// gave, when gas would do the same.
906///
907/// A name only this file can see is a place in a section and nothing more, so gas writes the
908/// section's own symbol and how far into it the place is, and a `.L` label then has no reason to be
909/// in the table at all. It keeps the name where the linker has to see it: a call, which may go
910/// through a stub the linker makes for that name, a slot of the global offset table, and a place in
911/// a section the linker may merge, where the offset into the section is not an offset into the
912/// merged one. The last of those is only a problem for a distance, or for an address with
913/// something added to it, since the address of the start of a string is what the linker follows.
914///
915/// COFF for i386, `pe32`, does the same for a temporary name, whatever refers to it, since it has
916/// no stubs or tables of that kind to go through. gas goes further there and writes the section for
917/// every name the file defines, `_main` included. Naming the symbol instead is what the other COFF
918/// machines here do, and the linker lands on the same address either way.
919fn moved(
920    flavour: Flavour,
921    pe32: bool,
922    input: &Assembled,
923    defined: &Map<&str, &Name>,
924    reloc: &Reloc,
925) -> Option<(usize, u64)> {
926    use crate::section::Reference;
927    let name = defined.get(reloc.symbol.as_str())?;
928    let Held::In { part, offset } = name.at else { return None };
929    // A reference to an indirect function is to whatever its resolver picks, and the place the
930    // name marks is the resolver. The section and an offset would be the resolver itself, so the
931    // name stays, which is what gas leaves for one too.
932    if name.sort == Sort::Ifunc {
933        return None;
934    }
935    if pe32 && unseen(name, pe32) {
936        return Some((part, offset));
937    }
938    if flavour != Flavour::Elf || name.binding != Binding::Local {
939        return None;
940    }
941    let near = matches!(reloc.kind, Reference::Data | Reference::Away | Reference::AwayWide);
942    let fixed = match reloc.kind {
943        Reference::Call
944        | Reference::Got
945        | Reference::GotBare
946        | Reference::GotKept
947        | Reference::Slot
948        | Reference::SlotKept
949        | Reference::GotFront
950        | Reference::Thread
951        | Reference::Tls(_) => false,
952        // The same for a field of an instruction that goes through a stub or a table slot, or that
953        // says where a thread-local variable is, which a linker checks against the name's type.
954        Reference::Field(
955            Fixup::Call26
956            | Fixup::Jump26
957            | Fixup::GotPage21
958            | Fixup::GotLo12
959            | Fixup::GotTprelPage21
960            | Fixup::GotTprelLo12Nc
961            | Fixup::TprelHi12
962            | Fixup::TprelLo12Nc,
963        ) => false,
964        _ if input.parts.get(part)?.shape.merge != 0 => !near && reloc.addend == 0,
965        _ => true,
966    };
967    fixed.then_some((part, offset))
968}
969
970/// Whether a name is one the assembler made up or a label only it sees, which gas leaves out of the
971/// table unless a relocation still names it. `.L` is the prefix for those that ELF assemblers agree
972/// on, and a name with a `\u{1}` in it is one this assembler made for a numbered label or a frame.
973///
974/// COFF for i386, `pe32`, adds a bare `L`, which is what gcc and clang for that target start their
975/// own labels with, `L3` for a block and `LC0` for a string. No C name can start that way there,
976/// since every one of them has an underscore in front, and gas for `pe-i386` leaves them out too.
977fn unseen(name: &Name, pe32: bool) -> bool {
978    name.binding == Binding::Local
979        && (name.name.starts_with(".L")
980            || name.name.starts_with("..")
981            || name.name.contains('\u{1}')
982            || (pe32 && name.name.starts_with('L')))
983}
984
985/// Every name in it a linker can find, which is what an archive's symbol index is built from.
986///
987/// The same rule as [`crate::defines`]: a local is left out, because a name the static link has
988/// already finished with is not one an archive may offer, and an undefined one is left out because
989/// this file does not have it.
990#[must_use]
991pub fn assembled_defines(input: &Assembled) -> Vec<String> {
992    input
993        .names
994        .iter()
995        .filter(|name| name.binding != Binding::Local && name.at != Held::Undefined)
996        .map(|name| name.name.clone())
997        .collect()
998}
999
1000#[cfg(test)]
1001mod tests {
1002    use super::*;
1003
1004    use object::read::elf::{FileHeader as _, SectionHeader as _, Sym as _};
1005    use object::read::{Object as _, ObjectComdat as _, ObjectSection as _, ObjectSymbol as _};
1006    use object::{RelocationFlags, SectionFlags};
1007    use rucc_target::{Arch as TargetArch, Env, Os, Triple};
1008
1009    use crate::section::{Reference, Tls};
1010
1011    /// A linux x86-64 target, which is the one most of these are written against.
1012    fn target() -> TargetInfo {
1013        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Linux, Env::Gnu))
1014    }
1015
1016    /// The same machine under mingw-w64, which is the target the COFF cases below are about.
1017    fn windows() -> TargetInfo {
1018        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Windows, Env::Gnu))
1019    }
1020
1021    /// One section of that name holding those bytes, with the flags the name implies.
1022    fn part(name: &str, bytes: Vec<u8>) -> Part {
1023        Part {
1024            name: name.to_owned(),
1025            size: bytes.len() as u64,
1026            bytes,
1027            align: 1,
1028            shape: Shape::of(name),
1029            relocs: Vec::new(),
1030            group: None,
1031            link: None,
1032        }
1033    }
1034
1035    /// One name at an offset into the first section.
1036    fn at(name: &str, offset: u64, sort: Sort, binding: Binding) -> Name {
1037        Name {
1038            name: name.to_owned(),
1039            at: Held::In { part: 0, offset },
1040            size: 0,
1041            sort,
1042            binding,
1043            visibility: Visibility::Default,
1044        }
1045    }
1046
1047    /// The raw `st_info` and `st_value` of a symbol, as the file holds them.
1048    ///
1049    /// The reader's own `kind()`, `is_global()` and `address()` are a translation of these, and a
1050    /// translation is what several of the cases below are about, so they ask the file rather than
1051    /// the reading. A common symbol is the clearest of them: `address()` gives zero for one because
1052    /// it has no address, and the field an ordinary symbol keeps its address in is where a common
1053    /// one states the boundary it has to start on.
1054    fn raw(bytes: &[u8], want: &str) -> (u8, u64) {
1055        let header = elf::FileHeader64::<Endianness>::parse(bytes).expect("a header");
1056        let endian = header.endian().expect("an endianness");
1057        let table = header.sections(endian, bytes).expect("the sections");
1058        let symbols = table.symbols(endian, bytes, elf::SHT_SYMTAB).expect("a symbol table");
1059        for symbol in symbols.iter() {
1060            if symbols.symbol_name(endian, symbol).expect("a name") == want.as_bytes() {
1061                return (symbol.st_info().0, symbol.st_value(endian));
1062            }
1063        }
1064        panic!("there is no symbol called '{want}'");
1065    }
1066
1067    /// The first half of that.
1068    fn st_info(bytes: &[u8], want: &str) -> u8 {
1069        raw(bytes, want).0
1070    }
1071
1072    #[test]
1073    fn a_section_carries_the_flags_the_source_said_and_not_the_ones_its_name_suggests() {
1074        // The whole reason a shape is separate facts rather than a kind. A program may write
1075        // `.section .init.text,"ax"` and mean a section with a name this compiler has never heard
1076        // of, and what it said about it is the letters.
1077        let mut odd = part(".init.text", vec![0x90]);
1078        odd.shape = Shape { alloc: true, exec: true, bits: true, ..Shape::default() };
1079        let input = Assembled { parts: vec![odd], names: Vec::new(), subsections: false };
1080        let bytes = assembled(&input, &target()).expect("an object");
1081        let file = object::File::parse(&bytes[..]).expect("a readable object");
1082        let section = file.section_by_name(".init.text").expect("the section");
1083        assert_eq!(section.data().expect("the bytes"), &[0x90]);
1084        let SectionFlags::Elf { sh_flags, sh_type } = section.flags() else {
1085            panic!("this is an ELF file");
1086        };
1087        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_EXECINSTR.0);
1088        assert_eq!(sh_flags.0 & elf::SHF_WRITE.0, 0, "nothing said it was writable");
1089        assert_eq!(sh_type, elf::SHT_PROGBITS);
1090    }
1091
1092    #[test]
1093    fn a_section_that_holds_no_bytes_still_says_how_long_it_is() {
1094        // `.bss` is a length and no bytes, and a writer that appended its data would produce a file
1095        // with that much zero in it, which is the difference between an object and a big object.
1096        let mut room = part(".bss", Vec::new());
1097        room.size = 4096;
1098        room.align = 16;
1099        let input = Assembled { parts: vec![room], names: Vec::new(), subsections: false };
1100        let bytes = assembled(&input, &target()).expect("an object");
1101        assert!(bytes.len() < 4096, "the empty space was written out: {} bytes", bytes.len());
1102        let file = object::File::parse(&bytes[..]).expect("a readable object");
1103        let section = file.section_by_name(".bss").expect("the section");
1104        assert_eq!(section.size(), 4096);
1105        assert_eq!(section.align(), 16);
1106        let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("an ELF file") };
1107        assert_eq!(sh_type, elf::SHT_NOBITS);
1108    }
1109
1110    #[test]
1111    fn a_label_nobody_stated_a_type_for_is_a_symbol_with_no_type() {
1112        // `STT_NOTYPE` is what gas writes for one, and it is a real answer rather than a missing
1113        // one. The writer underneath refuses a defined symbol whose kind is `Unknown` outright, so
1114        // this is also the case that says the mapping went to `Label` and not there.
1115        let input = Assembled {
1116            parts: vec![part(".text", vec![0; 8])],
1117            names: vec![at("plain", 4, Sort::Untyped, Binding::Global)],
1118            subsections: false,
1119        };
1120        let bytes = assembled(&input, &target()).expect("an object");
1121        let file = object::File::parse(&bytes[..]).expect("a readable object");
1122        let plain = file.symbols().find(|s| s.name() == Ok("plain")).expect("the label");
1123        assert_eq!(plain.address(), 4);
1124        assert_eq!(st_info(&bytes, "plain") & 0xf, elf::STT_NOTYPE.0);
1125    }
1126
1127    #[test]
1128    fn what_type_said_is_what_the_symbol_gets() {
1129        let input = Assembled {
1130            parts: vec![part(".text", vec![0; 8])],
1131            names: vec![
1132                at("run", 0, Sort::Func, Binding::Global),
1133                at("held", 4, Sort::Object, Binding::Local),
1134            ],
1135            subsections: false,
1136        };
1137        let bytes = assembled(&input, &target()).expect("an object");
1138        assert_eq!(st_info(&bytes, "run") & 0xf, elf::STT_FUNC.0);
1139        assert_eq!(st_info(&bytes, "held") & 0xf, elf::STT_OBJECT.0);
1140        assert_eq!(st_info(&bytes, "run") >> 4, elf::STB_GLOBAL.0);
1141        assert_eq!(st_info(&bytes, "held") >> 4, elf::STB_LOCAL.0);
1142    }
1143
1144    #[test]
1145    fn a_common_symbol_is_written_the_way_gas_writes_one() {
1146        // The writer underneath records `STT_COMMON` and gas records `STT_OBJECT` for the same
1147        // `.comm`. Both are a request for storage and a linker takes either, and the one gas writes
1148        // is the one written here, so an object of ours and an object of theirs do not differ in a
1149        // field somebody's tool reads years from now.
1150        let input = Assembled {
1151            parts: Vec::new(),
1152            names: vec![Name {
1153                name: "shared".to_owned(),
1154                at: Held::Common { size: 8, align: 8 },
1155                size: 0,
1156                sort: Sort::Object,
1157                binding: Binding::Global,
1158                visibility: Visibility::Default,
1159            }],
1160            subsections: false,
1161        };
1162        let bytes = assembled(&input, &target()).expect("an object");
1163        assert_eq!(st_info(&bytes, "shared"), elf::STB_GLOBAL.0 << 4 | elf::STT_OBJECT.0);
1164        let file = object::File::parse(&bytes[..]).expect("a readable object");
1165        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the symbol");
1166        assert!(shared.is_common(), "the linker has to be asked for the space");
1167        assert_eq!(shared.size(), 8);
1168        // Where an ordinary symbol keeps its address, which is why the two cannot both be said.
1169        assert_eq!(raw(&bytes, "shared").1, 8, "the boundary it has to start on");
1170    }
1171
1172    #[test]
1173    fn a_set_is_a_number_rather_than_a_place() {
1174        let input = Assembled {
1175            parts: vec![part(".text", vec![0; 8])],
1176            names: vec![Name {
1177                name: "size_of_it".to_owned(),
1178                at: Held::Absolute(25),
1179                size: 0,
1180                sort: Sort::Untyped,
1181                binding: Binding::Global,
1182                visibility: Visibility::Default,
1183            }],
1184            subsections: false,
1185        };
1186        let bytes = assembled(&input, &target()).expect("an object");
1187        let file = object::File::parse(&bytes[..]).expect("a readable object");
1188        let sym = file.symbols().find(|s| s.name() == Ok("size_of_it")).expect("the symbol");
1189        assert_eq!(sym.address(), 25);
1190        assert_eq!(sym.section(), object::SymbolSection::Absolute, "it is not in any section");
1191    }
1192
1193    #[test]
1194    fn a_relocation_names_a_symbol_and_lands_where_the_bytes_are() {
1195        let mut data = part(".data", vec![0; 8]);
1196        data.relocs.push(Reloc {
1197            at: 0,
1198            symbol: "message".to_owned(),
1199            kind: Reference::Address { bytes: 8 },
1200            addend: 0,
1201            after: 0,
1202        });
1203        let input = Assembled {
1204            parts: vec![data],
1205            names: vec![Name {
1206                name: "message".to_owned(),
1207                at: Held::Undefined,
1208                size: 0,
1209                sort: Sort::Untyped,
1210                binding: Binding::Global,
1211                visibility: Visibility::Default,
1212            }],
1213            subsections: false,
1214        };
1215        let bytes = assembled(&input, &target()).expect("an object");
1216        let file = object::File::parse(&bytes[..]).expect("a readable object");
1217        let section = file.section_by_name(".data").expect("the section");
1218        let (at, reloc) = section.relocations().next().expect("one relocation");
1219        assert_eq!(at, 0);
1220        assert_eq!(reloc.addend(), 0);
1221        let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("an ELF file") };
1222        assert_eq!(r_type, elf::R_X86_64_64);
1223    }
1224
1225    #[test]
1226    fn a_place_only_this_file_sees_is_reached_through_its_section_as_gas_does() {
1227        // The `.L` label goes, the static function stays in the table, and both relocations are
1228        // against `.text` at their offsets. A call keeps its name, since the linker may give it a
1229        // stub, and so does a name the linker is allowed to see.
1230        let mut text = part(".text", vec![0; 32]);
1231        for (at, symbol, kind) in [
1232            (0, ".L3", Reference::Data),
1233            (4, "helper", Reference::Data),
1234            (8, "helper", Reference::Call),
1235            (12, "shared", Reference::Data),
1236        ] {
1237            let symbol = symbol.to_owned();
1238            text.relocs.push(Reloc { at, symbol, kind, addend: -4, after: 0 });
1239        }
1240        let input = Assembled {
1241            parts: vec![text],
1242            names: vec![
1243                at(".L3", 20, Sort::Untyped, Binding::Local),
1244                at("helper", 24, Sort::Func, Binding::Local),
1245                at("shared", 28, Sort::Func, Binding::Global),
1246            ],
1247            subsections: false,
1248        };
1249        let bytes = assembled(&input, &target()).expect("an object");
1250        let file = object::File::parse(&bytes[..]).expect("a readable object");
1251        let names: Vec<_> = file.symbols().filter_map(|sym| sym.name().ok()).collect();
1252        assert!(!names.contains(&".L3") && names.contains(&"helper"), "{names:?}");
1253        let section = file.section_by_name(".text").expect("the section");
1254        let reached: Vec<_> = section
1255            .relocations()
1256            .map(|(at, reloc)| {
1257                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1258                    panic!("a symbol")
1259                };
1260                let symbol = file.symbol_by_index(index).expect("the symbol");
1261                let name = if symbol.kind() == SymbolKind::Section {
1262                    ".text"
1263                } else {
1264                    symbol.name().expect("a name")
1265                };
1266                (at, name, reloc.addend())
1267            })
1268            .collect();
1269        assert_eq!(
1270            reached,
1271            [(0, ".text", 16), (4, ".text", 20), (8, "helper", -4), (12, "shared", -4)]
1272        );
1273    }
1274
1275    #[test]
1276    fn a_section_of_constants_may_be_merged_and_a_distance_into_it_keeps_its_name() {
1277        let mut text = part(".text", vec![0; 8]);
1278        text.relocs.push(Reloc {
1279            at: 0,
1280            symbol: ".LC0".to_owned(),
1281            kind: Reference::Data,
1282            addend: -4,
1283            after: 0,
1284        });
1285        let strings = Part {
1286            shape: Shape { merge: 1, strings: true, ..Shape::of(".rodata") },
1287            ..part(".rodata.str1.1", b"hi\0".to_vec())
1288        };
1289        let mut name = at(".LC0", 0, Sort::Untyped, Binding::Local);
1290        name.at = Held::In { part: 1, offset: 0 };
1291        let input = Assembled { parts: vec![text, strings], names: vec![name], subsections: false };
1292        let bytes = assembled(&input, &target()).expect("an object");
1293        let file = object::File::parse(&bytes[..]).expect("a readable object");
1294        let section = file.section_by_name(".rodata.str1.1").expect("the section");
1295        let SectionFlags::Elf { sh_flags, .. } = section.flags() else { panic!("an ELF file") };
1296        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_MERGE.0 | elf::SHF_STRINGS.0);
1297        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
1298        let endian = header.endian().expect("an endianness");
1299        let table = header.sections(endian, &bytes[..]).expect("the sections");
1300        let (_, found) = table.section_by_name(endian, b".rodata.str1.1").expect("the section");
1301        assert_eq!(found.sh_entsize.get(endian), 1);
1302        let text = file.section_by_name(".text").expect("the section");
1303        let (_, reloc) = text.relocations().next().expect("one relocation");
1304        let object::RelocationTarget::Symbol(index) = reloc.target() else { panic!("a symbol") };
1305        assert_eq!(file.symbol_by_index(index).and_then(|sym| sym.name()), Ok(".LC0"));
1306    }
1307
1308    #[test]
1309    fn a_relocation_against_a_name_the_file_never_mentions_is_refused() {
1310        // Rather than written against symbol zero, which is a file that links and resolves the
1311        // reference to address zero. The list of names is the whole of what the reader found, so a
1312        // relocation naming something outside it is a mistake in this compiler.
1313        let mut data = part(".data", vec![0; 8]);
1314        data.relocs.push(Reloc {
1315            at: 0,
1316            symbol: "nowhere".to_owned(),
1317            kind: Reference::Address { bytes: 8 },
1318            addend: 0,
1319            after: 0,
1320        });
1321        let input = Assembled { parts: vec![data], names: Vec::new(), subsections: false };
1322        let why = assembled(&input, &target()).expect_err("this cannot be written");
1323        assert!(format!("{why}").contains("nowhere"), "{why}");
1324    }
1325
1326    #[test]
1327    fn the_stack_is_marked_only_by_the_file_and_only_once() {
1328        // gas adds no marker the file did not write unless `--noexecstack` asks, and that is the
1329        // reader's to do, so a file of parts gets exactly the ones it has.
1330        let bare = Assembled {
1331            parts: vec![part(".text", vec![0x90])],
1332            names: Vec::new(),
1333            subsections: false,
1334        };
1335        let bytes = assembled(&bare, &target()).expect("an object");
1336        let file = object::File::parse(&bytes[..]).expect("a readable object");
1337        assert!(file.section_by_name(".note.GNU-stack").is_none(), "a marker nobody wrote");
1338
1339        let said = Assembled {
1340            parts: vec![part(".text", vec![0x90]), part(".note.GNU-stack", Vec::new())],
1341            names: Vec::new(),
1342            subsections: false,
1343        };
1344        let bytes = assembled(&said, &target()).expect("an object");
1345        let file = object::File::parse(&bytes[..]).expect("a readable object");
1346        let marks = file.sections().filter(|s| s.name() == Ok(".note.GNU-stack")).count();
1347        assert_eq!(marks, 1, "the file said it and it was said again");
1348    }
1349
1350    #[test]
1351    fn only_the_names_a_linker_could_find_are_offered_to_an_archive() {
1352        let input = Assembled {
1353            parts: vec![part(".text", vec![0; 8])],
1354            names: vec![
1355                at("reachable", 0, Sort::Func, Binding::Global),
1356                at("mine", 4, Sort::Func, Binding::Local),
1357                Name {
1358                    name: "elsewhere".to_owned(),
1359                    at: Held::Undefined,
1360                    size: 0,
1361                    sort: Sort::Untyped,
1362                    binding: Binding::Global,
1363                    visibility: Visibility::Default,
1364                },
1365            ],
1366            subsections: false,
1367        };
1368        assert_eq!(assembled_defines(&input), vec!["reachable".to_owned()]);
1369    }
1370
1371    #[test]
1372    fn a_machine_this_does_not_write_is_refused_rather_than_written_wrong() {
1373        let input = Assembled {
1374            parts: vec![part(".text", vec![0x90])],
1375            names: Vec::new(),
1376            subsections: false,
1377        };
1378        let elsewhere = TargetInfo::new(Triple::new(TargetArch::Riscv64, Os::Linux, Env::Gnu));
1379        let why = assembled(&input, &elsewhere).expect_err("this cannot be written");
1380        assert!(format!("{why}").contains("riscv64"), "{why}");
1381    }
1382
1383    #[test]
1384    fn a_file_of_assembly_for_aarch64_is_written_with_that_machine_s_relocations() {
1385        // `adrp x0, table` and `add x0, x0, :lo12:table+8`, then `bl g`, then the address of
1386        // `table` in a table of its own. A field is its fixup's relocation and an address is the
1387        // AArch64 one of that width, and a label only this file sees is written against its
1388        // section, the way gas writes it.
1389        let mut text = part(".text", vec![0; 12]);
1390        let field = |at, symbol: &str, fixup, addend| Reloc {
1391            at,
1392            symbol: symbol.to_owned(),
1393            kind: Reference::Field(fixup),
1394            addend,
1395            after: 0,
1396        };
1397        text.relocs = vec![
1398            field(0, ".Ltable", Fixup::AdrPage21, 8),
1399            field(4, ".Ltable", Fixup::AddLo12, 8),
1400            field(8, "g", Fixup::Call26, 0),
1401        ];
1402        let mut data = part(".data", vec![0; 16]);
1403        data.relocs = vec![Reloc {
1404            at: 8,
1405            symbol: ".Ltable".to_owned(),
1406            kind: Reference::Address { bytes: 8 },
1407            addend: 0,
1408            after: 0,
1409        }];
1410        let mut table = at(".Ltable", 0, Sort::Object, Binding::Local);
1411        table.at = Held::In { part: 1, offset: 0 };
1412        let input = Assembled {
1413            parts: vec![text, data],
1414            names: vec![
1415                table,
1416                Name { at: Held::Undefined, ..at("g", 0, Sort::Untyped, Binding::Global) },
1417            ],
1418            subsections: false,
1419        };
1420        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Linux, Env::Gnu));
1421        let bytes = assembled(&input, &target).expect("an object");
1422        let file = object::File::parse(&bytes[..]).expect("a readable object");
1423        assert_eq!(file.architecture(), Architecture::Aarch64);
1424        let relocs = |name: &str| -> Vec<(u64, elf::RelocationType, i64)> {
1425            let section = file.section_by_name(name).expect("the section");
1426            section
1427                .relocations()
1428                .map(|(at, reloc)| {
1429                    let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1430                    (at, r_type, reloc.addend())
1431                })
1432                .collect()
1433        };
1434        assert_eq!(
1435            relocs(".text"),
1436            [
1437                (0, elf::R_AARCH64_ADR_PREL_PG_HI21, 8),
1438                (4, elf::R_AARCH64_ADD_ABS_LO12_NC, 8),
1439                (8, elf::R_AARCH64_CALL26, 0)
1440            ]
1441        );
1442        assert_eq!(relocs(".data"), [(8, elf::R_AARCH64_ABS64, 0)]);
1443        assert!(file.symbols().all(|s| s.name() != Ok(".Ltable")), "a label only this file sees");
1444    }
1445
1446    #[test]
1447    fn a_file_of_assembly_for_aarch64_windows_carries_its_addends_in_the_instructions() {
1448        // The same instructions as the ELF test above, and a load of the eighth byte of the table,
1449        // a `bl` to a name, then the two halves of an offset into `.tls`. COFF has no addend field,
1450        // so the eight goes into the page `adrp` names, into the low twelve bits `add` carries and,
1451        // divided by the size of the access, into the offset of the load. A distance written as
1452        // data is `REL32`, which counts from the end of its four bytes, so four more is in them.
1453        let adrp = 0x9000_0000u32;
1454        let add = 0x9100_0000u32;
1455        let ldr = 0xf940_0000u32;
1456        let bl = 0x9400_0000u32;
1457        let words = [adrp, add, ldr, bl, add | 1 << 22, add];
1458        let mut text = part(".text", words.iter().flat_map(|word| word.to_le_bytes()).collect());
1459        let field = |at, symbol: &str, fixup, addend| Reloc {
1460            at,
1461            symbol: symbol.to_owned(),
1462            kind: Reference::Field(fixup),
1463            addend,
1464            after: 0,
1465        };
1466        text.relocs = vec![
1467            field(0, "table", Fixup::AdrPage21, 8),
1468            field(4, "table", Fixup::AddLo12, 8),
1469            field(8, "table", Fixup::Ldst64Lo12, 8),
1470            field(12, "g", Fixup::Call26, 0),
1471            field(16, "counter", Fixup::SecrelHigh12A, 0),
1472            field(20, "counter", Fixup::SecrelLow12A, 0),
1473        ];
1474        let mut data = part(".data", vec![0; 16]);
1475        data.relocs = vec![
1476            Reloc {
1477                at: 0,
1478                symbol: "table".to_owned(),
1479                kind: Reference::Address { bytes: 8 },
1480                addend: 8,
1481                after: 0,
1482            },
1483            Reloc { at: 8, symbol: "g".to_owned(), kind: Reference::Data, addend: 0, after: 0 },
1484            Reloc {
1485                at: 12,
1486                symbol: "table".to_owned(),
1487                kind: Reference::Image,
1488                addend: 0,
1489                after: 0,
1490            },
1491        ];
1492        let mut table = at("table", 0, Sort::Object, Binding::Global);
1493        table.at = Held::In { part: 1, offset: 0 };
1494        let undefined =
1495            |name| Name { at: Held::Undefined, ..at(name, 0, Sort::Untyped, Binding::Global) };
1496        let input = Assembled {
1497            parts: vec![text, data],
1498            names: vec![table, undefined("g"), undefined("counter")],
1499            subsections: false,
1500        };
1501        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1502        let bytes = assembled(&input, &target).expect("an object");
1503        let file = object::File::parse(&bytes[..]).expect("a readable object");
1504        assert_eq!(file.format(), object::BinaryFormat::Coff);
1505        assert_eq!(file.architecture(), Architecture::Aarch64);
1506        let relocs = |name: &str| -> Vec<(u64, u16, String)> {
1507            let section = file.section_by_name(name).expect("the section");
1508            section
1509                .relocations()
1510                .map(|(at, reloc)| {
1511                    let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
1512                    let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
1513                        panic!("a symbol")
1514                    };
1515                    let symbol = file.symbol_by_index(symbol).expect("the symbol");
1516                    (at, typ.0, symbol.name().expect("a name").to_owned())
1517                })
1518                .collect()
1519        };
1520        let named = |at, typ: pe::RelocationType, name: &str| (at, typ.0, name.to_owned());
1521        assert_eq!(
1522            relocs(".text"),
1523            [
1524                named(0, pe::IMAGE_REL_ARM64_PAGEBASE_REL21, "table"),
1525                named(4, pe::IMAGE_REL_ARM64_PAGEOFFSET_12A, "table"),
1526                named(8, pe::IMAGE_REL_ARM64_PAGEOFFSET_12L, "table"),
1527                named(12, pe::IMAGE_REL_ARM64_BRANCH26, "g"),
1528                named(16, pe::IMAGE_REL_ARM64_SECREL_HIGH12A, "counter"),
1529                named(20, pe::IMAGE_REL_ARM64_SECREL_LOW12A, "counter"),
1530            ]
1531        );
1532        assert_eq!(
1533            relocs(".data"),
1534            [
1535                named(0, pe::IMAGE_REL_ARM64_ADDR64, "table"),
1536                named(8, pe::IMAGE_REL_ARM64_REL32, "g"),
1537                named(12, pe::IMAGE_REL_ARM64_ADDR32NB, "table"),
1538            ]
1539        );
1540        let text = file.section_by_name(".text").expect("the section");
1541        let text = text.data().expect("the bytes");
1542        let word = |nth: usize| u32::from_le_bytes(text[nth * 4..nth * 4 + 4].try_into().unwrap());
1543        assert_eq!(word(0), adrp | 2 << 5, "eight bytes is immhi two and immlo nothing");
1544        assert_eq!(word(1), add | 8 << 10);
1545        assert_eq!(word(2), ldr | 1 << 10, "eight bytes is one doubleword");
1546        assert_eq!([word(3), word(4), word(5)], [bl, add | 1 << 22, add]);
1547        let data = file.section_by_name(".data").expect("the section");
1548        let data = data.data().expect("the bytes");
1549        assert_eq!(data[..8], 8u64.to_le_bytes());
1550        assert_eq!(data[8..12], 4u32.to_le_bytes());
1551    }
1552
1553    #[test]
1554    fn an_addend_an_aarch64_coff_field_cannot_carry_is_refused() {
1555        // A load of four bytes cannot be told to start two bytes in, since its offset is counted in
1556        // fours, and a branch to a name and a number is not something the field can say for every
1557        // linker. Both are refused rather than written as something close.
1558        for (word, fixup, addend) in
1559            [(0xb940_0000u32, Fixup::Ldst32Lo12, 2), (0x9400_0000, Fixup::Call26, 4)]
1560        {
1561            let mut text = part(".text", word.to_le_bytes().to_vec());
1562            text.relocs = vec![Reloc {
1563                at: 0,
1564                symbol: "g".to_owned(),
1565                kind: Reference::Field(fixup),
1566                addend,
1567                after: 0,
1568            }];
1569            let input = Assembled {
1570                parts: vec![text],
1571                names: vec![Name {
1572                    at: Held::Undefined,
1573                    ..at("g", 0, Sort::Untyped, Binding::Global)
1574                }],
1575                subsections: false,
1576            };
1577            let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Gnu));
1578            let why = assembled(&input, &target).expect_err("a field that cannot say it");
1579            assert!(format!("{why}").contains(fixup.name()), "{why}");
1580        }
1581    }
1582
1583    #[test]
1584    fn a_file_of_assembly_for_windows_is_written_as_coff() {
1585        // What tamnd/rucc#1514 was about. `runtime/builtins/chkstk.S` is a file of assembly for a
1586        // Windows target, and until this it was refused with a message about there being no object
1587        // writer for the triple, which read as the whole back end being missing rather than this
1588        // one path through it.
1589        let input = Assembled {
1590            parts: vec![part(".text", vec![0xc3])],
1591            names: Vec::new(),
1592            subsections: false,
1593        };
1594        let bytes = assembled(&input, &windows()).expect("an object");
1595        let file = object::File::parse(&bytes[..]).expect("a readable object");
1596        assert_eq!(file.format(), object::BinaryFormat::Coff);
1597        let section = file.section_by_name(".text").expect("the section");
1598        assert_eq!(section.data().expect("the bytes"), &[0xc3]);
1599        assert_eq!(section.kind(), SectionKind::Text);
1600        assert!(
1601            file.section_by_name(".note.GNU-stack").is_none(),
1602            "a format with no marker got one anyway"
1603        );
1604    }
1605
1606    #[test]
1607    fn a_coff_section_keeps_the_letters_it_was_given_and_its_comdat() {
1608        // `.section .drectve,"yni"` in chkstk.S, and the `.refptr.` pointers mingw-w64 wants one
1609        // copy of across every object that has one.
1610        let drectve = Part {
1611            shape: Shape::coff("yni").expect("the letters"),
1612            ..part(".drectve", b" -exclude-symbols:f".to_vec())
1613        };
1614        let refptr = Part {
1615            shape: Shape::coff("dr").expect("the letters"),
1616            group: Some(Group { symbol: ".refptr.x".to_owned(), keep: Keep::Any }),
1617            ..part(".rdata$.refptr.x", vec![0; 8])
1618        };
1619        let input = Assembled {
1620            parts: vec![refptr, drectve],
1621            names: vec![at(".refptr.x", 0, Sort::Object, Binding::Global)],
1622            subsections: false,
1623        };
1624        let bytes = assembled(&input, &windows()).expect("an object");
1625        let file = object::File::parse(&bytes[..]).expect("a readable object");
1626        let flags = |name: &str| match file.section_by_name(name).expect("the section").flags() {
1627            SectionFlags::Coff { characteristics } => characteristics.0,
1628            other => panic!("{other:?}"),
1629        };
1630        let removed = pe::IMAGE_SCN_LNK_REMOVE.0 | pe::IMAGE_SCN_LNK_INFO.0;
1631        assert_eq!(flags(".drectve") & removed, removed);
1632        assert_eq!(flags(".drectve") & (pe::IMAGE_SCN_MEM_READ.0 | pe::IMAGE_SCN_MEM_WRITE.0), 0);
1633        let refptr = flags(".rdata$.refptr.x");
1634        assert_ne!(refptr & pe::IMAGE_SCN_LNK_COMDAT.0, 0, "not a COMDAT");
1635        assert_eq!(refptr & pe::IMAGE_SCN_MEM_WRITE.0, 0, "`r` did not take writing away");
1636        let comdat = file.comdats().next().expect("the COMDAT");
1637        assert_eq!(comdat.kind(), object::ComdatKind::Any);
1638        assert_eq!(file.symbol_by_index(comdat.symbol()).unwrap().name(), Ok(".refptr.x"));
1639
1640        // And one about a name the file never defines is refused rather than written broken.
1641        let input = Assembled { names: Vec::new(), ..input };
1642        assert!(assembled(&input, &windows()).is_err());
1643    }
1644
1645    #[test]
1646    fn a_global_label_with_no_type_under_it_is_still_offered_on_coff() {
1647        // The case a `.globl` and a label is, which is most of what a hand written file says. On
1648        // ELF that is `STT_NOTYPE` and the binding is a separate field, so the name is global
1649        // whatever its type. COFF has no such split: what the writer underneath calls a label is
1650        // storage class `LABEL`, which is a name inside one file, and a symbol written that way is
1651        // one no linker resolves against. `___chkstk_ms` came out of the archive as a local under
1652        // that mapping and mingw-w64's own objects went on wanting it.
1653        let input = Assembled {
1654            parts: vec![part(".text", vec![0; 8])],
1655            names: vec![
1656                at("offered", 0, Sort::Untyped, Binding::Global),
1657                at("ours", 4, Sort::Untyped, Binding::Local),
1658            ],
1659            subsections: false,
1660        };
1661        let bytes = assembled(&input, &windows()).expect("an object");
1662        let file = object::File::parse(&bytes[..]).expect("a readable object");
1663        let offered = file.symbols().find(|s| s.name() == Ok("offered")).expect("the label");
1664        assert!(offered.is_global(), "a `.globl` label came out local");
1665        let ours = file.symbols().find(|s| s.name() == Ok("ours")).expect("the other label");
1666        assert!(!ours.is_global(), "a label nothing offered came out global");
1667        // And the same input on ELF is still what gas writes there, which is the half of this that
1668        // would otherwise have been changed to fix the other half.
1669        let bytes = assembled(&input, &target()).expect("an object");
1670        assert_eq!(st_info(&bytes, "offered") & 0xf, elf::STT_NOTYPE.0);
1671    }
1672
1673    #[test]
1674    fn a_relocation_on_coff_says_how_much_of_the_instruction_comes_after_it() {
1675        // The one real difference between the two formats' relocations. ELF folds the distance
1676        // between the hole and the end of the instruction into the addend and has one type. COFF
1677        // counts from the end of the instruction and has no addend field, so the count is in the
1678        // type: `IMAGE_REL_AMD64_REL32_4` is four bytes of immediate behind the displacement.
1679        let mut text = part(".text", vec![0; 16]);
1680        text.relocs.push(Reloc {
1681            at: 2,
1682            symbol: "elsewhere".to_owned(),
1683            kind: Reference::Data,
1684            addend: -8,
1685            after: 4,
1686        });
1687        let input = Assembled {
1688            parts: vec![text],
1689            names: vec![Name {
1690                name: "elsewhere".to_owned(),
1691                at: Held::Undefined,
1692                size: 0,
1693                sort: Sort::Untyped,
1694                binding: Binding::Global,
1695                visibility: Visibility::Default,
1696            }],
1697            subsections: false,
1698        };
1699        let bytes = assembled(&input, &windows()).expect("an object");
1700        let file = object::File::parse(&bytes[..]).expect("a readable object");
1701        let section = file.section_by_name(".text").expect("the section");
1702        let (at, reloc) = section.relocations().next().expect("the relocation");
1703        assert_eq!(at, 2);
1704        assert_eq!(
1705            reloc.flags(),
1706            RelocationFlags::Coff { typ: pe::RelocationType(pe::IMAGE_REL_AMD64_REL32.0 + 4) }
1707        );
1708    }
1709
1710    /// A linux i386 target, which is written as a 32 bit ELF file with REL relocations.
1711    fn i386() -> TargetInfo {
1712        TargetInfo::new(Triple::new(TargetArch::X86, Os::Linux, Env::Gnu))
1713    }
1714
1715    /// Every relocation of one section of an i386 file, as the offset it is at, its type, the
1716    /// symbol it names, and the addend the bytes it covers hold.
1717    ///
1718    /// The reader reports an addend of nothing for a REL file and says the real one is implicit,
1719    /// so what is added is read out of the section, which is where the linker reads it too.
1720    fn implicit(file: &object::File<'_>, section: &str) -> Vec<(u64, u32, String, i64)> {
1721        let section = file.section_by_name(section).expect("the section");
1722        let data = section.data().expect("the bytes");
1723        section
1724            .relocations()
1725            .map(|(at, reloc)| {
1726                assert!(reloc.has_implicit_addend(), "a REL file keeps the addend in the bytes");
1727                assert_eq!(reloc.addend(), 0);
1728                let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1729                let width = crate::elf::width_i386(r_type).expect("a width");
1730                let at_ = at as usize;
1731                let mut word = [0u8; 8];
1732                word[..width].copy_from_slice(&data[at_..at_ + width]);
1733                // Sign extended from however many bytes it is, since a distance is negative as
1734                // often as not.
1735                let shift = 64 - 8 * width as u32;
1736                let addend = (i64::from_le_bytes(word) << shift) >> shift;
1737                let object::RelocationTarget::Symbol(index) = reloc.target() else {
1738                    panic!("a relocation against a symbol")
1739                };
1740                let symbol = file.symbol_by_index(index).expect("the symbol");
1741                let name = match symbol.kind() {
1742                    SymbolKind::Section => {
1743                        let section = symbol.section_index().expect("a section symbol's section");
1744                        file.section_by_index(section)
1745                            .expect("it")
1746                            .name()
1747                            .expect("a name")
1748                            .to_owned()
1749                    }
1750                    _ => symbol.name().expect("a name").to_owned(),
1751                };
1752                (at, r_type.0, name, addend)
1753            })
1754            .collect()
1755    }
1756
1757    /// The relocations gas writes for the same instructions and data, with the addends where gas
1758    /// puts them.
1759    ///
1760    /// The code is what `gcc -m32 -fPIC` makes of a function that calls through the PLT, finds the
1761    /// global offset table, reaches a string of its own and a variable of someone else's, plus a
1762    /// plain call and an absolute address, which is what code that is not position independent
1763    /// writes. The data is an address at each width and a distance.
1764    #[test]
1765    fn an_i386_object_is_32_bit_elf_with_the_addends_in_the_bytes() {
1766        let reloc = |at, symbol: &str, kind, addend| Reloc {
1767            at,
1768            symbol: symbol.to_owned(),
1769            kind,
1770            addend,
1771            after: 0,
1772        };
1773        let code = vec![
1774            0xe8, 0, 0, 0, 0, // call foo@PLT
1775            0xe8, 0, 0, 0, 0, // call bar
1776            0x81, 0xc3, 0, 0, 0, 0, // addl $_GLOBAL_OFFSET_TABLE_, %ebx
1777            0x8d, 0x83, 0, 0, 0, 0, // leal .LC0@GOTOFF(%ebx), %eax
1778            0x8b, 0x83, 0, 0, 0, 0, // movl foo@GOT(%ebx), %eax
1779            0x89, 0x83, 0, 0, 0, 0, // movl %eax, foo@GOT(%ebx)
1780            0xa1, 0, 0, 0, 0, // movl counter+8, %eax
1781        ];
1782        let mut text = part(".text", code);
1783        text.relocs = vec![
1784            reloc(1, "foo", Reference::Call, -4),
1785            reloc(6, "bar", Reference::Data, -4),
1786            reloc(12, "_GLOBAL_OFFSET_TABLE_", Reference::GotFront, 2),
1787            reloc(18, ".LC0", Reference::GotOffset, 0),
1788            reloc(24, "foo", Reference::Slot, 0),
1789            reloc(30, "foo", Reference::SlotKept, 0),
1790            reloc(35, "counter", Reference::Signed, 8),
1791        ];
1792        let rodata = part(".rodata", b"abc\0hi\0\0".to_vec());
1793        let mut data = part(".data", vec![0; 11]);
1794        data.relocs = vec![
1795            reloc(0, "foo", Reference::Address { bytes: 4 }, 16),
1796            reloc(4, "bar", Reference::Away, 0),
1797            reloc(8, "foo", Reference::Address { bytes: 2 }, 0),
1798            reloc(10, "foo", Reference::Address { bytes: 1 }, 0),
1799        ];
1800        let undefined = |name: &str| Name {
1801            at: Held::Undefined,
1802            ..at(name, 0, Sort::Untyped, Binding::Global)
1803        };
1804        let names = vec![
1805            at("f", 0, Sort::Func, Binding::Global),
1806            Name {
1807                at: Held::In { part: 1, offset: 4 },
1808                ..at(".LC0", 0, Sort::Untyped, Binding::Local)
1809            },
1810            undefined("foo"),
1811            undefined("bar"),
1812            undefined("counter"),
1813            undefined("_GLOBAL_OFFSET_TABLE_"),
1814        ];
1815        let input = Assembled { parts: vec![text, rodata, data], names, subsections: false };
1816        let bytes = assembled(&input, &i386()).expect("an object");
1817
1818        let header = elf::FileHeader32::<Endianness>::parse(&bytes[..]).expect("a 32 bit header");
1819        let endian = header.endian().expect("an endianness");
1820        assert_eq!(bytes[4], elf::ELFCLASS32.0);
1821        assert_eq!(header.e_machine(endian), elf::EM_386);
1822        assert_eq!(header.e_type(endian), elf::ET_REL);
1823
1824        let file = object::File::parse(&bytes[..]).expect("a readable object");
1825        assert!(!file.is_64());
1826        assert_eq!(file.architecture(), Architecture::I386);
1827        for name in [".rel.text", ".rel.data"] {
1828            let section = file.section_by_name(name).expect("a REL section");
1829            let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("ELF") };
1830            assert_eq!(sh_type, elf::SHT_REL, "{name}");
1831        }
1832        assert!(file.section_by_name(".rela.text").is_none(), "i386 has no addend field");
1833
1834        let text = implicit(&file, ".text");
1835        let want = [
1836            (1, elf::R_386_PLT32, "foo", -4),
1837            (6, elf::R_386_PC32, "bar", -4),
1838            (12, elf::R_386_GOTPC, "_GLOBAL_OFFSET_TABLE_", 2),
1839            // A label only this file sees is its section and how far into it, as gas writes it.
1840            (18, elf::R_386_GOTOFF, ".rodata", 4),
1841            (24, elf::R_386_GOT32X, "foo", 0),
1842            (30, elf::R_386_GOT32, "foo", 0),
1843            (35, elf::R_386_32, "counter", 8),
1844        ];
1845        let want: Vec<_> = want
1846            .into_iter()
1847            .map(|(at, r_type, name, addend)| (at, r_type.0, name.to_owned(), addend))
1848            .collect();
1849        assert_eq!(text, want);
1850
1851        let data = implicit(&file, ".data");
1852        let want = [
1853            (0, elf::R_386_32, "foo", 16),
1854            (4, elf::R_386_PC32, "bar", 0),
1855            (8, elf::R_386_16, "foo", 0),
1856            (10, elf::R_386_8, "foo", 0),
1857        ];
1858        let want: Vec<_> = want
1859            .into_iter()
1860            .map(|(at, r_type, name, addend)| (at, r_type.0, name.to_owned(), addend))
1861            .collect();
1862        assert_eq!(data, want);
1863    }
1864
1865    /// Each way i386 reaches a thread-local variable is the relocation gas writes for its suffix,
1866    /// with what is added in the bytes and the variable's own name kept even though only this file
1867    /// sees it, which is what gas does for these where it would have named the section for any
1868    /// other reference.
1869    #[test]
1870    fn the_i386_thread_local_references_are_the_relocations_gas_writes() {
1871        let kinds = [
1872            (Tls::General, elf::R_386_TLS_GD),
1873            (Tls::Module, elf::R_386_TLS_LDM),
1874            (Tls::InModule, elf::R_386_TLS_LDO_32),
1875            (Tls::Slot, elf::R_386_TLS_GOTIE),
1876            (Tls::SlotAddress, elf::R_386_TLS_IE),
1877            (Tls::SlotNegated, elf::R_386_TLS_IE_32),
1878            (Tls::Offset, elf::R_386_TLS_LE),
1879            (Tls::Negated, elf::R_386_TLS_LE_32),
1880        ];
1881        let mut text = part(".text", vec![0; 4 * kinds.len()]);
1882        text.relocs = kinds
1883            .iter()
1884            .enumerate()
1885            .map(|(n, &(tls, _))| Reloc {
1886                at: 4 * n,
1887                symbol: "x".to_owned(),
1888                kind: Reference::Tls(tls),
1889                addend: n as i64,
1890                after: 0,
1891            })
1892            .collect();
1893        let tdata = part(".tdata", vec![0; 8]);
1894        let names = vec![
1895            at("f", 0, Sort::Func, Binding::Global),
1896            Name {
1897                at: Held::In { part: 1, offset: 4 },
1898                ..at("x", 0, Sort::Thread, Binding::Local)
1899            },
1900        ];
1901        let input = Assembled { parts: vec![text, tdata], names, subsections: false };
1902        let bytes = assembled(&input, &i386()).expect("an object");
1903        let file = object::File::parse(&bytes[..]).expect("a readable object");
1904        let want: Vec<_> = kinds
1905            .iter()
1906            .enumerate()
1907            .map(|(n, &(_, r_type))| (4 * n as u64, r_type.0, "x".to_owned(), n as i64))
1908            .collect();
1909        assert_eq!(implicit(&file, ".text"), want);
1910    }
1911
1912    /// A file of assembly for i386 on Windows is COFF with the i386 relocations, and its names are
1913    /// what the file spelled: a listing for that platform already has the underscore on every C
1914    /// name, so nothing is put in front of `_main`, `_puts` or a `__fastcall` `@f@8`.
1915    #[test]
1916    fn an_i386_windows_file_of_assembly_keeps_its_names_as_written() {
1917        use object::pe::{
1918            IMAGE_REL_I386_DIR32, IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_REL32,
1919            IMAGE_REL_I386_SECREL,
1920        };
1921        let reloc = |at, symbol: &str, kind, addend| Reloc {
1922            at,
1923            symbol: symbol.to_owned(),
1924            kind,
1925            addend,
1926            after: 0,
1927        };
1928        let code = vec![
1929            0xe8, 0, 0, 0, 0, // call _puts
1930            0xe8, 0, 0, 0, 0, // call @f@8
1931            0xa1, 0, 0, 0, 0, // movl _counter+8, %eax
1932            0xc3,
1933        ];
1934        let mut text = part(".text", code);
1935        text.relocs = vec![
1936            reloc(1, "_puts", Reference::Call, -4),
1937            reloc(6, "@f@8", Reference::Call, -4),
1938            reloc(11, "_counter", Reference::Address { bytes: 4 }, 8),
1939        ];
1940        let mut data = part(".data", vec![0; 12]);
1941        data.relocs = vec![
1942            reloc(0, "_main", Reference::Image, 0),
1943            reloc(4, "_main", Reference::Section, 0),
1944            reloc(8, "_puts", Reference::Away, 0),
1945        ];
1946        let undefined = |name: &str| Name {
1947            at: Held::Undefined,
1948            ..at(name, 0, Sort::Untyped, Binding::Global)
1949        };
1950        let names = vec![
1951            at("_main", 0, Sort::Func, Binding::Global),
1952            undefined("_puts"),
1953            undefined("@f@8"),
1954            undefined("_counter"),
1955        ];
1956        let input = Assembled { parts: vec![text, data], names, subsections: false };
1957        let target = TargetInfo::new(Triple::new(TargetArch::X86, Os::Windows, Env::Gnu));
1958        let bytes = assembled(&input, &target).expect("an object");
1959        let file = object::File::parse(&bytes[..]).expect("a readable object");
1960        assert_eq!(file.format(), object::BinaryFormat::Coff);
1961        assert_eq!(file.architecture(), Architecture::I386);
1962        for name in ["_main", "_puts", "@f@8", "_counter"] {
1963            assert!(file.symbol_by_name(name).is_some(), "{name}");
1964        }
1965        assert!(file.symbol_by_name("__main").is_none());
1966        let types = |name: &str| -> Vec<(u64, u16, String)> {
1967            let section = file.section_by_name(name).expect("a section");
1968            section
1969                .relocations()
1970                .map(|(at, reloc)| {
1971                    let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
1972                    let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
1973                        panic!("a symbol")
1974                    };
1975                    let symbol = file.symbol_by_index(symbol).expect("a symbol");
1976                    (at, typ.0, symbol.name().expect("a name").to_owned())
1977                })
1978                .collect()
1979        };
1980        let want = |list: &[(u64, pe::RelocationType, &str)]| -> Vec<(u64, u16, String)> {
1981            list.iter().map(|&(at, typ, name)| (at, typ.0, name.to_owned())).collect()
1982        };
1983        assert_eq!(
1984            types(".text"),
1985            want(&[
1986                (1, IMAGE_REL_I386_REL32, "_puts"),
1987                (6, IMAGE_REL_I386_REL32, "@f@8"),
1988                (11, IMAGE_REL_I386_DIR32, "_counter"),
1989            ])
1990        );
1991        assert_eq!(
1992            types(".data"),
1993            want(&[
1994                (0, IMAGE_REL_I386_DIR32NB, "_main"),
1995                (4, IMAGE_REL_I386_SECREL, "_main"),
1996                (8, IMAGE_REL_I386_REL32, "_puts"),
1997            ])
1998        );
1999        let code = file.section_by_name(".text").expect("a text section");
2000        let code = code.data().expect("the bytes");
2001        assert_eq!(&code[1..5], &0i32.to_le_bytes(), "a call counts from the end of its bytes");
2002        assert_eq!(&code[11..15], &8i32.to_le_bytes());
2003        let image = file.section_by_name(".data").expect("a data section");
2004        assert_eq!(&image.data().expect("the bytes")[8..12], &4i32.to_le_bytes());
2005    }
2006
2007    /// The labels gcc writes for i686 Windows start with a bare `L`, and they are left out of the
2008    /// table the way gas leaves them out, with whatever pointed at one pointing at its section and
2009    /// the distance into it added in the bytes.
2010    #[test]
2011    fn an_i386_windows_label_of_gccs_own_is_its_section_and_not_a_name() {
2012        use object::pe::{IMAGE_REL_I386_DIR32, IMAGE_REL_I386_REL32};
2013        // movl $LC1, (%esp) ; jmp L3 ; L3: ret
2014        let mut text = part(".text", vec![0xc7, 0x04, 0x24, 0, 0, 0, 0, 0xe9, 0, 0, 0, 0, 0xc3]);
2015        let reloc = |at, symbol: &str, kind, addend| Reloc {
2016            at,
2017            symbol: symbol.to_owned(),
2018            kind,
2019            addend,
2020            after: 0,
2021        };
2022        text.relocs = vec![
2023            reloc(3, "LC1", Reference::Address { bytes: 4 }, 0),
2024            reloc(8, "Lfar", Reference::Data, -4),
2025        ];
2026        let rdata = part(".rdata", vec![0; 8]);
2027        let names = vec![
2028            at("_f", 0, Sort::Func, Binding::Global),
2029            at("L3", 12, Sort::Untyped, Binding::Local),
2030            Name {
2031                at: Held::In { part: 1, offset: 4 },
2032                ..at("LC1", 0, Sort::Untyped, Binding::Local)
2033            },
2034            Name {
2035                at: Held::In { part: 1, offset: 0 },
2036                ..at("Lfar", 0, Sort::Untyped, Binding::Local)
2037            },
2038        ];
2039        let input = Assembled { parts: vec![text, rdata], names, subsections: false };
2040        let target = TargetInfo::new(Triple::new(TargetArch::X86, Os::Windows, Env::Gnu));
2041        let bytes = assembled(&input, &target).expect("an object");
2042        let file = object::File::parse(&bytes[..]).expect("a readable object");
2043        assert!(file.symbol_by_name("_f").is_some());
2044        for name in ["L3", "LC1", "Lfar"] {
2045            assert!(file.symbol_by_name(name).is_none(), "{name}");
2046        }
2047        let section = file.section_by_name(".text").expect("a section");
2048        let relocs: Vec<_> = section
2049            .relocations()
2050            .map(|(at, reloc)| {
2051                let RelocationFlags::Coff { typ } = reloc.flags() else { panic!("COFF") };
2052                let object::RelocationTarget::Symbol(symbol) = reloc.target() else {
2053                    panic!("a symbol")
2054                };
2055                let symbol = file.symbol_by_index(symbol).expect("a symbol");
2056                (at, typ.0, symbol.name().expect("a name").to_owned())
2057            })
2058            .collect();
2059        assert_eq!(
2060            relocs,
2061            vec![
2062                (3, IMAGE_REL_I386_DIR32.0, ".rdata".to_owned()),
2063                (8, IMAGE_REL_I386_REL32.0, ".rdata".to_owned())
2064            ]
2065        );
2066        let code = section.data().expect("the bytes");
2067        assert_eq!(&code[3..7], &4i32.to_le_bytes(), "the distance into .rdata");
2068        assert_eq!(&code[8..12], &0i32.to_le_bytes(), "the start of .rdata, counted from the end");
2069    }
2070
2071    /// What i386 has no relocation for is refused rather than written as the nearest thing.
2072    ///
2073    /// An address in eight bytes and a distance in eight are wider than anything this machine
2074    /// relocates, a load from the instruction pointer is something it cannot do, and the x86-64 slot
2075    /// of a thread-local variable is not how this machine reaches one.
2076    #[test]
2077    fn a_reference_i386_has_no_relocation_for_is_refused() {
2078        for kind in [
2079            Reference::Address { bytes: 8 },
2080            Reference::AwayWide,
2081            Reference::Got,
2082            Reference::GotBare,
2083            Reference::GotKept,
2084            Reference::Thread,
2085            Reference::Image,
2086            Reference::Section,
2087        ] {
2088            let mut text = part(".text", vec![0; 8]);
2089            text.relocs =
2090                vec![Reloc { at: 0, symbol: "foo".to_owned(), kind, addend: 0, after: 0 }];
2091            let names =
2092                vec![Name { at: Held::Undefined, ..at("foo", 0, Sort::Untyped, Binding::Global) }];
2093            let input = Assembled { parts: vec![text], names, subsections: false };
2094            let Err(Error::Refused { .. }) = assembled(&input, &i386()) else {
2095                panic!("{kind:?} was written for i386");
2096            };
2097        }
2098    }
2099
2100    /// An addend that does not fit in the bytes that have to hold it is refused, since a REL file
2101    /// has nowhere else to put the rest of it.
2102    #[test]
2103    fn an_i386_addend_too_wide_for_its_bytes_is_refused() {
2104        let mut data = part(".data", vec![0; 2]);
2105        let kind = Reference::Address { bytes: 1 };
2106        data.relocs = vec![Reloc { at: 0, symbol: "foo".to_owned(), kind, addend: 300, after: 0 }];
2107        let names =
2108            vec![Name { at: Held::Undefined, ..at("foo", 0, Sort::Untyped, Binding::Global) }];
2109        let input = Assembled { parts: vec![data], names, subsections: false };
2110        let Err(Error::Refused { why }) = assembled(&input, &i386()) else {
2111            panic!("an addend of 300 went into one byte");
2112        };
2113        assert!(why.contains("300"), "{why}");
2114    }
2115
2116    /// The header fields written into the finished bytes land where a 32 bit file keeps them,
2117    /// which is not where a 64 bit one does.
2118    #[test]
2119    fn a_32_bit_file_gets_its_entry_size_and_groups_in_the_right_place() {
2120        let mut strings = part(".rodata.str1.1", b"hi\0".to_vec());
2121        strings.shape.merge = 1;
2122        strings.shape.strings = true;
2123        let mut kept = part(".text.f", vec![0xc3]);
2124        kept.group = Some(Group { symbol: "f".to_owned(), keep: Keep::Together });
2125        let names = vec![Name {
2126            at: Held::In { part: 1, offset: 0 },
2127            ..at("f", 0, Sort::Func, Binding::Global)
2128        }];
2129        let input = Assembled { parts: vec![strings, kept], names, subsections: false };
2130        let bytes = assembled(&input, &i386()).expect("an object");
2131        let header = elf::FileHeader32::<Endianness>::parse(&bytes[..]).expect("a 32 bit header");
2132        let endian = header.endian().expect("an endianness");
2133        let sections = header.sections(endian, &bytes[..]).expect("the sections");
2134        let find = |name: &str| {
2135            sections
2136                .iter()
2137                .find(|section| sections.section_name(endian, section) == Ok(name.as_bytes()))
2138                .expect("the section")
2139        };
2140        assert_eq!(find(".rodata.str1.1").sh_entsize(endian), 1);
2141        let group = find(".group");
2142        let at = group.sh_offset(endian) as usize;
2143        assert_eq!(&bytes[at..at + 4], &[0; 4], "a group that is not a COMDAT says so");
2144    }
2145
2146    /// Two records of the same name, each going with the text of the function it is about. The
2147    /// linker refuses to put a section that goes with something beside one of the same name that
2148    /// does not, so both have to say it, and each has to say the right section.
2149    #[test]
2150    fn a_section_that_goes_with_a_name_points_at_the_section_the_name_is_in() {
2151        let record = |link: &str| Part {
2152            shape: Shape { write: true, ..Shape::of("__patchable_function_entries") },
2153            link: Some(link.to_owned()),
2154            ..part("__patchable_function_entries", vec![0; 8])
2155        };
2156        let parts = vec![
2157            part(".text", vec![0xc3]),
2158            part(".init.text", vec![0xc3]),
2159            record("f"),
2160            record("g"),
2161        ];
2162        let names = vec![
2163            Name { at: Held::In { part: 1, offset: 0 }, ..at("f", 0, Sort::Func, Binding::Global) },
2164            at("g", 0, Sort::Func, Binding::Global),
2165        ];
2166        let bytes = assembled(&Assembled { parts, names, subsections: false }, &target())
2167            .expect("an object");
2168        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
2169        let endian = header.endian().expect("an endianness");
2170        let sections = header.sections(endian, &bytes[..]).expect("the sections");
2171        let index = |name: &str| {
2172            sections
2173                .iter()
2174                .position(|section| sections.section_name(endian, section) == Ok(name.as_bytes()))
2175                .expect("the section") as u32
2176        };
2177        let records: Vec<_> = sections
2178            .iter()
2179            .filter(|section| {
2180                sections.section_name(endian, section) == Ok(&b"__patchable_function_entries"[..])
2181            })
2182            .collect();
2183        assert_eq!(records.len(), 2);
2184        for record in &records {
2185            assert!(
2186                record.sh_flags(endian).contains(elf::SHF_LINK_ORDER),
2187                "a record that goes with nothing"
2188            );
2189        }
2190        assert_eq!(records[0].sh_link(endian), index(".init.text"));
2191        assert_eq!(records[1].sh_link(endian), index(".text"));
2192    }
2193
2194    #[test]
2195    fn a_section_that_goes_with_a_name_in_no_section_is_refused() {
2196        let record = Part {
2197            link: Some("nowhere".to_owned()),
2198            ..part("__patchable_function_entries", vec![0; 8])
2199        };
2200        let input = Assembled { parts: vec![record], names: Vec::new(), subsections: false };
2201        assert!(assembled(&input, &target()).is_err());
2202    }
2203}