• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# encoding=utf-8
3# ============================================================================
4# @brief    Mem Script file
5
6# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11#     http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18# ============================================================================
19import os
20import sys
21import errno
22import re
23
24# Security Core - Test Suite
25# --------------------------
26#
27#     Section             Start Addr   Size     Length
28#     -------             ----------   ------   -------
29#
30#     Flash               0x10000000   0x31f80  0x13360 = 78688
31#         startup         0x10000000               0xa0
32#         .flash_version  0x100000c0               0x28
33#         .text           0x10000100            0x118b4
34#         .ARM.exidx      0x100119b4              0xf08
35#         .ARM.extab      0x100128bc              0x924
36#         .ram_text       0x100131e0               0x88
37#         .data           0x10013268               0xf8
38#
39#     SRAM                0x38000000    0x2800   0x27A0 = 10144
40#         .stacks         0x38000000              0x400
41#         .ramtext        0x38000400              0x88 load address 0x100131e0
42#         .data           0x38000488              0xf8 load address 0x10013268
43#         .bss            0x38000580             0x1e34
44#         .preserve       0x380023b4               0x9c
45#         .heap           0x38002450                0x0
46#         .ipc_mailbox    0x38002700               0xa0
47
48flag_names = [
49    'CONTENTS',
50    'ALLOC',
51    'LOAD',
52    'READONLY',
53    'CODE',
54    'DATA',
55    'DEBUGGING']
56
57
58class Section:
59    def __init__(self):
60        self.name = ''
61        self.size = 0
62        self.vma = 0
63        self.lma = 0
64        self.flags = ''
65        self.exclude = False
66
67    def get_name(self):
68        return self.name
69
70    def get_size(self):
71        return self.size
72
73    def is_loaded(self):
74        if(not self.exclude):
75            if(self.lma != self.vma):
76                if('LOAD' in self.flags):
77                    return True
78        return False
79
80    def get_load_section(self):
81        load_section = Section()
82        load_section.name = self.name
83        load_section.size = self.size
84        load_section.vma = self.lma
85        load_section.lma = self.lma
86        load_section.flags = self.flags
87        return load_section
88
89    def display(self):
90        size_str = "%d" % self.size
91        if(self.vma == self.lma):
92            print(
93                "    %-16s  0x%08X  %10s  %8s                  " %
94                (self.name, self.vma, "", size_str))
95        else:
96            print("    %-16s  0x%08X  %10s  %8s load address 0x%X" %
97                  (self.name, self.vma, "", size_str, self.lma))
98
99    def display_raw(self):
100        print("<%-16s  0x%08X  0x%08X  0x%08X  %s>" %
101              (self.name, self.vma, self.lma, self.size, self.flags))
102
103    def get_all_size(self, size_list):
104        temp = "{0}:{1}:{2}".format(self.name, self.vma, self.size)
105        size_list.append(temp)
106
107
108class Region:
109    def __init__(self, name, origin, length):
110        self.name = name
111        self.origin = origin
112        self.length = length
113        self.sections = []
114
115    def get_name(self):
116        return self.name
117
118    # Get used_size or region length, if it does not contain any sections
119    def get_size(self):
120        used_size = self.used_size()
121        if(used_size > 0):
122            return used_size
123        return self.length
124
125    def add_section(self, section):
126        if(not section.exclude):
127            if((section.vma >= self.origin) and
128               (section.vma < (self.origin + self.length))):
129                self.sections.append(section)
130                return True
131        return False
132
133    def find_section(self, name):
134        if name in self.name:
135            return self
136
137        for section in self.sections:
138            if name in section.name:
139                return Region(section.name, section.vma, section.size)
140        return None
141
142    # Calculate region size used based on contained sections
143    def used_size(self):
144        # Begin with start at end of memory region...
145        start = self.origin + self.length
146        end = self.origin                   # and end at beginning
147        for section in self.sections:
148            if(section.vma < start):
149                start = section.vma
150            if((section.vma + section.size) > end):
151                end = (section.vma + section.size)
152        if(start < end):
153            return end - start  # Beginning of first section to end of last
154        else:
155            return 0            # No sections cotained by this memory region
156
157    def display(self):
158        used_size = self.used_size()
159        if(used_size > 0):
160            used_size_str = "0x%X" % used_size
161            length_str = "0x%X" % self.length
162            print("  ")
163            print("  %-18s  0x%08X  %10s  %8s = %d" %
164                  (self.name, self.origin, length_str, used_size_str,
165                   used_size))
166            for section in self.sections:
167                section.display()
168
169    def get_section_size(self, section_name):
170        size_list = []
171        for section in self.sections:
172            section.get_all_size(size_list)
173        for val in size_list:
174            if section_name in val:
175                return int(val.split(":")[2])
176        return 0
177
178    def get_section_used_size(self, section_name):
179        size_list = []
180        for section in self.sections:
181            section.get_all_size(size_list)
182        for i, x in enumerate(size_list):
183            if section_name in x and i != (len(size_list) - 1):
184                return x.split(":")[-1]
185        return 0
186
187
188class Memory:
189    def __init__(self):
190        self.regions = []
191        self.bth_list = [0] * 21
192        self.btc_list = [0] * 21
193        self.plt_list = [0] * 21
194        self.all_size = 0
195        self.filename = ""
196        self.chipname = ""
197        self.all_code_rodata_list = 0
198        self.all_data_list = 0
199        self.all_bss_list = 0
200        self.all_memory_size = 985088
201
202    def add_region(self, region):
203        self.regions.append(region)
204
205    def add_section(self, section):
206        for region in self.regions:
207            if(region.add_section(section)):
208                return True
209        return False
210
211    def find_section(self, name):
212        for region in self.regions:
213            section = region.find_section(name)
214            if section is not None:
215                return section
216        return None
217
218    def display(self):
219        print("  %-18s  %10s  %10s  %8s" %
220              ('Section', 'Start Addr', 'Size', 'Used'))
221        print("  %-18s  %10s  %10s  %8s" %
222              ('------------------', '----------', '----------', '--------'))
223        for region in self.regions:
224            region.display()
225
226    def summary_display(self, lst_file_name):
227        print("  ")
228        print("  %-18s  %10s  %10s  %8s  %8s" %
229              ('Section', 'Total', 'BTH', 'BTC', 'PLT'))
230        print("  %-18s  %10s  %10s  %8s  %8s" %
231              ('------------------', '----------', '----------', '--------',
232               '--------'))
233        print("  ")
234        self.memory_display(lst_file_name)
235        print("  %-18s  %10s  %10s  %8s" %
236              ('Section', 'Start Addr', 'Size', 'Used'))
237        print("  %-18s  %10s  %10s  %8s" %
238              ('------------------', '----------', '----------', '--------'))
239        for region in self.regions:
240            region.display()
241
242    def get_region_section_size(self, region_name, section_name):
243        for region in self.regions:
244            if region_name in region.name:
245                return region.get_section_size(section_name)
246        return 0
247
248    def get_region_section_used_size(self, region_name, section_name):
249        for region in self.regions:
250            if region_name in region.name:
251                return region.get_section_used_size(section_name)
252
253    def set_init_param(self, name, index, value):
254        if name in "btc_list":
255            self.btc_list[index] = value
256        elif name in "plt_list":
257            self.plt_list[index] = value
258        elif name in "bth_list":
259            self.bth_list[index] = value
260
261
262# Used to aid parsing lines of text from a Lst file
263class SectionLine:
264    def __init__(self, line):
265        self.line = line
266
267    def update(self, line):
268        self.line = line
269
270    def append(self, line):
271        self.line += line
272
273    def length(self):
274        return len(self.line)
275
276    def find_space(self, pos):
277        length = len(self.line)
278        while((pos < length) and not self.line[pos].isspace()):
279            pos = pos + 1
280        if(pos < length):
281            return pos
282        return length
283
284    def find_nonspace(self, pos):
285        length = len(self.line)
286        while((pos < length) and self.line[pos].isspace()):
287            pos = pos + 1
288        if(pos < length):
289            return pos
290        return length
291
292    def get_word(self, index):
293        start = self.find_nonspace(0)
294        end = self.find_space(start)
295        while(index > 0):
296            index = index - 1
297            start = self.find_nonspace(end)
298            end = self.find_space(start)
299        return self.line[start:end]
300
301    def contains(self, line):
302        if(line in self.line):
303            return True
304        return False
305
306
307class LstFile:
308    def __init__(self, fp_filename, filename, lds_file, chip_name, chip_version):
309        self.file = fp_filename
310        self.line = SectionLine("")
311        self.chip_name = chip_name
312        self.memory = self.create_memory_map(lds_file)
313        self.memory.chipname = chip_name
314        self.filename = filename
315        self.chip_version = chip_version
316
317    def __del__(self):
318        self.file.close()
319
320    def valid_section_line(self):
321        if(self.line.length() > 0):
322            if(self.line.get_word(0).isdigit()):
323                return True
324        return False
325
326    def get_next_section(self):
327        if(self.read_section_line()):
328            if(self.valid_section_line()):
329                section = self.parse_section_line()
330                if(section is not None):
331                    # Move this knowledge into Section?
332                    # Not a physical section
333                    if(('READONLY' in section.flags) and
334                       ('CODE' not in section.flags) and
335                       ('DATA' not in section.flags)):
336                        section.exclude = True
337                    # Debugging sections do not appear in binary
338                    if('DEBUGGING' in section.flags):
339                        section.exclude = True
340                    # Section might have a phantom load address
341                    if((section.lma != section.vma) and
342                       ('LOAD' not in section.flags)):
343                        section.lma = section.vma
344                    return section
345        return None
346
347    def get_first_section(self):
348        self.file.seek(0)
349        if(self.find_file_sections()):
350            return self.get_next_section()
351        return None
352
353    def find_file_sections(self):
354        file_line = self.file.readline()
355        while(len(file_line) > 0):
356            if(file_line.find('Sections:') == 0):
357                return True
358            file_line = self.file.readline()
359        return False
360
361    def read_section_line(self):
362        file_line = SectionLine(self.file.readline())
363        while(file_line.length() > 0):
364            if(file_line.contains('SYMBOL TABLE:')):
365                return False
366
367            # Use split instead?  # Place in a function
368            if(file_line.get_word(0).isdigit()):
369                file_line.append(self.file.readline())
370                self.line = file_line
371                return True
372            else:
373                file_line.update(self.file.readline())
374        return False
375
376    def parse_section_line(self):
377        # Use split instead?  # Place in a function
378        if(self.line.get_word(0).isdigit()):
379            section = Section()
380            section.name = self.line.get_word(1)
381            section.size = int(self.line.get_word(2), 16)
382            section.vma = int(self.line.get_word(3), 16)
383            section.lma = int(self.line.get_word(4), 16)
384            section.flags = ''
385            for flag_name in flag_names:
386                if(self.line.contains(flag_name)):
387                    section.flags = "%s_%s_" % (section.flags, flag_name)
388            return section
389        return None
390
391    def create_memory_map(self, lds_file):
392        memory = Memory()
393        with open(lds_file, 'r') as f:
394            text = f.read()
395            mem_map = re.findall(r"([A-Za-z0-9_]*?) *: *ORIGIN *= *([\(\)0-9A-Fa-f/\+\-x* ]*), *LENGTH *= *([\(\)0-9A-Fa-f/\+\-x* ]*)", text)
396            for item in mem_map:
397                memory.add_region(Region(item[0], int(eval(item[1])), int(eval(item[2]))))
398
399        memory.add_region(Region("Unexpected", 0x00000000, 0xFFFFFFFF))
400
401        return memory
402
403    # Should this create and return a ProcessedLstFile, which contains the two
404    # function below?
405    def process(self):
406        section = self.get_first_section()
407        while(section is not None):
408            if(self.memory.add_section(section)):
409                if(section.is_loaded()):
410                    load_section = section.get_load_section()
411                    self.memory.add_section(load_section)
412            section = self.get_next_section()
413
414    def find_section(self, name):
415        return self.memory.find_section(name)
416
417    def display(self):
418        print("\n  Memory Usage Summary...")
419        self.memory.filename = self.filename
420        self.memory.display()
421        print("\n  ====================================================\n")
422
423
424
425display_depth = {
426    '.': 99,
427    'platform': 3,
428    'drivers': 6,
429    'hal': 6,}
430min_display_level = 1
431
432
433class SectionTree:
434    last_display_level = 0
435    last_section_size = 0
436
437    def __init__(self, name, level):
438        self.root = name
439        self.level = level
440        self.size = 0
441        self.children = []
442
443    def get_name(self):
444        return self.root
445
446    def get_size(self):
447        return self.size
448
449    def get_children(self):
450        return self.children
451
452    def find(self, name):
453        if(self.root == name):
454            return self
455        for child in self.children:
456            found_tree = child.find(name)
457            if(found_tree is not None):
458                return found_tree
459        return None
460
461    def add_content(self, size, content):
462        assert len(content) > 0
463
464        if(size > 0):
465            self.size += size
466
467            if(len(content) > 1):
468                content_added = False
469                for child in self.children:
470                    if(child.root == content[0]):
471                        child.add_content(size, content[1:])
472                        content_added = True
473                if not content_added:
474                    new_child = SectionTree(content[0], self.level + 1)
475                    self.children.append(new_child)
476                    new_child.add_content(size, content[1:])
477
478    def display(self, depth):
479        if(self.root in display_depth):
480            depth = display_depth[self.root]
481        if(depth >= 0):
482            if(self.level < SectionTree.last_display_level):
483                print()
484                SectionTree.last_section_size = 0
485            SectionTree.last_display_level = self.level
486
487            if(self.level == min_display_level):
488
489                print("\n  Section                            Total     Size")
490                print("\n  --------------------------------  --------  -----")
491                print("\n")
492
493            if(self.level >= min_display_level):
494                if(self.root.endswith(".c") or self.root.endswith(".s")):
495                    name = self.root[:len(self.root) - 2]
496                else:
497                    name = self.root
498
499                # A top level section name
500                if(self.level == min_display_level):
501                    SectionTree.last_section_size = self.size
502                    print("  %-32s  %8d" %
503                          (((self.level - min_display_level) * '  ') + name,
504                           self.size))
505                    print()
506                # Last entry being dealt with so show its size
507                elif((depth == 0) or (len(self.children) == 0)):
508                    SectionTree.last_section_size = 0
509                    print("  %-42s  %8d" %
510                          (((self.level - min_display_level) * '  ') + name,
511                           self.size))
512                # Has children and size not the same as last size displayed
513                elif(len(self.children) > 0 and
514                     (SectionTree.last_section_size != self.size)):
515                    SectionTree.last_section_size = self.size
516                    print("  %-32s  %8d" %
517                          (((self.level - min_display_level) * '  ') + name,
518                           self.size))
519
520                else:
521                    print("  %-42s" %
522                          (((self.level - min_display_level) * '  ') + name))
523
524            if(depth > 0):
525                for child in self.children:
526                    child.display(depth - 1)
527
528    def get_section_child(
529            self,
530            depth,
531            section_name,
532            tree_data_list,
533            start_flag,
534            end_flag):
535
536        if(self.root in display_depth):
537            depth = display_depth[self.root]
538        if(depth >= 0):
539            if(self.level < SectionTree.last_display_level):
540                SectionTree.last_section_size = 0
541            SectionTree.last_display_level = self.level
542
543            if(self.level == min_display_level):
544                if start_flag[-1] == 1:
545                    end_flag[-1] = 1
546
547            if start_flag[-1] and end_flag[-1]:
548                return 1
549
550            if(self.level >= min_display_level):
551                if(self.root.endswith(".c") or self.root.endswith(".s")):
552                    name = self.root[:len(self.root) - 2]
553                else:
554                    name = self.root
555
556                if(self.level == min_display_level):
557                    SectionTree.last_section_size = self.size
558
559                    if name == section_name:
560                        tree_data_list.append(
561                            ["{0}:{1}".format(name, self.size)])
562                        start_flag[-1] = 1
563
564                # Last entry being dealt with so show its size
565                elif((depth == 0) or (len(self.children) == 0)):
566                    SectionTree.last_section_size = 0
567
568                    if start_flag[-1] == 1:
569                        tree_data_list[-1].append(
570                            "{0}:{1}".format(name, self.size))
571                # Has children and size not the same as last size displayed
572                elif(len(self.children) > 0 and
573                     (SectionTree.last_section_size != self.size)):
574                    SectionTree.last_section_size = self.size
575
576                    if start_flag[-1] == 1:
577                        tree_data_list[-1].append(
578                            "{0}:{1}".format(name, self.size))
579
580                else:
581                    if start_flag[-1] == 1:
582                        tree_data_list[-1].append("{0}:{1}".format(name, ""))
583
584            if(depth > 0):
585                for child in self.children:
586                    if(child.get_section_child(depth - 1, section_name,
587                       tree_data_list, start_flag, end_flag)):
588                        return 1
589
590
591class DuFile:
592    def __init__(self, fp_filename):
593        self.file = fp_filename
594        # Construct empty tree with '.' as root, since all pathnames in .du
595        # file
596        # begin in the same way
597        self.section_tree = SectionTree('.', 0)
598
599    def __del__(self):
600        self.file.close()
601
602    def get_next_section_content(self):
603        # e.g. "216
604        # ./.text/drivers/non-os/ipc/shared/ipc.c/ipc_send_message"
605        file_line = SectionLine(self.file.readline())
606        if(file_line.length() > 0):
607            # Obtains size e.g. "216"
608            size = file_line.get_word(0)
609            # Last line of .du file begins with non-numeric characters
610            if(size.isdigit()):
611                size = int(size)
612                # Generates a list of words, separated by '/' e.g.
613                # ".",".text","drivers","non-os","ipc" etc
614                content = file_line.get_word(1).split(os.sep)
615                return (size, content)
616        return (0, None)
617
618    def get_first_section_content(self):
619        # Point to beginning of file
620        self.file.seek(0)
621        return self.get_next_section_content()
622
623    # Should this create and return a ProcessedDuFile, which will contain the
624    # two function below?
625    def process(self):
626        # Scan entire file contents
627        (size, content) = self.get_first_section_content()
628        while(content is not None):
629            self.section_tree.add_content(size, content[1:])
630            (size, content) = self.get_next_section_content()
631
632    def find_section(self, name):
633        return self.section_tree.find(name)
634
635    def display(self):
636        print("\n  Memory Usage Details...")
637        # Use a 0 depth here as it will be overridden by entries in
638        # display_depth
639        self.section_tree.display(0)
640        print("\n  ====================================================\n")
641
642    def get_tree_size(self, section_name, path_name):
643        tree_data_list = []
644        flag_list = []
645        start_flag = [0]
646        end_flag = [0]
647        self.section_tree.get_section_child(
648            0, section_name, tree_data_list, start_flag, end_flag)
649
650        for i in range(len(tree_data_list[-1])):
651            if path_name[0] == tree_data_list[-1][i].split(":")[0]:
652                flag_list.append(1)
653                for path_index in range(len(path_name[1:])):
654                    if path_name[1:][path_index] == tree_data_list[-1][
655                        i + 1 + path_index].split(":")[0]:
656                        flag_list.append(1)
657                    else:
658                        flag_list.append(0)
659                if len(flag_list) == sum(flag_list):
660                    size = tree_data_list[-1][i].split(":")[1]
661                    if size == '':
662                        size = 0
663                    return int(size)
664                else:
665                    flag_list = []
666
667
668def display_mismatches(lst_file, du_file):
669    section_names = [
670        "startup",
671        ".flash_version",
672        ".text",
673        ".ramtext",
674        ".data",
675        ".bss",
676        ".preserve",
677        ".sys_status",
678        ".ipc_mailbox",
679        "btc_ramtext",
680        "bth_ramtext",
681        "plt_ramtext",
682        "bth_share_ramtext",
683        "btc_data",
684        "bth_data",
685        "plt_data",
686        "btc_bss",
687        "bth_bss",
688        "plt_bss"]
689
690    print("  Mismatched section sizes...\n")
691    for section_name in section_names:
692        lst_section = lst_file.find_section(section_name)
693        du_section = du_file.find_section(section_name)
694        if lst_section is not None and du_section is not None:
695            lst_size = lst_section.get_size()
696            du_size = du_section.get_size()
697            if(lst_size != du_size):
698                print(
699                    "  %12s  .lst file = %d (0x%X)" %
700                    (section_name, lst_size, lst_size))
701                print("  %12s   .du file = %d (0x%X)" %
702                      ("", du_size, du_size))
703                print()
704    print("  ====================================================\n")
705
706
707def main(lst_file_name, du_file_name, lds_file, chip_name, chip_version=None):
708    with open(lst_file_name, "r") as fp_lst_file_name, open(
709            du_file_name, "r") as fp_du_file_name:
710        lst_file = LstFile(
711            fp_lst_file_name,
712            lst_file_name,
713            lds_file,
714            chip_name,
715            chip_version)
716        lst_file.process()
717        du_file = DuFile(fp_du_file_name)
718        du_file.process()
719
720        lst_file.display()
721
722        du_file.display()
723        display_mismatches(lst_file, du_file)
724
725
726if __name__ == "__main__":
727    if(len(sys.argv) == 5):
728        main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
729    else:
730        print(
731            "Usage: %s <lstfile> <dufile> <chip>" %
732            os.path.basename(
733                sys.argv[0]))
734