summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/codegen/ElfReader.java
blob: 534bb10f5d5f37725121d583cf42bf92ed66a7c4 (plain)
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package com.seaofnodes.simple.codegen;

import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMemPtr;
import com.seaofnodes.simple.type.TypeStruct;
import com.seaofnodes.simple.util.Ary;
import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.io.*;
import java.nio.file.Files;
import java.util.HashMap;

public class ElfReader {

    public final File _file;
    public final byte[] _buf;
    int _x;                     // Cursor into buffer
    // ELF Header
    public final ElfHeader _header;
    // All sections
    public final Section[] _sections;
    // Simple section, if available
    public final Section _simple;
    // Byte array in/out stream - used as a reader
    public final BAOS _bais;
    // Compilation unit; written into FunNodes
    public final CompUnit _compunit;

    // Strings from the Simple section, either small public set or whole set
    public String[] _strs;
    // Dependent file names
    public String[] _deps;
    // Only published symbol this file
    public TypeStruct _clz;
    // Canonical named types, distinct from optimized same-named graph states.
    public TypeStruct[] _pubTypes;
    // Nodes read in this Elf
    public Ary<Node> _nodes;

    // Load an Elf file (mostly lazily)
    public static ElfReader load( File f, CompUnit cu ) {
        try { return new ElfReader(f,cu); }
        catch( IOException ioe ) { return null; }
    }

    // Load the Simple section from the ELF file
    private ElfReader( File f, CompUnit cu ) throws IOException {
        _file = f;
        _compunit = cu;
        _buf = Files.readAllBytes(f.toPath());
        _header = new ElfHeader();
        // No attempt is made to parse the program header table.
        _sections = new Section[_header._shnum];
        for( int i=0; i<_header._shnum; i++ )
            _sections[i] = new Section(i);

        // Load section names
        Section secstr = _sections[_header._shstrndx];
        for( Section sec : _sections )
            if( sec._type != 0 )
                sec._name = secstr.str(sec._noff);

        Section simple = null;
        for( Section sec : _sections )
            if( ".simple".equals(sec._name) )
                simple = sec;
        _simple = simple;
        _bais = new BAOS(_buf, (int)_simple._offset);
    }

    // For Serialize self-test
    ElfReader( BAOS bais ) {
        _file = null;
        _compunit = null;
        _buf = bais.buf();
        _header = null;
        _sections = null;
        _simple = null;
        _bais = bais;
    }

    // Load the public symbols and dependent objs, but not the whole IR
    void loadPublicSymbols() {
        // Check already loaded and cached
        if( _strs == null && _simple != null )
            // Read only the published public strings, for speed purposes when
            // scanning a large count of ELF files but getting hits in only a
            // few of them.  i.e. optimizing (prematurely?) for the traditional
            // case of linking a small file against a large library
            Serialize.read_public_strs(this);
    }

    // Loads the entire IR
    public TypeStruct loadSimple(CodeGen code) {
        Serialize.readAll(code, this, code._aliases, code._fidxs, code._rpcs);
        return _clz;
    }

    // Loads strings, mappings, and types, but not Nodes.  Used after the
    // public-string scan finds a matching Simple ELF, so parsing can resolve
    // imported field/array aliases before the full graph is linked in later.
    public TypeStruct loadPublicTypes(CodeGen code) {
        Serialize.readTypes(code, this, code._aliases, code._fidxs, code._rpcs);
        return _clz;
    }

    int u1() { return _buf[_x++]&0xFF; }
    int magic() {                         // Big endian
        return (u1() << 24) | (u1() << 16) | (u1() << 8) | u1();
    }
    int u2() { return u1() | (u1()<<8); } // Little endian
    int u4() {                  // Little endian
        return u1() | (u1() << 8) | (u1() << 16) | (u1() << 24);
    }
    long u8() { return u4() | ((long)u4()<<32); }
    void skip(int skip) { _x += skip; }


