@@ -62,10 +62,14 @@ main(List<String> args) async {
6262 scriptDill,
6363 ]);
6464
65- checkElf (scriptSnapshot);
66- checkElf (scriptDebuggingInfo);
65+ final snapshotElf = Elf .fromFile (scriptSnapshot);
66+ Expect .isNotNull (snapshotElf);
67+ final debugInfoElf = Elf .fromFile (scriptDebuggingInfo);
68+ Expect .isNotNull (debugInfoElf);
6769
68- if (Platform .isLinux && ! isSimulator) {
70+ checkElf (snapshotElf! , debugInfoElf! , isAssembled: false );
71+
72+ if ((Platform .isLinux || Platform .isMacOS) && ! isSimulator) {
6973 final scriptAssembly = path.join (tempDir, 'snapshot.S' );
7074 final scriptAssemblySnapshot = path.join (tempDir, 'assembly.so' );
7175 final scriptAssemblyDebuggingInfo =
@@ -83,63 +87,57 @@ main(List<String> args) async {
8387 await assembleSnapshot (scriptAssembly, scriptAssemblySnapshot,
8488 debug: true );
8589
86- checkElf (scriptAssemblySnapshot, isAssembled: true );
87- checkElf (scriptAssemblyDebuggingInfo);
90+ // This one may be null if on MacOS.
91+ final assembledElf = Elf .fromFile (scriptAssemblySnapshot);
92+ if (Platform .isLinux) {
93+ Expect .isNotNull (assembledElf);
94+ }
95+ final assembledDebugElf = Elf .fromFile (scriptAssemblyDebuggingInfo);
96+ Expect .isNotNull (assembledDebugElf);
97+
98+ checkElf (assembledElf, assembledDebugElf! , isAssembled: true );
8899 }
89100 });
90101}
91102
92- void checkElf (String filename, {bool isAssembled = false }) {
93- // Check that the static symbol table contains entries that are not in the
94- // dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
95- final elf = Elf .fromFile (filename);
96- Expect .isNotNull (elf);
97- final dynamicSymbols = elf! .dynamicSymbols;
98- // All symbol tables have an initial entry with zero-valued fields.
99- Expect .isNotEmpty (dynamicSymbols);
100- print ('Dynamic symbols:' );
101- final initialDynamic = dynamicSymbols.first;
102- print (initialDynamic);
103- Expect .equals (SymbolBinding .STB_LOCAL , initialDynamic.bind);
104- Expect .equals (SymbolType .STT_NOTYPE , initialDynamic.type);
105- Expect .equals (0 , initialDynamic.value);
106- for (final symbol in dynamicSymbols.skip (1 )) {
103+ final _uniqueSuffixRegexp = RegExp (r' \(#\d+\)' );
104+
105+ void checkVMSymbolsAreValid (Iterable <Symbol > symbols,
106+ {required String source, required bool isAssembled}) {
107+ print ("Checking VM symbols from $source :" );
108+ for (final symbol in symbols) {
107109 print (symbol);
108- if (! symbol.name.startsWith ('_kDart' )) {
109- // The VM only adds symbols with names starting with _kDart, so this
110- // must be an assembled snapshot.
111- Expect .isTrue (isAssembled);
110+ // All VM-created symbols should have sizes.
111+ Expect .notEquals (0 , symbol.size);
112+ if (symbol.bind != SymbolBinding .STB_GLOBAL &&
113+ symbol.bind != SymbolBinding .STB_LOCAL ) {
114+ Expect .fail ('Unexpected symbol binding ${symbol .bind }' );
115+ }
116+ if (symbol.bind == SymbolBinding .STB_GLOBAL ) {
117+ // All global symbols created by the VM are currently object symbols.
118+ Expect .equals (SymbolType .STT_OBJECT , symbol.type);
119+ Expect .isTrue (symbol.name.startsWith ('_kDart' ),
120+ 'Unexpected symbol name ${symbol .name }' );
121+ Expect .isFalse (_uniqueSuffixRegexp.hasMatch (symbol.name),
122+ 'Global VM symbols should have no numeric suffix' );
112123 continue ;
113124 }
114- Expect .equals (SymbolBinding .STB_GLOBAL , symbol.bind);
115- Expect .equals (SymbolType .STT_OBJECT , symbol.type);
116- // All VM-generated read-only object symbols should have a non-zero size.
117- Expect .notEquals (0 , symbol.size);
118- }
119- print ("" );
120- final onlyStaticSymbols = elf.staticSymbols
121- .where ((s1) => ! dynamicSymbols.any ((s2) => s1.name == s2.name));
122- Expect .isNotEmpty (onlyStaticSymbols, 'no static-only symbols' );
123- final objectSymbols =
124- onlyStaticSymbols.where ((s) => s.type == SymbolType .STT_OBJECT );
125- Expect .isNotEmpty (objectSymbols, 'no static-only object symbols' );
126- print ("Static-only object symbols:" );
127- for (final symbol in objectSymbols) {
128- print (symbol);
129- // There should be no static-only global object symbols.
130- Expect .equals (SymbolBinding .STB_LOCAL , symbol.bind);
131- final objectTypeEnd = symbol.name.indexOf ('_' );
132- // All VM-generated read-only object symbols are prefixed with the type of
133- // the C++ object followed by an underscore. If assembling the snapshot,
134- // the assembler might introduce other object symbols which either start
135- // with an underscore or have no underscore.
136- if (objectTypeEnd <= 0 ) {
137- Expect .isTrue (isAssembled);
125+ if (symbol.type == SymbolType .STT_FUNC ||
126+ symbol.type == SymbolType .STT_SECTION ) {
127+ // Currently we don't do any additional checking on these.
138128 continue ;
139129 }
140- // All VM-generated read-only object symbols should have a non-zero size.
141- Expect .notEquals (0 , symbol.size);
142- final objectType = symbol.name.substring (0 , objectTypeEnd);
130+ if (symbol.type != SymbolType .STT_OBJECT ) {
131+ Expect .fail ('Unexpected symbol type ${symbol .type }' );
132+ }
133+ // The name of object symbols are prefixed with the type of the object. If
134+ // there is useful additional information, the additional information is
135+ // provided after the type in parentheses.
136+ int objectTypeEnd = symbol.name.indexOf (' (' );
137+ final objectType = objectTypeEnd > 0
138+ ? symbol.name.substring (0 , objectTypeEnd)
139+ : symbol.name;
140+
143141 switch (objectType) {
144142 // Used for entries in the non-clustered portion of the read-only data
145143 // section that don't correspond to a specific Dart object.
@@ -152,8 +150,101 @@ void checkElf(String filename, {bool isAssembled = false}) {
152150 case 'PcDescriptors' :
153151 case 'CompressedStackMaps' :
154152 break ;
153+ // In assembly snapshots, we use local object symbols to initialize the
154+ // InstructionsSection header with the right offset to the BSS section.
155+ case '_kDartVmSnapshotBss' :
156+ case '_kDartIsolateSnapshotBss' :
157+ Expect .isTrue (isAssembled);
158+ break ;
155159 default :
156- Expect .fail ('unexpected object type $objectType ' );
160+ Expect .fail ('unexpected object type $objectType in "${ symbol . name }" ' );
157161 }
158162 }
159163}
164+
165+ void checkSymbols (List <Symbol >? snapshotSymbols, List <Symbol > debugInfoSymbols,
166+ {required bool isAssembled}) {
167+ // All symbols in the separate debugging info are created by the VM.
168+ Expect .isNotEmpty (debugInfoSymbols);
169+ checkVMSymbolsAreValid (debugInfoSymbols,
170+ source: 'debug info' , isAssembled: isAssembled);
171+ if (snapshotSymbols == null ) return ;
172+ List <Symbol > checkedSnapshotSymbols = snapshotSymbols;
173+ if (isAssembled) {
174+ final names = debugInfoSymbols.map ((s) => s.name).toSet ();
175+ // All VM-generated symbols should have unique names in assembled output.
176+ Expect .equals (names.length, debugInfoSymbols.length);
177+ // For assembled snapshots, we may have symbols that are created by the
178+ // assembler and not the VM, so ignore those symbols. Since all VM symbols
179+ // have unique names when generating assembly, we just check that the
180+ // debug info has a symbol with the same name.
181+ checkedSnapshotSymbols = < Symbol > [];
182+ for (final symbol in snapshotSymbols) {
183+ if (names.contains (symbol.name)) {
184+ checkedSnapshotSymbols.add (symbol);
185+ }
186+ }
187+ }
188+ checkVMSymbolsAreValid (checkedSnapshotSymbols,
189+ source: 'snapshot' , isAssembled: isAssembled);
190+ }
191+
192+ void checkElf (Elf ? snapshot, Elf debugInfo, {required bool isAssembled}) {
193+ // All symbol tables have an initial entry with zero-valued fields.
194+ final snapshotDynamicSymbols = snapshot? .dynamicSymbols? .skip (1 )? .toList ();
195+ final debugDynamicSymbols = debugInfo.dynamicSymbols.skip (1 ).toList ();
196+ final snapshotStaticSymbols = snapshot? .staticSymbols? .skip (1 )? .toList ();
197+ final debugStaticSymbols = debugInfo.staticSymbols.skip (1 ).toList ();
198+
199+ // First, do our general round of checks against each group of tables.
200+ checkSymbols (snapshotDynamicSymbols, debugDynamicSymbols,
201+ isAssembled: isAssembled);
202+ checkSymbols (snapshotStaticSymbols, debugStaticSymbols,
203+ isAssembled: isAssembled);
204+
205+ // Now do some additional spot checks to make sure we actually haven't missed
206+ // generating some expected VM symbols by examining the debug info tables,
207+ // which only contain VM generated symbols.
208+
209+ // For the dynamic symbol tables, we expect that all VM symbols are global
210+ // object symbols.
211+ for (final symbol in debugDynamicSymbols) {
212+ Expect .equals (symbol.bind, SymbolBinding .STB_GLOBAL ,
213+ 'Expected all global symbols in the dynamic table, got $symbol ' );
214+ Expect .equals (symbol.type, SymbolType .STT_OBJECT ,
215+ 'Expected all object symbols in the dynamic table, got $symbol ' );
216+ }
217+
218+ final debugLocalSymbols =
219+ debugStaticSymbols.where ((s) => s.bind == SymbolBinding .STB_LOCAL );
220+ final debugLocalObjectSymbols =
221+ debugLocalSymbols.where ((s) => s.type == SymbolType .STT_OBJECT );
222+ // We should be generating at least _some_ local object symbols, since we're
223+ // using the --add-readonly-data-symbols flag.
224+ Expect .isNotEmpty (debugLocalObjectSymbols);
225+
226+ // We expect exactly two local object symbols with names starting with
227+ // 'RawBytes'.
228+ int rawBytesSeen = debugLocalObjectSymbols.fold <int >(
229+ 0 , (i, s) => i + (s.name.startsWith ('RawBytes' ) ? 1 : 0 ));
230+ Expect .equals (2 , rawBytesSeen,
231+ 'saw $rawBytesSeen (!= 2) RawBytes local object symbols' );
232+
233+ // All snapshots include at least one (and likely many) duplicate local
234+ // symbols. For assembly snapshots and their separate debugging information,
235+ // these symbols will have a numeric suffix to make them unique. In
236+ // direct-to-ELF snapshots and their separate debugging information, we allow
237+ // duplicate symbol names and thus there should be no numeric suffixes.
238+ bool sawUniqueSuffix = false ;
239+ for (final symbol in debugLocalSymbols) {
240+ if (_uniqueSuffixRegexp.hasMatch (symbol.name)) {
241+ if (! isAssembled) {
242+ Expect .fail ('Saw numeric suffix on symbol: $symbol ' );
243+ }
244+ sawUniqueSuffix = true ;
245+ }
246+ }
247+ if (isAssembled) {
248+ Expect .isTrue (sawUniqueSuffix, 'No numeric suffixes seen' );
249+ }
250+ }
0 commit comments