1 /**
2  * Copyright 2013 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 package jogamp.common.os.elf;
29 
30 import static jogamp.common.os.elf.IOUtils.toHexString;
31 import static jogamp.common.os.elf.IOUtils.readUInt32;
32 import static jogamp.common.os.elf.IOUtils.getString;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 import com.jogamp.common.util.Bitstream;
38 
39 /**
40  * ARM EABI attributes within section header {@link SectionHeader#SHT_ARM_ATTRIBUTES}.
41  * <p>
42  * References:
43  * <ul>
44  *   <li>http://infocenter.arm.com/
45  *   <ul>
46  *      <li>ARM IHI 0044E, current through ABI release 2.09</li>
47  *      <li>ARM IHI 0045D, current through ABI release 2.09</li>
48  *   </ul></li>
49  * </ul>
50  * </p>
51  */
52 public class SectionArmAttributes extends Section {
53     public static final byte FORMAT_VERSION_A = 0x41; // 'A';
54 
55     public static enum Type {
56         /** No Value */
57         None,
58         /** A Sub-Section - following the 4 byte sub section total size (tag + size + content) - byte order of the ELF file */
59         SubSection,
60         /** Null Terminated Byte-String */
61         NTBS,
62         ULEB128,
63     }
64 
65     /** ULEB128 Value for {@link Tag#ABI_VFP_args}: FP parameter/result passing conforms to AAPCS, BASE variant. */
66     public static final byte ABI_VFP_ARGS_IS_BASE_VARIANT = 0;
67     /** ULEB128 Value for {@link Tag#ABI_VFP_args}: FP parameter/result passing conforms to AAPCS, VFP variant. */
68     public static final byte ABI_VFP_ARGS_IS_VFP_VARIANT = 1;
69     /** ULEB128 Value for {@link Tag#ABI_VFP_args}: FP parameter/result passing conforms to custom toolchain. */
70     public static final byte ABI_VFP_ARGS_IS_CUSTOM_VARIANT = 2;
71     /** ULEB128 Value for {@link Tag#ABI_VFP_args}: FP parameter/result passing conforms to both , BASE and VFP variant. */
72     public static final byte ABI_VFP_ARGS_IS_BOTH_BASE_AND_VFP_VARIANT = 3;
73 
74     /**
75      * Returns true if value is either {@link #ABI_VFP_ARGS_IS_VFP_VARIANT} or {@link #ABI_VFP_ARGS_IS_BOTH_BASE_AND_VFP_VARIANT}
76      * @param v ULEB128 Value from {@link Tag#ABI_VFP_args} attribute
77      */
abiVFPArgsAcceptsVFPVariant(final byte v)78     public static final boolean abiVFPArgsAcceptsVFPVariant(final byte v) {
79         return ABI_VFP_ARGS_IS_VFP_VARIANT == v || ABI_VFP_ARGS_IS_BOTH_BASE_AND_VFP_VARIANT == v;
80     }
81 
82     public static enum Tag {
83         None(0, Type.None),
84         File(1, Type.SubSection), Section(2, Type.SubSection), Symbol(3, Type.SubSection),
85         CPU_raw_name( 4, Type.NTBS ),
86         CPU_name( 5, Type.NTBS ),
87         CPU_arch( 6, Type.ULEB128 ),
88         CPU_arch_profile( 7, Type.ULEB128 ),
89         ARM_ISA_use( 8, Type.ULEB128 ),
90         THUMB_ISA_use( 9, Type.ULEB128 ),
91         FP_arch( 10, Type.ULEB128 ),
92         WMMX_arch( 11, Type.ULEB128 ),
93         Advanced_SIMD_arch( 12, Type.ULEB128 ),
94         PCS_config( 13, Type.ULEB128 ),
95         ABI_PCS_R9_use ( 14, Type.ULEB128 ),
96         ABI_PCS_RW_data( 15, Type.ULEB128 ),
97         ABI_PCS_RO_data( 16, Type.ULEB128 ),
98         ABI_PCS_GOT_use( 17, Type.ULEB128 ),
99         ABI_PCS_wchar_t( 18, Type.ULEB128 ),
100         ABI_FP_rounding( 19, Type.ULEB128 ),
101         ABI_FP_denormal( 20, Type.ULEB128 ),
102         ABI_FP_exceptions( 21, Type.ULEB128 ),
103         ABI_FP_user_exceptions( 22, Type.ULEB128 ),
104         ABI_FP_number_model( 23, Type.ULEB128 ),
105         ABI_align_needed( 24, Type.ULEB128 ),
106         ABI_align_preserved( 25, Type.ULEB128 ),
107         ABI_enum_size( 26, Type.ULEB128 ),
108         ABI_HardFP_use( 27, Type.ULEB128 ),
109         ABI_VFP_args( 28, Type.ULEB128 ),
110         ABI_WMMX_args( 29, Type.ULEB128 ),
111         ABI_optimization_goals( 30, Type.ULEB128 ),
112         ABI_FP_optimization_goals( 31, Type.ULEB128 ),
113         compatibility ( 32, Type.NTBS ), /** with each byte interpreted as an ULEB128 with closing EOS */
114         CPU_unaligned_access( 34, Type.ULEB128 ),
115         FP_HP_extension( 36, Type.ULEB128 ),
116         ABI_FP_16bit_format( 38, Type.ULEB128 ),
117         MPextension_use( 42, Type.ULEB128 ),
118         DIV_use( 44, Type.ULEB128 ),
119         nodefaults( 64, Type.ULEB128 ), /* value ignored */
120         also_compatible_with( 65, Type.ULEB128 ),
121         T2EE_use( 66, Type.ULEB128 ),
122         conformance( 67, Type.NTBS ),
123         Virtualization_use( 68, Type.ULEB128 ),
124         undefined69( 69, Type.None ),
125         MPextension_use_legacy( 70, Type.ULEB128 )
126         ;
127 
128         public final int id;
129         public final Type type;
130 
131         /** Slow O(n) transition of a native tag value to a Tag. */
get(final int id)132         public static Tag get(final int id) {
133             final Tag[] tags = Tag.values();
134             final int tag_count = tags.length;
135             for(int i=0; i < tag_count; i++) {
136                 if( tags[i].id == id ) {
137                     return tags[i];
138                 }
139             }
140             return null;
141         }
142 
Tag(final int id, final Type type)143         Tag(final int id, final Type type){
144             this.id = id;
145             this.type = type;
146         }
147     }
148 
149     public static class Attribute {
150         public final Tag tag;
151         private final Object value;
152 
Attribute(final Tag tag, final Object value)153         Attribute(final Tag tag, final Object value) {
154             this.tag = tag;
155             this.value = value;
156         }
157 
isNTBS()158         public final boolean isNTBS() {
159             return Type.NTBS == tag.type;
160         }
getNTBS()161         public final String getNTBS() {
162             if( Type.NTBS == tag.type ) {
163                 return (String) value;
164             }
165             throw new IllegalArgumentException("Not NTBS but "+tag.type);
166         }
167 
isULEB128()168         public final boolean isULEB128() {
169             return Type.ULEB128 == tag.type;
170         }
getULEB128()171         public final byte getULEB128() {
172             if( Type.ULEB128== tag.type ) {
173                 return ((Byte) value).byteValue();
174             }
175             throw new IllegalArgumentException("Not ULEB128 but "+tag.type);
176         }
177 
178         @Override
toString()179         public String toString() {
180             return tag+" = "+value;
181         }
182     }
183 
184     public static class VendorAttributes {
185         public final String vendor;
186         public final List<Attribute> attributes;
187 
VendorAttributes(final String vendor, final List<Attribute> attributes)188         VendorAttributes(final String vendor, final List<Attribute> attributes) {
189             this.vendor = vendor;
190             this.attributes = attributes;
191         }
192 
193         @Override
toString()194         public String toString() {
195             return vendor + attributes.toString();
196         }
197     }
198     public final List<VendorAttributes> vendorAttributesList;
199 
SectionArmAttributes(final SectionHeader sh, final byte[] data, final int offset, final int length)200     SectionArmAttributes(final SectionHeader sh, final byte[] data, final int offset, final int length) throws IndexOutOfBoundsException, IllegalArgumentException {
201         super(sh, data, offset, length);
202         this.vendorAttributesList = parse(sh, data, offset, length);
203     }
204 
205     @Override
toString()206     public String toString() {
207         return "SectionArmAttributes["+super.toSubString()+", "+vendorAttributesList.toString()+"]";
208     }
209 
get(final Tag tag)210     public final Attribute get(final Tag tag) {
211         for(int i=0; i<vendorAttributesList.size(); i++) {
212             final List<Attribute> attributes = vendorAttributesList.get(i).attributes;
213             for(int j=0; j<attributes.size(); j++) {
214                 final Attribute a = attributes.get(j);
215                 if( a.tag == tag ) {
216                     return a;
217                 }
218             }
219         }
220         return null;
221     }
222 
get(final String vendor)223     public final List<Attribute> get(final String vendor) {
224         return get(vendorAttributesList, vendor);
225     }
226 
get(final List<VendorAttributes> vendorAttributesList, final String vendor)227     static final List<Attribute> get(final List<VendorAttributes> vendorAttributesList, final String vendor) {
228         for(int i=0; i<vendorAttributesList.size(); i++) {
229             final VendorAttributes vas = vendorAttributesList.get(i);
230             if( vas.vendor.equals(vendor) ) {
231                 return vas.attributes;
232             }
233         }
234         return null;
235     }
236 
237     /**
238      * @param sh TODO
239      * @param in byte source buffer to parse
240      * @param offset offset within byte source buffer to start parsing
241      * @param remaining remaining numbers of bytes to parse beginning w/ <code>sb_off</code>,
242      *                  which shall not exceed <code>sb.length - offset</code>.
243      * @throws IndexOutOfBoundsException if <code>offset + remaining > sb.length</code>.
244      * @throws IllegalArgumentException if section parsing failed, i.e. incompatible version or data.
245      */
parse(final SectionHeader sh, final byte[] in, final int offset, final int remaining)246     static List<VendorAttributes> parse(final SectionHeader sh, final byte[] in, final int offset, final int remaining) throws IndexOutOfBoundsException, IllegalArgumentException {
247         Bitstream.checkBounds(in, offset, remaining);
248         int i = offset;
249         if( FORMAT_VERSION_A != in[ i ] ) {
250             throw new IllegalArgumentException("ShArmAttr: Not version A, but: "+toHexString(in[i]));
251         }
252         i++;
253 
254         final List<VendorAttributes> vendorAttributesList = new ArrayList<VendorAttributes>();
255         final boolean isBigEndian = sh.eh2.eh1.isBigEndian();
256 
257         while(i < remaining) {
258             final int i_pre = i;
259             final int secLen = readUInt32(isBigEndian, in, i); /* total section size: 4 + string + content, i.e. offset to next section */
260             i+=4;
261 
262             final String vendor;
263             {
264                 final int[] i_post = new int[] { 0 };
265                 vendor = getString(in, i, secLen - 4, i_post);
266                 i = i_post[0];
267             }
268 
269             final List<Attribute> attributes = new ArrayList<Attribute>();
270 
271             while(i < secLen) {
272                 final int[] i_post = new int[] { 0 };
273                 parseSub(isBigEndian, in, i, secLen - i, i_post, attributes);
274                 i = i_post[0];
275             }
276 
277             if( i_pre + secLen != i ) {
278                 throw new IllegalArgumentException("ShArmAttr: Section length count mismatch, expected "+(i_pre + secLen)+", has "+i);
279             }
280 
281             final List<Attribute> mergeAttribs = get(vendorAttributesList, vendor);
282             if( null != mergeAttribs ) {
283                 mergeAttribs.addAll(attributes);
284             } else {
285                 vendorAttributesList.add(new VendorAttributes(vendor, attributes));
286             }
287         }
288 
289         return vendorAttributesList;
290     }
291 
292     /**
293      * @param isBigEndian TODO
294      * @param in byte source buffer to parse
295      * @param offset offset within byte source buffer to start parsing
296      * @param remaining remaining numbers of bytes to parse beginning w/ <code>sb_off</code>,
297      *                  which shall not exceed <code>sb.length - offset</code>.
298      * @throws IndexOutOfBoundsException if <code>offset + remaining > sb.length</code>.
299      * @throws IllegalArgumentException if section parsing failed, i.e. incompatible version or data.
300      */
parseSub(final boolean isBigEndian, final byte[] in, final int offset, final int remaining, final int[] offset_post, final List<Attribute> attributes)301     private static void parseSub(final boolean isBigEndian, final byte[] in, final int offset, final int remaining,
302                                  final int[] offset_post, final List<Attribute> attributes)
303             throws IndexOutOfBoundsException, IllegalArgumentException
304     {
305         Bitstream.checkBounds(in, offset, remaining);
306 
307         // Starts w/ sub-section Tag
308         int i = offset;
309         final int i_sTag = in[i++];
310         final Tag sTag = Tag.get(i_sTag);
311         if( null == sTag ) {
312             throw new IllegalArgumentException("ShArmAttr: Invalid Sub-Section tag (NaT): "+i_sTag);
313         }
314         final int subSecLen; // sub section total size (tag + size + content)
315         switch(sTag) {
316             case File:
317             case Section:
318             case Symbol:
319                 subSecLen = readUInt32(isBigEndian, in, i);
320                 i+=4;
321                 break;
322             default:
323                 throw new IllegalArgumentException("ShArmAttr: Invalid Sub-Section tag: "+sTag);
324         }
325         if( Tag.File == sTag ) {
326             while( i < offset + subSecLen ) {
327                 final int i_tag = in[i++];
328                 final Tag tag = Tag.get(i_tag);
329                 if( null == tag ) {
330                     throw new IllegalArgumentException("ShArmAttr: Invalid Attribute tag (NaT): "+i_tag);
331                 }
332                 switch(tag.type) {
333                     case NTBS:
334                         {
335                             final int[] i_post = new int[] { 0 };
336                             final String value = getString(in, i, subSecLen + offset - i, i_post);
337                             attributes.add(new Attribute(tag, value));
338                             i = i_post[0];
339                         }
340                         break;
341                     case ULEB128:
342                         {
343                             final byte value = in[i++];
344                             attributes.add(new Attribute(tag, new Byte(value)));
345                         }
346                         break;
347                     default:
348                         throw new IllegalArgumentException("ShArmAttr: Invalid Attribute tag: "+tag);
349                 }
350             }
351         }
352         offset_post[0] = offset + subSecLen;
353     }
354 }
355