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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
use crate::io::{Endian, ReadExt as _, WriteExt as _};
use std::io::{Read, Write};

/// This is the second set of the symbolic information which is used to support
/// the data structures for the dynamically link editor.
///
/// The original set of symbolic information in the symtab_command which contains
/// the symbol and string tables must also be present when this load command is
/// present.  When this load command is present the symbol table is organized
/// into three groups of symbols:
/// * local symbols (static and debugging symbols) - grouped by module
/// * defined external symbols - grouped by module (sorted by name if not lib)
/// * undefined external symbols (sorted by name if MH_BINDATLOAD is not set,
///   and in order the were seen by the static linker if MH_BINDATLOAD is set)
///
/// In this load command there are offsets and counts to each of the three groups of symbols.
///
/// This load command contains a the offsets and sizes of the following new
/// symbolic information tables:
/// * table of contents
/// * module table
/// * reference symbol table
/// * indirect symbol table
///
/// The first three tables above (the table of contents, module table and
/// reference symbol table) are only present if the file is a dynamically linked
/// shared library.  For executable and object modules, which are files
/// containing only one module, the information that would be in these three
/// tables is determined as follows:
/// * table of contents - the defined external symbols are sorted by name
/// * module table - the file contains only one module so everything in the
/// file is part of the module.
/// * reference symbol table - is the defined and undefined external symbols
///
/// For dynamically linked shared library files this load command also contains
/// offsets and sizes to the pool of relocation entries for all sections separated into two groups:
/// * external relocation entries
/// * local relocation entries
///
/// For executable and object modules the relocation entries continue to hang
/// off the section structures.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DysymtabCommand {
    pub cmd: u32,
    pub cmdsize: u32,
    /// index to local symbols
    pub ilocalsym: u32,
    /// number of local symbols
    pub nlocalsym: u32,
    /// index to externally defined symbols
    pub iextdefsym: u32,
    /// number of externally defined symbols
    pub nextdefsym: u32,
    /// index to undefined symbols
    pub iundefsym: u32,
    /// number of undefined symbols
    pub nundefsym: u32,
    /// file offset to table of contents
    pub tocoff: u32,
    /// number of entries in table of contents
    pub ntoc: u32,
    /// file offset to module table
    pub modtaboff: u32,
    /// number of module table entries
    pub nmodtab: u32,
    /// offset to referenced symbol table
    pub extrefsymoff: u32,
    /// number of referenced symbol table entries
    pub nextrefsyms: u32,
    /// file offset to the indirect symbol table
    pub indirectsymoff: u32,
    /// number of indirect symbol table entries
    pub nindirectsyms: u32,
    /// offset to external relocation entries
    pub extreloff: u32,
    /// number of external relocation entries
    pub nextrel: u32,
    /// offset to local relocation entries
    pub locreloff: u32,
    /// number of local relocation entries
    pub nlocrel: u32,
}

impl DysymtabCommand {
    pub const TYPE: u32 = 0xB;

    pub const SIZE: u32 = 0x50;

    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
        let cmd = read.read_u32_in(endian);
        assert_eq!(cmd, Self::TYPE);

        let cmdsize = read.read_u32_in(endian);
        assert_eq!(cmdsize, Self::SIZE);

        let ilocalsym = read.read_u32_in(endian);
        let nlocalsym = read.read_u32_in(endian);
        let iextdefsym = read.read_u32_in(endian);
        let nextdefsym = read.read_u32_in(endian);
        let iundefsym = read.read_u32_in(endian);
        let nundefsym = read.read_u32_in(endian);
        let tocoff = read.read_u32_in(endian);
        let ntoc = read.read_u32_in(endian);
        let modtaboff = read.read_u32_in(endian);
        let nmodtab = read.read_u32_in(endian);
        let extrefsymoff = read.read_u32_in(endian);
        let nextrefsyms = read.read_u32_in(endian);
        let indirectsymoff = read.read_u32_in(endian);
        let nindirectsyms = read.read_u32_in(endian);
        let extreloff = read.read_u32_in(endian);
        let nextrel = read.read_u32_in(endian);
        let locreloff = read.read_u32_in(endian);
        let nlocrel = read.read_u32_in(endian);

        DysymtabCommand {
            cmd,
            cmdsize,
            ilocalsym,
            nlocalsym,
            iextdefsym,
            nextdefsym,
            iundefsym,
            nundefsym,
            tocoff,
            ntoc,
            modtaboff,
            nmodtab,
            extrefsymoff,
            nextrefsyms,
            indirectsymoff,
            nindirectsyms,
            extreloff,
            nextrel,
            locreloff,
            nlocrel,
        }
    }

    pub fn write_into<W: Write>(&self, write: &mut W) {
        write.write_u32_native(self.cmd);
        write.write_u32_native(self.cmdsize);
        write.write_u32_native(self.ilocalsym);
        write.write_u32_native(self.nlocalsym);
        write.write_u32_native(self.iextdefsym);
        write.write_u32_native(self.nextdefsym);
        write.write_u32_native(self.iundefsym);
        write.write_u32_native(self.nundefsym);
        write.write_u32_native(self.tocoff);
        write.write_u32_native(self.ntoc);
        write.write_u32_native(self.modtaboff);
        write.write_u32_native(self.nmodtab);
        write.write_u32_native(self.extrefsymoff);
        write.write_u32_native(self.nextrefsyms);
        write.write_u32_native(self.indirectsymoff);
        write.write_u32_native(self.nindirectsyms);
        write.write_u32_native(self.extreloff);
        write.write_u32_native(self.nextrel);
        write.write_u32_native(self.locreloff);
        write.write_u32_native(self.nlocrel);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn write_and_read_dysymtab_command() {
        let cmd = DysymtabCommand {
            cmd: DysymtabCommand::TYPE,
            cmdsize: DysymtabCommand::SIZE,
            ilocalsym: 0,
            nlocalsym: 2,
            iextdefsym: 3,
            nextdefsym: 4,
            iundefsym: 5,
            nundefsym: 9,
            tocoff: 8,
            ntoc: 5,
            modtaboff: 8,
            nmodtab: 5,
            extrefsymoff: 0,
            nextrefsyms: 2,
            indirectsymoff: 3,
            nindirectsyms: 6,
            extreloff: 8,
            nextrel: 9,
            locreloff: 0,
            nlocrel: 2,
        };

        let mut buf = Vec::new();

        cmd.write_into(&mut buf);

        assert_eq!(buf.len(), DysymtabCommand::SIZE as usize);

        let read_cmd = DysymtabCommand::read_from_in(&mut buf.as_slice(), Endian::NATIVE);

        assert_eq!(read_cmd, cmd);
    }
}