File: macos.rs

package info (click to toggle)
firefox 148.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,719,656 kB
  • sloc: cpp: 7,618,171; javascript: 6,701,506; ansic: 3,781,787; python: 1,418,364; xml: 638,647; asm: 438,962; java: 186,285; sh: 62,885; makefile: 19,010; objc: 13,092; perl: 12,763; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (139 lines) | stat: -rw-r--r-- 5,914 bytes parent folder | download | duplicates (12)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::result::{Error, Result};

use goblin::mach;

use super::BuildIdReader;

use log::trace;

const HEADER_SIZE: usize = std::mem::size_of::<goblin::mach::header::Header64>();

impl BuildIdReader {
    pub fn get_build_id_bytes(&mut self, buffer: &[u8], note_name: &str) -> Result<Vec<u8>> {
        trace!("get_build_id_bytes: {}", note_name);
        let (section, note) = note_name.split_once(",").ok_or(Error::InvalidNoteName)?;
        trace!("get_build_id_bytes: {} {}", section, note);

        let fat_header = mach::fat::FatHeader::parse(buffer).map_err(|source| Error::Goblin {
            action: "parse fat header",
            source,
        })?;
        trace!("get_build_id_bytes: fat header: {:?}", fat_header);

        /* First we attempt to parse if there's a Fat header there
         *
         * If we have one, then we have a universal binary so we are going to
         * search the architectures to find the one we want, extract the correct
         * MachO buffer as well as the offset at which the MachO binary is.
         * Testing Universal binaries will require running gtest against a
         * Shippable build.
         *
         * If not we have a normal MachO and we directly parse the buffer we
         * have read earlier, and use a 0 offset.
         */
        let (buf, main_offset): ([u8; HEADER_SIZE], usize) =
            if fat_header.magic == mach::fat::FAT_CIGAM || fat_header.magic == mach::fat::FAT_MAGIC
            {
                let total = std::mem::size_of::<mach::fat::FatHeader>() as usize
                    + (std::mem::size_of::<mach::fat::FatArch>() * fat_header.nfat_arch as usize);

                let mach_buffer = self.copy_bytes(0, total)?;

                if let mach::Mach::Fat(multi_arch) =
                    mach::Mach::parse_lossy(&mach_buffer).map_err(|source| Error::Goblin {
                        action: "parse mach binary",
                        source,
                    })?
                {
                    let arches = multi_arch.arches().map_err(|source| Error::Goblin {
                        action: "get multiarch arches",
                        source,
                    })?;

                    #[cfg(target_arch = "x86_64")]
                    let that_arch = mach::constants::cputype::CPU_TYPE_X86_64;
                    #[cfg(target_arch = "aarch64")]
                    let that_arch = mach::constants::cputype::CPU_TYPE_ARM64;

                    let arch_index = arches
                        .iter()
                        .position(|&x| x.cputype == that_arch)
                        .ok_or(Error::ArchNotAvailable)?;
                    trace!("get_build_id_bytes: arches[]: {:?}", arches[arch_index]);

                    let offset = arches[arch_index].offset as usize;

                    let b = self
                        .copy_bytes(offset, HEADER_SIZE)?
                        .try_into()
                        .expect("copy_bytes didn't copy exactly as many bytes as requested");
                    (b, offset)
                } else {
                    return Err(Error::NotFatArchive);
                }
            } else {
                (
                    buffer.try_into().map_err(|source| Error::NotEnoughData {
                        expected: HEADER_SIZE,
                        source,
                    })?,
                    0,
                )
            };
        trace!("get_build_id_bytes: {} {}", section, note);

        let macho_head = mach::header::Header64::from_bytes(&buf);
        let mut address = main_offset + HEADER_SIZE;
        let end_of_commands = address + (macho_head.sizeofcmds as usize);

        while address < end_of_commands {
            let command =
                unsafe { self.copy::<mach::load_command::LoadCommandHeader>(address as usize)? };
            trace!("get_build_id_bytes: command {:?}", command);

            if command.cmd == mach::load_command::LC_SEGMENT_64 {
                let segment =
                    unsafe { self.copy::<mach::load_command::SegmentCommand64>(address as usize)? };
                trace!("get_build_id_bytes: segment {:?}", segment);

                let name = segment.name().map_err(|source| Error::Goblin {
                    action: "get segment name",
                    source,
                })?;
                if name == section {
                    let sections_addr =
                        address + std::mem::size_of::<mach::load_command::SegmentCommand64>();
                    let sections = unsafe {
                        self.copy_array::<mach::load_command::Section64>(
                            sections_addr as usize,
                            segment.nsects as usize,
                        )?
                    };
                    trace!("get_build_id_bytes: sections {:?}", sections);

                    for section in &sections {
                        trace!("get_build_id_bytes: section {:?}", section);
                        if let Ok(sname) = Self::string_from_bytes(&section.sectname) {
                            trace!("get_build_id_bytes: sname {:?}", sname);
                            if (sname.len() == 0) || (sname != note) {
                                continue;
                            }

                            return self.copy_bytes(
                                main_offset + section.addr as usize,
                                section.size as usize,
                            );
                        }
                    }
                }
            }
            address += command.cmdsize as usize;
        }

        Err(Error::NoteNotAvailable)
    }
}