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, Symbol, SymbolSection};
31use object::{Architecture, Endianness, RelocationFlags, SectionKind, SymbolFlags, elf};
32use rucc_target::aarch64::Fixup;
33use rucc_target::{ObjectFormat, TargetInfo};
34use rucc_tuple::Arch;
35
36use crate::file::{Error, Flavour};
37use crate::section::{Array, Binding, Info, Reloc, Visibility};
38
39/// One section, as a file of assembly describes one.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Part {
42    /// What it is called, with the leading dot the source wrote.
43    pub name: String,
44    /// Its bytes, which are empty for a section that says how big it is and holds none of them.
45    pub bytes: Vec<u8>,
46    /// How long it is. The same as the length of the bytes for every section that has any, and the
47    /// whole of what a `@nobits` section says about itself.
48    pub size: u64,
49    /// The boundary it starts on, which is the largest any directive in it asked for.
50    pub align: u64,
51    /// The flags and the type, which the source states and this does not work out.
52    pub shape: Shape,
53    /// Every place in it that names something, counted from the start of the section.
54    pub relocs: Vec<Reloc>,
55}
56
57/// What a section is, which on ELF is a handful of flag letters and a type.
58///
59/// Held as the separate facts rather than as one of a fixed list of kinds, because the list is not
60/// fixed: a program may write `.section .init.text,"ax",@progbits` and mean a section this compiler
61/// has no name for, and the letters are the whole of what it said about it. The writer underneath
62/// takes a [`SectionKind`], so `Shape::kind` is the one place that turns these back into one, and
63/// the cases it cannot say are written as flags directly.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
65pub struct Shape {
66    /// `a`: the section takes space in the loaded image. A section without this is for a debugger
67    /// or a linker to read and is not in the program at run time.
68    pub alloc: bool,
69    /// `w`: the program may write to it.
70    pub write: bool,
71    /// `x`: the processor may execute it.
72    pub exec: bool,
73    /// `T`: one copy per thread rather than one copy per program.
74    pub thread: bool,
75    /// Whether the file carries the bytes. False is `@nobits`, which is what `.bss` is.
76    pub bits: bool,
77    /// Which kind of table of function addresses this is, for the three ELF has a type for.
78    pub array: Option<Array>,
79    /// `M`: how long each entry is in a section of constants the linker may keep one copy of
80    /// wherever two objects hold the same one, and zero for a section that is not one of those.
81    /// gcc puts a `double` it loads from memory in `.rodata.cst8`, which is one of these.
82    pub merge: u64,
83    /// `S`: the entries are strings ended by a zero rather than all of one length, which is where
84    /// gcc puts every string literal. Only means anything beside `merge`.
85    pub strings: bool,
86    /// The type and attributes of a Mach-O section, in the one word the format keeps them in,
87    /// which is what [`Shape::mach`] works out. Zero on the other two formats, where the fields
88    /// above are the whole answer, and zero is also an ordinary Mach-O section with nothing said.
89    pub mach: u32,
90}
91
92impl Shape {
93    /// What a section of this name is when the source named it and said nothing else.
94    ///
95    /// `.text`, `.data` and the rest are names an assembler already knows the flags of, which is
96    /// why a program may write `.data` on its own and why `.section .data` without letters is the
97    /// same section rather than an unallocated one. A name nothing here knows gets the flags of an
98    /// ordinary allocated writable section, which is what gas does with one.
99    #[must_use]
100    pub fn of(name: &str) -> Shape {
101        let base = Shape { alloc: true, bits: true, ..Shape::default() };
102        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
103        let head = head.split_once('.').map_or(head, |(first, _)| first);
104        match head {
105            "text" | "init" | "fini" => Shape { exec: true, ..base },
106            "rodata" | "eh_frame_hdr" => base,
107            "bss" => Shape { write: true, bits: false, ..base },
108            "tbss" => Shape { write: true, thread: true, bits: false, ..base },
109            "tdata" => Shape { write: true, thread: true, ..base },
110            // The three the linker gathers and the startup code walks. The type is what makes one
111            // of them that, rather than the name: a section of the ordinary type under the same
112            // name is gathered into the same run and called by nobody.
113            _ if Array::of(name).is_some() => Shape { write: true, array: Array::of(name), ..base },
114            // Not allocated, because nothing in the running program reads it. A debugger reads it
115            // out of the file, and a section marked allocated would take space in every process.
116            "debug_info" | "debug_abbrev" | "debug_line" | "debug_str" | "comment" => {
117                Shape { alloc: false, bits: true, ..Shape::default() }
118            }
119            _ => Shape { write: true, ..base },
120        }
121    }
122
123    /// The flags a section of this name has whatever letters the source gave it.
124    ///
125    /// gas adds these to the letters rather than taking the letters alone, so
126    /// `.section .data.rel.ro.local,"a"` is writable all the same. GMP names its jump tables that
127    /// way, and a linker making a position independent program refuses an address it would have to
128    /// fix up in a section it may not write. Only the names gas treats as a family are here, which
129    /// is fewer than [`Shape::of`] knows: `.init.data` is not executable just because `.init` is.
130    #[must_use]
131    pub fn implied(name: &str) -> Shape {
132        let base = Shape { alloc: true, ..Shape::default() };
133        let head = name.split_once('.').map_or(name, |(_, rest)| rest);
134        let head = head.split_once('.').map_or(head, |(first, _)| first);
135        match head {
136            "text" => Shape { exec: true, ..base },
137            "rodata" => base,
138            "data" | "bss" => Shape { write: true, ..base },
139            "tdata" | "tbss" => Shape { write: true, thread: true, ..base },
140            _ if Array::of(name).is_some() => Shape { write: true, ..base },
141            _ => Shape::default(),
142        }
143    }
144
145    /// What a Mach-O section is, from its segment, its section and the type and attributes a
146    /// `.section` directive gave after them.
147    ///
148    /// The word the format keeps is the answer and the fields beside it are filled in from it, so
149    /// that what reads a shape without knowing the format still sees code as code and a zero
150    /// filled section as one that holds no bytes.
151    ///
152    /// # Errors
153    ///
154    /// A type or an attribute Apple's assembler does not take, as a sentence.
155    pub fn mach(
156        segment: &str,
157        section: &str,
158        kind: Option<&str>,
159        attributes: &[&str],
160    ) -> Result<Shape, String> {
161        let mach = crate::macho::section_flags(segment, section, kind, attributes)?;
162        let exec = mach & object::macho::S_ATTR_PURE_INSTRUCTIONS.0 != 0;
163        let typ = object::macho::SectionFlags(mach).typ();
164        let thread = matches!(
165            typ,
166            object::macho::S_THREAD_LOCAL_REGULAR | object::macho::S_THREAD_LOCAL_ZEROFILL
167        );
168        Ok(Shape {
169            alloc: true,
170            write: segment != "__TEXT",
171            exec,
172            thread,
173            bits: !crate::macho::zero_filled(mach),
174            mach,
175            ..Shape::default()
176        })
177    }
178
179    /// The flag word ELF holds these in.
180    ///
181    /// Not public, and neither are the two below it. The fields above are the whole of what a
182    /// caller says about a section, and how ELF spells them is this crate's business: a reader that
183    /// had to name an ELF constant to describe an executable section would be one that could not
184    /// describe one for any other format.
185    pub(crate) fn sh_flags(self) -> elf::SectionFlags {
186        let mut flags = 0;
187        if self.alloc {
188            flags |= elf::SHF_ALLOC.0;
189        }
190        if self.write {
191            flags |= elf::SHF_WRITE.0;
192        }
193        if self.exec {
194            flags |= elf::SHF_EXECINSTR.0;
195        }
196        if self.thread {
197            flags |= elf::SHF_TLS.0;
198        }
199        if self.merge != 0 {
200            flags |= elf::SHF_MERGE.0;
201            if self.strings {
202                flags |= elf::SHF_STRINGS.0;
203            }
204        }
205        elf::SectionFlags(flags)
206    }
207
208    /// The type ELF holds in the header beside those flags.
209    pub(crate) fn sh_type(self) -> elf::SectionType {
210        match self.array {
211            _ if !self.bits => elf::SHT_NOBITS,
212            Some(Array::Init) => elf::SHT_INIT_ARRAY,
213            Some(Array::Fini) => elf::SHT_FINI_ARRAY,
214            Some(Array::Preinit) => elf::SHT_PREINIT_ARRAY,
215            None => elf::SHT_PROGBITS,
216        }
217    }
218
219    /// What the writer underneath calls the nearest thing to this.
220    ///
221    /// It is told the flags in full afterwards, so this only has to be close enough that nothing
222    /// else the writer decides from the kind comes out wrong, which is the default alignment and
223    /// whether it appends bytes or counts them.
224    pub(crate) const fn kind(self) -> SectionKind {
225        match self {
226            Shape { bits: false, thread: true, .. } => SectionKind::UninitializedTls,
227            Shape { bits: false, .. } => SectionKind::UninitializedData,
228            Shape { thread: true, .. } => SectionKind::Tls,
229            Shape { exec: true, .. } => SectionKind::Text,
230            Shape { alloc: false, .. } => SectionKind::Other,
231            Shape { write: false, .. } => SectionKind::ReadOnlyData,
232            Shape { .. } => SectionKind::Data,
233        }
234    }
235}
236
237/// One name in the symbol table, as a file of assembly defines one.
238#[derive(Debug, Clone, PartialEq, Eq)]
239pub struct Name {
240    /// The name, spelled as the source spelled it.
241    pub name: String,
242    /// Where it is.
243    pub at: Held,
244    /// How long the thing it names is, which is what `.size` said and is zero when nothing did.
245    pub size: u64,
246    /// What kind of thing it names, which is what `.type` said.
247    pub sort: Sort,
248    /// Who can see it.
249    pub binding: Binding,
250    /// How far outside a shared library it reaches.
251    pub visibility: Visibility,
252}
253
254/// Where a name is, which is four different things and not an offset with special cases.
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum Held {
257    /// At an offset into one of the sections, which is what a label is.
258    In {
259        /// Which section, as an index into the list given alongside.
260        part: usize,
261        /// How far into it.
262        offset: u64,
263    },
264    /// A number rather than a place, which is what `.set` and `.equ` produce. The linker resolves
265    /// a reference to one to the number itself and there is nothing for it to be relative to.
266    Absolute(u64),
267    /// That much zeroed space asked of the linker under this name, which is `.comm` and `.lcomm`.
268    /// Every definition of the name across every object is merged into one.
269    Common {
270        /// How much space.
271        size: u64,
272        /// What boundary it has to start on. ELF records this where an ordinary symbol records its
273        /// address, which is why the two cannot both be said.
274        align: u64,
275    },
276    /// Named and not defined here, which the linker has to find somewhere else.
277    Undefined,
278}
279
280/// What kind of thing a name names, which is what `.type` says.
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
282pub enum Sort {
283    /// `@function`. A call through the procedure linkage table may be made to it.
284    Func,
285    /// `@object`. Data.
286    Object,
287    /// `@tls_object`. A thread-local variable, which a linker checks relocations against.
288    Thread,
289    /// `.file`, which names the source this was assembled from rather than anything in it.
290    ///
291    /// Not a thing `.type` can say, and here because it is a symbol and there is nowhere else for
292    /// it. A debugger reads it and so does `nm`, and gas writes one for every file that says its
293    /// own name, which is every file gcc produces.
294    File,
295    /// Nothing was said, which is what a plain label gets and is a real answer rather than a
296    /// missing one: gas writes `STT_NOTYPE` for a label nobody stated a type for.
297    #[default]
298    Untyped,
299}
300
301/// Everything an assembled file holds: its sections, and the names that point into them.
302#[derive(Debug, Clone, Default, PartialEq, Eq)]
303pub struct Assembled {
304    /// The sections, in the order the file first mentioned each of them.
305    pub parts: Vec<Part>,
306    /// The names, in the order the file defined or first referred to each of them.
307    pub names: Vec<Name>,
308    /// Whether the file said `.subsections_via_symbols`, which tells a Mach-O linker it may cut
309    /// every section at every symbol in it. Nothing on the other two formats.
310    pub subsections: bool,
311}
312
313/// That, as a relocatable object in whichever of the two formats the target wants.
314///
315/// Both formats, the same two the module that writes a compilation writes it into, and the
316/// differences between them are the same answers there. That is the whole reason this is not two
317/// functions: a file of assembly names its own sections and a compilation does not, but what a
318/// relocation is called and whether a symbol has anywhere to keep a visibility are facts about the
319/// format rather than about where the bytes came from, and a second set of answers to them would
320/// be a second set to get wrong.
321///
322/// What a [`Part`] carries is the section type and flags the source wrote in as many words. ELF has
323/// a field for each of them and they are written down as they stand. COFF has no field they map
324/// onto, so what the section is comes from the kind on the shape and the writer underneath turns it
325/// into the characteristics every other Windows assembler writes. A program that means a Windows
326/// section to be something other than what its name says is a program that has to say so some other
327/// way, which is what `.section` with COFF's own letters is for and what tamnd/rucc#1514 left open.
328///
329/// # Errors
330///
331/// [`Error::Format`] for a machine or a platform this does not write, and [`Error::Refused`] for a
332/// relocation against a name the list does not hold or one this format has no relocation for.
333pub fn assembled(input: &Assembled, target: &TargetInfo) -> Result<Vec<u8>, Error> {
334    assembled_described(input, target, &Info::default())
335}
336
337/// The same object as [`assembled`], with the debug sections in `info` added to it.
338///
339/// For a compilation that went through a listing and asked for debug information, where the line
340/// table and the entries are built from the compilation rather than read from the file. A
341/// relocation in a chunk names another chunk or a name the file defines, and the second is written
342/// against the section the name is in, for the reason [`crate::write`] gives: a distance to a
343/// global name is not one a linker can work out.
344///
345/// # Errors
346///
347/// As for [`assembled`], and [`Error::Refused`] for a chunk that names something the file does
348/// not define.
349pub fn assembled_described(
350    input: &Assembled,
351    target: &TargetInfo,
352    info: &Info,
353) -> Result<Vec<u8>, Error> {
354    // AArch64 on ELF and Mach-O, and x86-64 on ELF and COFF. What an AArch64 file for Windows would
355    // need is a table of its own relocations and an unwind table of its own shape, and neither is
356    // written yet. Mach-O is a function of its own, since what it answers differently is most of
357    // what is below.
358    let (flavour, machine) = match (Flavour::of(target), target.tuple.arch()) {
359        (Some(flavour), Arch::X86_64) => (flavour, Architecture::X86_64),
360        (Some(Flavour::Elf), Arch::Aarch64) => (Flavour::Elf, Architecture::Aarch64),
361        (None, Arch::Aarch64) if target.object_format == ObjectFormat::MachO => {
362            return crate::macho::write(input, target, info);
363        }
364        _ => return Err(Error::Format { triple: target.tuple.to_string() }),
365    };
366    let flags_of = |kind, after| match machine {
367        Architecture::Aarch64 => {
368            crate::elf::r_type_aarch64(kind).map(|r_type| RelocationFlags::Elf { r_type })
369        }
370        _ => flavour.reloc(kind, after),
371    };
372    let mut obj = Writer::new(flavour.binary(), machine, Endianness::Little);
373
374    // Every section first, because a symbol says which one it is in and a relocation says which one
375    // it is written into, so both need the whole list before either can be added.
376    let mut made = Vec::with_capacity(input.parts.len());
377    for part in &input.parts {
378        let id = obj.add_section(Vec::new(), part.name.clone().into_bytes(), part.shape.kind());
379        // The flags in full rather than whatever the kind implied, because the kind is a summary of
380        // them and the source said them exactly. A section the program wrote `"ax"` on is executable
381        // whether or not its name is one this compiler would have made executable. Only where the
382        // format has the fields: see [`Flavour::stated`].
383        if let Some(flags) = flavour.stated(part.shape) {
384            obj.section_mut(id).flags = flags;
385        }
386        let align = part.align.max(1);
387        if part.shape.bits {
388            obj.append_section_data(id, &part.bytes, align);
389        } else {
390            obj.append_section_bss(id, part.size, align);
391        }
392        made.push(id);
393    }
394
395    // Which relocations point at the section a name is in rather than at the name, and which names
396    // are then asked for by nothing and left out, before either is written down.
397    let defined: std::collections::HashMap<&str, &Name> =
398        input.names.iter().map(|name| (name.name.as_str(), name)).collect();
399    let onto = |reloc: &Reloc| moved(flavour, input, &defined, reloc);
400    let wanted: std::collections::HashSet<&str> = input
401        .parts
402        .iter()
403        .flat_map(|part| &part.relocs)
404        .filter(|reloc| onto(reloc).is_none())
405        .map(|reloc| reloc.symbol.as_str())
406        .collect();
407
408    // Then every name. A relocation names one, and the writer wants the symbol before the
409    // relocation that points at it, so this whole pass is in front of the one below.
410    let mut symbols = std::collections::BTreeMap::new();
411    for name in &input.names {
412        if flavour == Flavour::Elf && unseen(name) && !wanted.contains(name.name.as_str()) {
413            continue;
414        }
415        let (section, value, size) = match name.at {
416            Held::In { part, offset } => {
417                let Some(id) = made.get(part) else {
418                    let why = format!(
419                        "'{}' is in section {part} and there is no such section",
420                        name.name
421                    );
422                    return Err(Error::Refused { why });
423                };
424                (SymbolSection::Section(*id), offset, name.size)
425            }
426            Held::Absolute(value) => (SymbolSection::Absolute, value, name.size),
427            // A common symbol says what it wants rather than where it is, and ELF records the
428            // boundary it wants where an ordinary symbol records its address.
429            Held::Common { size, align } => (SymbolSection::Common, align, size),
430            Held::Undefined => (SymbolSection::Undefined, 0, 0),
431        };
432        let id = obj.add_symbol(Symbol {
433            name: name.name.clone().into_bytes(),
434            value,
435            size,
436            kind: flavour.sort(name.sort, name.binding),
437            scope: crate::file::scope_of(name.binding),
438            weak: name.binding == Binding::Weak,
439            section,
440            flags: SymbolFlags::None,
441        });
442        flavour.see(&mut obj, id, name.binding, name.visibility);
443        // The writer underneath records a common symbol as `STT_COMMON` and gas records the same
444        // symbol as `STT_OBJECT`. Both are a request for storage and a linker reads either, and the
445        // one gas writes is written here, because an object that says the same thing a different
446        // way is the kind of difference that turns up years later in a tool that only ever saw the
447        // other one. A common symbol is global by definition, so there is no binding to preserve.
448        if matches!(name.at, Held::Common { .. }) {
449            if let SymbolFlags::Elf { st_info, .. } = obj.symbol_flags_mut(id) {
450                *st_info = elf::STB_GLOBAL | elf::STT_OBJECT;
451            }
452        }
453        symbols.insert(name.name.clone(), id);
454    }
455
456    for (part, id) in input.parts.iter().zip(&made) {
457        for reloc in &part.relocs {
458            let (symbol, addend) = match onto(reloc) {
459                Some((part, offset)) => {
460                    (obj.section_symbol(made[part]), reloc.addend + offset as i64)
461                }
462                None => {
463                    let Some(&symbol) = symbols.get(&reloc.symbol) else {
464                        let why = format!(
465                            "'{}' is named by a relocation and by nothing else",
466                            reloc.symbol
467                        );
468                        return Err(Error::Refused { why });
469                    };
470                    (symbol, reloc.addend)
471                }
472            };
473            let flags = flags_of(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
474                why: format!("no relocation is {:?}", reloc.kind),
475            })?;
476            obj.add_relocation(*id, Relocation { offset: reloc.at as u64, symbol, addend, flags })
477                .map_err(|why| Error::Refused { why: why.to_string() })?;
478        }
479    }
480
481    // The debug information, every section before any relocation because a relocation in one of
482    // them names another as often as it names a function.
483    let mut named = std::collections::HashMap::new();
484    for chunk in &info.chunks {
485        let id = obj.add_section(Vec::new(), chunk.name.clone().into_bytes(), SectionKind::Debug);
486        obj.append_section_data(id, &chunk.bytes, 1);
487        named.insert(chunk.name.as_str(), id);
488    }
489    for chunk in &info.chunks {
490        let section = named[chunk.name.as_str()];
491        for reloc in &chunk.relocs {
492            let (symbol, addend) = match named.get(reloc.symbol.as_str()) {
493                Some(&id) => (obj.section_symbol(id), reloc.addend),
494                None => match defined.get(reloc.symbol.as_str()).map(|name| name.at) {
495                    Some(Held::In { part, offset }) => {
496                        (obj.section_symbol(made[part]), reloc.addend + offset as i64)
497                    }
498                    _ => match symbols.get(&reloc.symbol) {
499                        Some(&symbol) => (symbol, reloc.addend),
500                        None => {
501                            let why = format!(
502                                "'{}' is named by the debug information and is not defined here",
503                                reloc.symbol
504                            );
505                            return Err(Error::Refused { why });
506                        }
507                    },
508                },
509            };
510            let flags = flags_of(reloc.kind, reloc.after).ok_or_else(|| Error::Refused {
511                why: format!("no relocation is {:?}", reloc.kind),
512            })?;
513            let record = Relocation { offset: reloc.at as u64, symbol, addend, flags };
514            obj.add_relocation(section, record)
515                .map_err(|why| Error::Refused { why: why.to_string() })?;
516        }
517    }
518
519    // The same marker every other object this compiler writes gets, and for the same reason: a
520    // linker that does not find it in every input marks the stack executable. Not a second one if
521    // the file already said it, which a file written by hand for a linker that cares often does,
522    // and nothing at all on a format whose answer to the question is in the finished image.
523    if !input.parts.iter().any(|part| part.name == ".note.GNU-stack") {
524        flavour.marker(&mut obj);
525    }
526
527    let mut bytes = obj.write().map_err(|why| Error::Refused { why: why.to_string() })?;
528    if flavour == Flavour::Elf {
529        for part in input.parts.iter().filter(|part| part.shape.merge != 0) {
530            entry_size(&mut bytes, &part.name, part.shape.merge);
531        }
532    }
533    Ok(bytes)
534}
535
536/// Write how long an entry of a mergeable section is into its header, which the linker needs and
537/// the writer underneath has no field for. It writes one only for a section of strings it made
538/// itself. The file is a 64 bit little endian ELF one, since that is the only kind this writes, and
539/// the section is found by its name, which is unique because the assembler gave every name one
540/// section.
541fn entry_size(bytes: &mut [u8], name: &str, size: u64) {
542    let word = |bytes: &[u8], at: usize, width: usize| {
543        bytes[at..at + width].iter().rev().fold(0u64, |sum, &byte| sum << 8 | u64::from(byte))
544    };
545    let table = word(bytes, 0x28, 8) as usize;
546    let each = word(bytes, 0x3a, 2) as usize;
547    let count = word(bytes, 0x3c, 2) as usize;
548    let names = table + each * word(bytes, 0x3e, 2) as usize;
549    let names = word(bytes, names + 0x18, 8) as usize;
550    for header in (0..count).map(|nth| table + nth * each) {
551        let at = names + word(bytes, header, 4) as usize;
552        if bytes[at..].starts_with(name.as_bytes()) && bytes.get(at + name.len()) == Some(&0) {
553            bytes[header + 0x38..header + 0x40].copy_from_slice(&size.to_le_bytes());
554        }
555    }
556}
557
558/// The section and the offset into it a relocation is written against in place of the name it
559/// gave, when gas would do the same.
560///
561/// A name only this file can see is a place in a section and nothing more, so gas writes the
562/// section's own symbol and how far into it the place is, and a `.L` label then has no reason to be
563/// in the table at all. It keeps the name where the linker has to see it: a call, which may go
564/// through a stub the linker makes for that name, a slot of the global offset table, and a place in
565/// a section the linker may merge, where the offset into the section is not an offset into the
566/// merged one. The last of those is only a problem for a distance, or for an address with
567/// something added to it, since the address of the start of a string is what the linker follows.
568fn moved(
569    flavour: Flavour,
570    input: &Assembled,
571    defined: &std::collections::HashMap<&str, &Name>,
572    reloc: &Reloc,
573) -> Option<(usize, u64)> {
574    use crate::section::Reference;
575    let name = defined.get(reloc.symbol.as_str())?;
576    let Held::In { part, offset } = name.at else { return None };
577    if flavour != Flavour::Elf || name.binding != Binding::Local {
578        return None;
579    }
580    let near = matches!(reloc.kind, Reference::Data | Reference::Away);
581    let fixed = match reloc.kind {
582        Reference::Call
583        | Reference::Got
584        | Reference::GotBare
585        | Reference::GotKept
586        | Reference::Thread => false,
587        // The same for a field of an instruction that goes through a stub or a table slot, or that
588        // says where a thread-local variable is, which a linker checks against the name's type.
589        Reference::Field(
590            Fixup::Call26
591            | Fixup::Jump26
592            | Fixup::GotPage21
593            | Fixup::GotLo12
594            | Fixup::GotTprelPage21
595            | Fixup::GotTprelLo12Nc
596            | Fixup::TprelHi12
597            | Fixup::TprelLo12Nc,
598        ) => false,
599        _ if input.parts.get(part)?.shape.merge != 0 => !near && reloc.addend == 0,
600        _ => true,
601    };
602    fixed.then_some((part, offset))
603}
604
605/// Whether a name is one the assembler made up or a label only it sees, which gas leaves out of the
606/// table unless a relocation still names it. `.L` is the prefix for those that ELF assemblers agree
607/// on, and a name with a `\u{1}` in it is one this assembler made for a numbered label or a frame.
608fn unseen(name: &Name) -> bool {
609    name.binding == Binding::Local
610        && (name.name.starts_with(".L")
611            || name.name.starts_with("..")
612            || name.name.contains('\u{1}'))
613}
614
615/// Every name in it a linker can find, which is what an archive's symbol index is built from.
616///
617/// The same rule as [`crate::defines`]: a local is left out, because a name the static link has
618/// already finished with is not one an archive may offer, and an undefined one is left out because
619/// this file does not have it.
620#[must_use]
621pub fn assembled_defines(input: &Assembled) -> Vec<String> {
622    input
623        .names
624        .iter()
625        .filter(|name| name.binding != Binding::Local && name.at != Held::Undefined)
626        .map(|name| name.name.clone())
627        .collect()
628}
629
630#[cfg(test)]
631mod tests {
632    use super::*;
633
634    use object::read::elf::{FileHeader as _, Sym as _};
635    use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
636    use object::{RelocationFlags, SectionFlags};
637    use rucc_target::{Arch as TargetArch, Env, Os, Triple};
638
639    use crate::section::Reference;
640
641    /// A linux x86-64 target, which is the one most of these are written against.
642    fn target() -> TargetInfo {
643        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Linux, Env::Gnu))
644    }
645
646    /// The same machine under mingw-w64, which is the target the COFF cases below are about.
647    fn windows() -> TargetInfo {
648        TargetInfo::new(Triple::new(TargetArch::X86_64, Os::Windows, Env::Gnu))
649    }
650
651    /// One section of that name holding those bytes, with the flags the name implies.
652    fn part(name: &str, bytes: Vec<u8>) -> Part {
653        Part {
654            name: name.to_owned(),
655            size: bytes.len() as u64,
656            bytes,
657            align: 1,
658            shape: Shape::of(name),
659            relocs: Vec::new(),
660        }
661    }
662
663    /// One name at an offset into the first section.
664    fn at(name: &str, offset: u64, sort: Sort, binding: Binding) -> Name {
665        Name {
666            name: name.to_owned(),
667            at: Held::In { part: 0, offset },
668            size: 0,
669            sort,
670            binding,
671            visibility: Visibility::Default,
672        }
673    }
674
675    /// The raw `st_info` and `st_value` of a symbol, as the file holds them.
676    ///
677    /// The reader's own `kind()`, `is_global()` and `address()` are a translation of these, and a
678    /// translation is what several of the cases below are about, so they ask the file rather than
679    /// the reading. A common symbol is the clearest of them: `address()` gives zero for one because
680    /// it has no address, and the field an ordinary symbol keeps its address in is where a common
681    /// one states the boundary it has to start on.
682    fn raw(bytes: &[u8], want: &str) -> (u8, u64) {
683        let header = elf::FileHeader64::<Endianness>::parse(bytes).expect("a header");
684        let endian = header.endian().expect("an endianness");
685        let table = header.sections(endian, bytes).expect("the sections");
686        let symbols = table.symbols(endian, bytes, elf::SHT_SYMTAB).expect("a symbol table");
687        for symbol in symbols.iter() {
688            if symbols.symbol_name(endian, symbol).expect("a name") == want.as_bytes() {
689                return (symbol.st_info().0, symbol.st_value(endian));
690            }
691        }
692        panic!("there is no symbol called '{want}'");
693    }
694
695    /// The first half of that.
696    fn st_info(bytes: &[u8], want: &str) -> u8 {
697        raw(bytes, want).0
698    }
699
700    #[test]
701    fn a_section_carries_the_flags_the_source_said_and_not_the_ones_its_name_suggests() {
702        // The whole reason a shape is separate facts rather than a kind. A program may write
703        // `.section .init.text,"ax"` and mean a section with a name this compiler has never heard
704        // of, and what it said about it is the letters.
705        let mut odd = part(".init.text", vec![0x90]);
706        odd.shape = Shape { alloc: true, exec: true, bits: true, ..Shape::default() };
707        let input = Assembled { parts: vec![odd], names: Vec::new(), subsections: false };
708        let bytes = assembled(&input, &target()).expect("an object");
709        let file = object::File::parse(&bytes[..]).expect("a readable object");
710        let section = file.section_by_name(".init.text").expect("the section");
711        assert_eq!(section.data().expect("the bytes"), &[0x90]);
712        let SectionFlags::Elf { sh_flags, sh_type } = section.flags() else {
713            panic!("this is an ELF file");
714        };
715        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_EXECINSTR.0);
716        assert_eq!(sh_flags.0 & elf::SHF_WRITE.0, 0, "nothing said it was writable");
717        assert_eq!(sh_type, elf::SHT_PROGBITS);
718    }
719
720    #[test]
721    fn a_section_that_holds_no_bytes_still_says_how_long_it_is() {
722        // `.bss` is a length and no bytes, and a writer that appended its data would produce a file
723        // with that much zero in it, which is the difference between an object and a big object.
724        let mut room = part(".bss", Vec::new());
725        room.size = 4096;
726        room.align = 16;
727        let input = Assembled { parts: vec![room], names: Vec::new(), subsections: false };
728        let bytes = assembled(&input, &target()).expect("an object");
729        assert!(bytes.len() < 4096, "the empty space was written out: {} bytes", bytes.len());
730        let file = object::File::parse(&bytes[..]).expect("a readable object");
731        let section = file.section_by_name(".bss").expect("the section");
732        assert_eq!(section.size(), 4096);
733        assert_eq!(section.align(), 16);
734        let SectionFlags::Elf { sh_type, .. } = section.flags() else { panic!("an ELF file") };
735        assert_eq!(sh_type, elf::SHT_NOBITS);
736    }
737
738    #[test]
739    fn a_label_nobody_stated_a_type_for_is_a_symbol_with_no_type() {
740        // `STT_NOTYPE` is what gas writes for one, and it is a real answer rather than a missing
741        // one. The writer underneath refuses a defined symbol whose kind is `Unknown` outright, so
742        // this is also the case that says the mapping went to `Label` and not there.
743        let input = Assembled {
744            parts: vec![part(".text", vec![0; 8])],
745            names: vec![at("plain", 4, Sort::Untyped, Binding::Global)],
746            subsections: false,
747        };
748        let bytes = assembled(&input, &target()).expect("an object");
749        let file = object::File::parse(&bytes[..]).expect("a readable object");
750        let plain = file.symbols().find(|s| s.name() == Ok("plain")).expect("the label");
751        assert_eq!(plain.address(), 4);
752        assert_eq!(st_info(&bytes, "plain") & 0xf, elf::STT_NOTYPE.0);
753    }
754
755    #[test]
756    fn what_type_said_is_what_the_symbol_gets() {
757        let input = Assembled {
758            parts: vec![part(".text", vec![0; 8])],
759            names: vec![
760                at("run", 0, Sort::Func, Binding::Global),
761                at("held", 4, Sort::Object, Binding::Local),
762            ],
763            subsections: false,
764        };
765        let bytes = assembled(&input, &target()).expect("an object");
766        assert_eq!(st_info(&bytes, "run") & 0xf, elf::STT_FUNC.0);
767        assert_eq!(st_info(&bytes, "held") & 0xf, elf::STT_OBJECT.0);
768        assert_eq!(st_info(&bytes, "run") >> 4, elf::STB_GLOBAL.0);
769        assert_eq!(st_info(&bytes, "held") >> 4, elf::STB_LOCAL.0);
770    }
771
772    #[test]
773    fn a_common_symbol_is_written_the_way_gas_writes_one() {
774        // The writer underneath records `STT_COMMON` and gas records `STT_OBJECT` for the same
775        // `.comm`. Both are a request for storage and a linker takes either, and the one gas writes
776        // is the one written here, so an object of ours and an object of theirs do not differ in a
777        // field somebody's tool reads years from now.
778        let input = Assembled {
779            parts: Vec::new(),
780            names: vec![Name {
781                name: "shared".to_owned(),
782                at: Held::Common { size: 8, align: 8 },
783                size: 0,
784                sort: Sort::Object,
785                binding: Binding::Global,
786                visibility: Visibility::Default,
787            }],
788            subsections: false,
789        };
790        let bytes = assembled(&input, &target()).expect("an object");
791        assert_eq!(st_info(&bytes, "shared"), elf::STB_GLOBAL.0 << 4 | elf::STT_OBJECT.0);
792        let file = object::File::parse(&bytes[..]).expect("a readable object");
793        let shared = file.symbols().find(|s| s.name() == Ok("shared")).expect("the symbol");
794        assert!(shared.is_common(), "the linker has to be asked for the space");
795        assert_eq!(shared.size(), 8);
796        // Where an ordinary symbol keeps its address, which is why the two cannot both be said.
797        assert_eq!(raw(&bytes, "shared").1, 8, "the boundary it has to start on");
798    }
799
800    #[test]
801    fn a_set_is_a_number_rather_than_a_place() {
802        let input = Assembled {
803            parts: vec![part(".text", vec![0; 8])],
804            names: vec![Name {
805                name: "size_of_it".to_owned(),
806                at: Held::Absolute(25),
807                size: 0,
808                sort: Sort::Untyped,
809                binding: Binding::Global,
810                visibility: Visibility::Default,
811            }],
812            subsections: false,
813        };
814        let bytes = assembled(&input, &target()).expect("an object");
815        let file = object::File::parse(&bytes[..]).expect("a readable object");
816        let sym = file.symbols().find(|s| s.name() == Ok("size_of_it")).expect("the symbol");
817        assert_eq!(sym.address(), 25);
818        assert_eq!(sym.section(), object::SymbolSection::Absolute, "it is not in any section");
819    }
820
821    #[test]
822    fn a_relocation_names_a_symbol_and_lands_where_the_bytes_are() {
823        let mut data = part(".data", vec![0; 8]);
824        data.relocs.push(Reloc {
825            at: 0,
826            symbol: "message".to_owned(),
827            kind: Reference::Address { bytes: 8 },
828            addend: 0,
829            after: 0,
830        });
831        let input = Assembled {
832            parts: vec![data],
833            names: vec![Name {
834                name: "message".to_owned(),
835                at: Held::Undefined,
836                size: 0,
837                sort: Sort::Untyped,
838                binding: Binding::Global,
839                visibility: Visibility::Default,
840            }],
841            subsections: false,
842        };
843        let bytes = assembled(&input, &target()).expect("an object");
844        let file = object::File::parse(&bytes[..]).expect("a readable object");
845        let section = file.section_by_name(".data").expect("the section");
846        let (at, reloc) = section.relocations().next().expect("one relocation");
847        assert_eq!(at, 0);
848        assert_eq!(reloc.addend(), 0);
849        let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("an ELF file") };
850        assert_eq!(r_type, elf::R_X86_64_64);
851    }
852
853    #[test]
854    fn a_place_only_this_file_sees_is_reached_through_its_section_as_gas_does() {
855        // The `.L` label goes, the static function stays in the table, and both relocations are
856        // against `.text` at their offsets. A call keeps its name, since the linker may give it a
857        // stub, and so does a name the linker is allowed to see.
858        let mut text = part(".text", vec![0; 32]);
859        for (at, symbol, kind) in [
860            (0, ".L3", Reference::Data),
861            (4, "helper", Reference::Data),
862            (8, "helper", Reference::Call),
863            (12, "shared", Reference::Data),
864        ] {
865            let symbol = symbol.to_owned();
866            text.relocs.push(Reloc { at, symbol, kind, addend: -4, after: 0 });
867        }
868        let input = Assembled {
869            parts: vec![text],
870            names: vec![
871                at(".L3", 20, Sort::Untyped, Binding::Local),
872                at("helper", 24, Sort::Func, Binding::Local),
873                at("shared", 28, Sort::Func, Binding::Global),
874            ],
875            subsections: false,
876        };
877        let bytes = assembled(&input, &target()).expect("an object");
878        let file = object::File::parse(&bytes[..]).expect("a readable object");
879        let names: Vec<_> = file.symbols().filter_map(|sym| sym.name().ok()).collect();
880        assert!(!names.contains(&".L3") && names.contains(&"helper"), "{names:?}");
881        let section = file.section_by_name(".text").expect("the section");
882        let reached: Vec<_> = section
883            .relocations()
884            .map(|(at, reloc)| {
885                let object::RelocationTarget::Symbol(index) = reloc.target() else {
886                    panic!("a symbol")
887                };
888                let symbol = file.symbol_by_index(index).expect("the symbol");
889                let name = if symbol.kind() == object::SymbolKind::Section {
890                    ".text"
891                } else {
892                    symbol.name().expect("a name")
893                };
894                (at, name, reloc.addend())
895            })
896            .collect();
897        assert_eq!(
898            reached,
899            [(0, ".text", 16), (4, ".text", 20), (8, "helper", -4), (12, "shared", -4)]
900        );
901    }
902
903    #[test]
904    fn a_section_of_constants_may_be_merged_and_a_distance_into_it_keeps_its_name() {
905        let mut text = part(".text", vec![0; 8]);
906        text.relocs.push(Reloc {
907            at: 0,
908            symbol: ".LC0".to_owned(),
909            kind: Reference::Data,
910            addend: -4,
911            after: 0,
912        });
913        let strings = Part {
914            shape: Shape { merge: 1, strings: true, ..Shape::of(".rodata") },
915            ..part(".rodata.str1.1", b"hi\0".to_vec())
916        };
917        let mut name = at(".LC0", 0, Sort::Untyped, Binding::Local);
918        name.at = Held::In { part: 1, offset: 0 };
919        let input = Assembled { parts: vec![text, strings], names: vec![name], subsections: false };
920        let bytes = assembled(&input, &target()).expect("an object");
921        let file = object::File::parse(&bytes[..]).expect("a readable object");
922        let section = file.section_by_name(".rodata.str1.1").expect("the section");
923        let SectionFlags::Elf { sh_flags, .. } = section.flags() else { panic!("an ELF file") };
924        assert_eq!(sh_flags.0, elf::SHF_ALLOC.0 | elf::SHF_MERGE.0 | elf::SHF_STRINGS.0);
925        let header = elf::FileHeader64::<Endianness>::parse(&bytes[..]).expect("a header");
926        let endian = header.endian().expect("an endianness");
927        let table = header.sections(endian, &bytes[..]).expect("the sections");
928        let (_, found) = table.section_by_name(endian, b".rodata.str1.1").expect("the section");
929        assert_eq!(found.sh_entsize.get(endian), 1);
930        let text = file.section_by_name(".text").expect("the section");
931        let (_, reloc) = text.relocations().next().expect("one relocation");
932        let object::RelocationTarget::Symbol(index) = reloc.target() else { panic!("a symbol") };
933        assert_eq!(file.symbol_by_index(index).and_then(|sym| sym.name()), Ok(".LC0"));
934    }
935
936    #[test]
937    fn a_relocation_against_a_name_the_file_never_mentions_is_refused() {
938        // Rather than written against symbol zero, which is a file that links and resolves the
939        // reference to address zero. The list of names is the whole of what the reader found, so a
940        // relocation naming something outside it is a mistake in this compiler.
941        let mut data = part(".data", vec![0; 8]);
942        data.relocs.push(Reloc {
943            at: 0,
944            symbol: "nowhere".to_owned(),
945            kind: Reference::Address { bytes: 8 },
946            addend: 0,
947            after: 0,
948        });
949        let input = Assembled { parts: vec![data], names: Vec::new(), subsections: false };
950        let why = assembled(&input, &target()).expect_err("this cannot be written");
951        assert!(format!("{why}").contains("nowhere"), "{why}");
952    }
953
954    #[test]
955    fn the_stack_is_marked_once_whoever_asked_for_it() {
956        // A linker that does not find this marker in every input marks the stack executable, and a
957        // file written by hand for one that cares often says it itself.
958        let bare = Assembled {
959            parts: vec![part(".text", vec![0x90])],
960            names: Vec::new(),
961            subsections: false,
962        };
963        let bytes = assembled(&bare, &target()).expect("an object");
964        let file = object::File::parse(&bytes[..]).expect("a readable object");
965        assert!(file.section_by_name(".note.GNU-stack").is_some(), "the marker was left out");
966
967        let said = Assembled {
968            parts: vec![part(".text", vec![0x90]), part(".note.GNU-stack", Vec::new())],
969            names: Vec::new(),
970            subsections: false,
971        };
972        let bytes = assembled(&said, &target()).expect("an object");
973        let file = object::File::parse(&bytes[..]).expect("a readable object");
974        let marks = file.sections().filter(|s| s.name() == Ok(".note.GNU-stack")).count();
975        assert_eq!(marks, 1, "the file said it and it was said again");
976    }
977
978    #[test]
979    fn only_the_names_a_linker_could_find_are_offered_to_an_archive() {
980        let input = Assembled {
981            parts: vec![part(".text", vec![0; 8])],
982            names: vec![
983                at("reachable", 0, Sort::Func, Binding::Global),
984                at("mine", 4, Sort::Func, Binding::Local),
985                Name {
986                    name: "elsewhere".to_owned(),
987                    at: Held::Undefined,
988                    size: 0,
989                    sort: Sort::Untyped,
990                    binding: Binding::Global,
991                    visibility: Visibility::Default,
992                },
993            ],
994            subsections: false,
995        };
996        assert_eq!(assembled_defines(&input), vec!["reachable".to_owned()]);
997    }
998
999    #[test]
1000    fn a_machine_this_does_not_write_is_refused_rather_than_written_wrong() {
1001        let input = Assembled {
1002            parts: vec![part(".text", vec![0x90])],
1003            names: Vec::new(),
1004            subsections: false,
1005        };
1006        let elsewhere = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Windows, Env::Msvc));
1007        let why = assembled(&input, &elsewhere).expect_err("this cannot be written");
1008        assert!(format!("{why}").contains("aarch64"), "{why}");
1009    }
1010
1011    #[test]
1012    fn a_file_of_assembly_for_aarch64_is_written_with_that_machine_s_relocations() {
1013        // `adrp x0, table` and `add x0, x0, :lo12:table+8`, then `bl g`, then the address of
1014        // `table` in a table of its own. A field is its fixup's relocation and an address is the
1015        // AArch64 one of that width, and a label only this file sees is written against its
1016        // section, the way gas writes it.
1017        let mut text = part(".text", vec![0; 12]);
1018        let field = |at, symbol: &str, fixup, addend| Reloc {
1019            at,
1020            symbol: symbol.to_owned(),
1021            kind: Reference::Field(fixup),
1022            addend,
1023            after: 0,
1024        };
1025        text.relocs = vec![
1026            field(0, ".Ltable", Fixup::AdrPage21, 8),
1027            field(4, ".Ltable", Fixup::AddLo12, 8),
1028            field(8, "g", Fixup::Call26, 0),
1029        ];
1030        let mut data = part(".data", vec![0; 16]);
1031        data.relocs = vec![Reloc {
1032            at: 8,
1033            symbol: ".Ltable".to_owned(),
1034            kind: Reference::Address { bytes: 8 },
1035            addend: 0,
1036            after: 0,
1037        }];
1038        let mut table = at(".Ltable", 0, Sort::Object, Binding::Local);
1039        table.at = Held::In { part: 1, offset: 0 };
1040        let input = Assembled {
1041            parts: vec![text, data],
1042            names: vec![
1043                table,
1044                Name { at: Held::Undefined, ..at("g", 0, Sort::Untyped, Binding::Global) },
1045            ],
1046            subsections: false,
1047        };
1048        let target = TargetInfo::new(Triple::new(TargetArch::Aarch64, Os::Linux, Env::Gnu));
1049        let bytes = assembled(&input, &target).expect("an object");
1050        let file = object::File::parse(&bytes[..]).expect("a readable object");
1051        assert_eq!(file.architecture(), Architecture::Aarch64);
1052        let relocs = |name: &str| -> Vec<(u64, elf::RelocationType, i64)> {
1053            let section = file.section_by_name(name).expect("the section");
1054            section
1055                .relocations()
1056                .map(|(at, reloc)| {
1057                    let RelocationFlags::Elf { r_type } = reloc.flags() else { panic!("ELF") };
1058                    (at, r_type, reloc.addend())
1059                })
1060                .collect()
1061        };
1062        assert_eq!(
1063            relocs(".text"),
1064            [
1065                (0, elf::R_AARCH64_ADR_PREL_PG_HI21, 8),
1066                (4, elf::R_AARCH64_ADD_ABS_LO12_NC, 8),
1067                (8, elf::R_AARCH64_CALL26, 0)
1068            ]
1069        );
1070        assert_eq!(relocs(".data"), [(8, elf::R_AARCH64_ABS64, 0)]);
1071        assert!(file.symbols().all(|s| s.name() != Ok(".Ltable")), "a label only this file sees");
1072    }
1073
1074    #[test]
1075    fn a_file_of_assembly_for_windows_is_written_as_coff() {
1076        // What tamnd/rucc#1514 was about. `runtime/builtins/chkstk.S` is a file of assembly for a
1077        // Windows target, and until this it was refused with a message about there being no object
1078        // writer for the triple, which read as the whole back end being missing rather than this
1079        // one path through it.
1080        let input = Assembled {
1081            parts: vec![part(".text", vec![0xc3])],
1082            names: Vec::new(),
1083            subsections: false,
1084        };
1085        let bytes = assembled(&input, &windows()).expect("an object");
1086        let file = object::File::parse(&bytes[..]).expect("a readable object");
1087        assert_eq!(file.format(), object::BinaryFormat::Coff);
1088        let section = file.section_by_name(".text").expect("the section");
1089        assert_eq!(section.data().expect("the bytes"), &[0xc3]);
1090        assert_eq!(section.kind(), SectionKind::Text);
1091        assert!(
1092            file.section_by_name(".note.GNU-stack").is_none(),
1093            "a format with no marker got one anyway"
1094        );
1095    }
1096
1097    #[test]
1098    fn a_global_label_with_no_type_under_it_is_still_offered_on_coff() {
1099        // The case a `.globl` and a label is, which is most of what a hand written file says. On
1100        // ELF that is `STT_NOTYPE` and the binding is a separate field, so the name is global
1101        // whatever its type. COFF has no such split: what the writer underneath calls a label is
1102        // storage class `LABEL`, which is a name inside one file, and a symbol written that way is
1103        // one no linker resolves against. `___chkstk_ms` came out of the archive as a local under
1104        // that mapping and mingw-w64's own objects went on wanting it.
1105        let input = Assembled {
1106            parts: vec![part(".text", vec![0; 8])],
1107            names: vec![
1108                at("offered", 0, Sort::Untyped, Binding::Global),
1109                at("ours", 4, Sort::Untyped, Binding::Local),
1110            ],
1111            subsections: false,
1112        };
1113        let bytes = assembled(&input, &windows()).expect("an object");
1114        let file = object::File::parse(&bytes[..]).expect("a readable object");
1115        let offered = file.symbols().find(|s| s.name() == Ok("offered")).expect("the label");
1116        assert!(offered.is_global(), "a `.globl` label came out local");
1117        let ours = file.symbols().find(|s| s.name() == Ok("ours")).expect("the other label");
1118        assert!(!ours.is_global(), "a label nothing offered came out global");
1119        // And the same input on ELF is still what gas writes there, which is the half of this that
1120        // would otherwise have been changed to fix the other half.
1121        let bytes = assembled(&input, &target()).expect("an object");
1122        assert_eq!(st_info(&bytes, "offered") & 0xf, elf::STT_NOTYPE.0);
1123    }
1124
1125    #[test]
1126    fn a_relocation_on_coff_says_how_much_of_the_instruction_comes_after_it() {
1127        // The one real difference between the two formats' relocations. ELF folds the distance
1128        // between the hole and the end of the instruction into the addend and has one type. COFF
1129        // counts from the end of the instruction and has no addend field, so the count is in the
1130        // type: `IMAGE_REL_AMD64_REL32_4` is four bytes of immediate behind the displacement.
1131        let mut text = part(".text", vec![0; 16]);
1132        text.relocs.push(Reloc {
1133            at: 2,
1134            symbol: "elsewhere".to_owned(),
1135            kind: Reference::Data,
1136            addend: -8,
1137            after: 4,
1138        });
1139        let input = Assembled {
1140            parts: vec![text],
1141            names: vec![Name {
1142                name: "elsewhere".to_owned(),
1143                at: Held::Undefined,
1144                size: 0,
1145                sort: Sort::Untyped,
1146                binding: Binding::Global,
1147                visibility: Visibility::Default,
1148            }],
1149            subsections: false,
1150        };
1151        let bytes = assembled(&input, &windows()).expect("an object");
1152        let file = object::File::parse(&bytes[..]).expect("a readable object");
1153        let section = file.section_by_name(".text").expect("the section");
1154        let (at, reloc) = section.relocations().next().expect("the relocation");
1155        assert_eq!(at, 2);
1156        assert_eq!(
1157            reloc.flags(),
1158            RelocationFlags::Coff {
1159                typ: object::pe::RelocationType(object::pe::IMAGE_REL_AMD64_REL32.0 + 4)
1160            }
1161        );
1162    }
1163}