    public class ElfHeader {
        final int _magic;
        final boolean _format32; // Format32 or format64
        final byte _osabi;
        final boolean _little;  // True if little endian, false if big endian
        final char _type;
        final char _machine;
        final long _entry;      // Process entry point
        final long _phoff;      // Program header table offset, from file start
        final long _shoff;      // Section header table offset, from file start
        final int _flags;       // Flags
        final int _phnum;       // Number of program header entries
        final int _shentsize;
        final int _shnum;       // Number of section header entries
        final int _shstrndx;    // Section header index contains section names


        ElfHeader() throws IOException {
            _x = 0;
            if( (_magic = magic()) != 0x7F454C46 ) bad("magic number",_magic);
            byte EI_CLASS = (byte)u1();
            if( EI_CLASS != 1 && EI_CLASS != 2 ) bad("EI_CLASS",EI_CLASS);
            _format32 = (EI_CLASS==1);
            byte EI_DATA = (byte)u1();
            if( EI_DATA != 1 && EI_DATA != 2 ) bad("EI_DATA",EI_DATA);
            _little = EI_DATA == 1;
            if( u1() != 1 )  bad("version",1);
            if( (_osabi=(byte)u1()) != 0 )  bad("EI_VERSION",_osabi);
            byte abiversion = (byte)u1();
            if( abiversion != 0 ) bad("EI_ABIVERSION",abiversion);
            skip(7);            // Pad

            // Endian matters now
            if( !_little ) throw new IOException("TODO: Big endian");
            if( (_type = (char)u2()) != 1 )  bad("ET_REL",_type);
            if( (_machine = (char)u2()) != 0x3E )  bad("AMD x86-64",_machine);
            int ver = u4();
            if( ver != 1 ) bad("Version",ver);
            // Format32 matters now
            if( _format32 ) bad("Format",1);
            _entry = u8();      // Process entry point
            _phoff = u8();      // Program header table offset
            _shoff = u8();      // Section header table offset
            _flags = u4();      // Flags
            int ehsize = u2();
            if( ehsize != 0x40 ) bad("EHSIZE",ehsize);
            int phentsize = u2();
            if( phentsize != 0x38 && phentsize != 0 ) bad("PHENTSIZE",phentsize);
            _phnum = u2();
            _shentsize = u2();
            if( _shentsize != 0x40 ) bad("SHENTSIZE",_shentsize);
            _shnum = u2();
            // String section
            _shstrndx = u2();
            assert _x == ehsize;
        }
    }

    public class Section {
        final int _noff;        // Offset in string section for name
        final int _type;        // Section type
        final long _flags;
        final long _addr;       // Offset in virtual mem, if loaded
        final long _offset;     // Offset in file
        final long _size;       // Size in bytes
        final int _link;        // Associated section index, varies
        final int _info;        // Varies
        final byte _align;      // Log2 alignment
        final int _esize;       // Element size, if non-zero
        String _name;

        Section(int sidx) throws IOException {
            _x = (int)(_header._shoff + (sidx * _header._shentsize));
            _noff  = u4();
            _type  = u4();
            _flags = u8();
            _addr  = u8();
            _offset= u8();
            _size  = u8();
            _link  = u4();
            _info  = u4();
            long align = u8();
            if( align==0 ) align=1;
            _align = (byte)Long.numberOfTrailingZeros(align);
            if( (1L << _align ) != align )
                throw new IOException("Bad section alignment: "+align);
            _esize = (int)u8();
        }

        String str(int off) {
            assert _type == 3/*SHT_STRTAB*/; // String table section header
            int start = (int)(_offset+off);
            int x = start;
            while( _buf[x++] !=0 ) ;
            return new String(_buf,start,x-start-1);
        }
    }

    private static void bad(String msg, int val) throws IOException {
        throw new IOException("Bad "+msg+": "+val);
    }



    //public TypeMemPtr lookup( String name ) {
    //    if( _simple == null ) return null;
    //    BAOS baos = new BAOS(_buf, (int)_simple._offset);
    //    Ary<String> strs = Serialize.read_strs(baos);
    //
    //
    //
    //    Ary<Node> nodes = Serialize.read_types(baos);
    //
    //    throw Utils.TODO("Elf load symbol "+name);
    //}
}