|
4 | 4 | # ------------------------------------------------------------------------------ |
5 | 5 | # The MIT License (MIT) |
6 | 6 | # |
7 | | -# Copyright (c) 2021-2024 Aarno Labs, LLC |
| 7 | +# Copyright (c) 2021-2025 Aarno Labs, LLC |
8 | 8 | # |
9 | 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | 10 | # of this software and associated documentation files (the "Software"), to deal |
|
51 | 51 | - DataBlocks |
52 | 52 | pairs of addresses (start inclusive, end exclusive) that indicate data |
53 | 53 |
|
| 54 | +- FunctionAnnotations |
| 55 | + register and stack variable introductions |
| 56 | +
|
54 | 57 | - FunctionEntryPoints |
55 | 58 | addresses of function entry points |
56 | 59 |
|
|
102 | 105 | from chb.userdata.UserBType import UserStructBType |
103 | 106 |
|
104 | 107 | import chb.util.fileutil as UF |
| 108 | +from chb.util.loggingutil import chklogger |
105 | 109 | import chb.util.xmlutil as UX |
106 | 110 |
|
107 | 111 |
|
@@ -451,6 +455,245 @@ def __str__(self) -> str: |
451 | 455 | return "\n".join(lines) |
452 | 456 |
|
453 | 457 |
|
| 458 | +class RegisterVarIntro: |
| 459 | + |
| 460 | + def __init__(self, d: Dict[str, Union[str, List[str]]]) -> None: |
| 461 | + self._d = d |
| 462 | + |
| 463 | + @property |
| 464 | + def varintro(self) -> Dict[str, Union[str, List[str]]]: |
| 465 | + return self._d |
| 466 | + |
| 467 | + @property |
| 468 | + def iaddr(self) -> str: |
| 469 | + if not "iaddr" in self.varintro: |
| 470 | + chklogger.logger.warning( |
| 471 | + "Register variable intro without address; returning 0x0") |
| 472 | + return cast(str, self.varintro.get("iaddr", "0x0")) |
| 473 | + |
| 474 | + @property |
| 475 | + def name(self) -> str: |
| 476 | + if not "name" in self.varintro: |
| 477 | + chklogger.logger.warning( |
| 478 | + "Register variable intro without name; returning noname") |
| 479 | + return cast(str, self.varintro.get("name", "noname")) |
| 480 | + |
| 481 | + @property |
| 482 | + def typename(self) -> Optional[str]: |
| 483 | + return cast(str, self.varintro.get("typename", None)) |
| 484 | + |
| 485 | + @property |
| 486 | + def ispointerto(self) -> bool: |
| 487 | + return "ptrto" in self.mods |
| 488 | + |
| 489 | + @property |
| 490 | + def mods(self) -> List[str]: |
| 491 | + return cast(List[str], self.varintro.get("mods", [])) |
| 492 | + |
| 493 | + def has_cast(self) -> bool: |
| 494 | + return "cast" in self.mods |
| 495 | + |
| 496 | + def to_xml(self, node: ET.Element) -> None: |
| 497 | + xvintro = ET.Element("vintro") |
| 498 | + node.append(xvintro) |
| 499 | + xvintro.set("name", self.name) |
| 500 | + xvintro.set("iaddr", self.iaddr) |
| 501 | + if self.typename is not None: |
| 502 | + xvintro.set("typename", self.typename) |
| 503 | + if self.ispointerto: |
| 504 | + xvintro.set("ptrto", "yes") |
| 505 | + if self.has_cast(): |
| 506 | + xvintro.set("cast", "yes") |
| 507 | + |
| 508 | + def __str__(self) -> str: |
| 509 | + return ( |
| 510 | + "iaddr: " + self.iaddr |
| 511 | + + "; name: " + self.name |
| 512 | + + (("; typename: " + self.typename) if self.typename else "")) |
| 513 | + |
| 514 | + |
| 515 | +class StackVarIntro: |
| 516 | + |
| 517 | + def __init__(self, d: Dict[str, str]) -> None: |
| 518 | + self._d = d |
| 519 | + |
| 520 | + @property |
| 521 | + def varintro(self) -> Dict[str, str]: |
| 522 | + return self._d |
| 523 | + |
| 524 | + @property |
| 525 | + def offset(self) -> int: |
| 526 | + if not "offset" in self.varintro: |
| 527 | + chklogger.logger.warning( |
| 528 | + "Stack variable intro without offset; returning 0") |
| 529 | + return int(self.varintro.get("offset", "0")) |
| 530 | + |
| 531 | + @property |
| 532 | + def name(self) -> str: |
| 533 | + if not "name" in self.varintro: |
| 534 | + chklogger.logger.warning( |
| 535 | + "Stack variable intro without name; returning noname") |
| 536 | + return self.varintro.get("name", "noname") |
| 537 | + |
| 538 | + @property |
| 539 | + def typename(self) -> Optional[str]: |
| 540 | + return self.varintro.get("typename", None) |
| 541 | + |
| 542 | + @property |
| 543 | + def arraysize(self) -> Optional[int]: |
| 544 | + if "array" in self.varintro: |
| 545 | + return int(self.varintro.get("array", "0")) |
| 546 | + else: |
| 547 | + return None |
| 548 | + |
| 549 | + @property |
| 550 | + def ispointer(self) -> bool: |
| 551 | + if "ptrto" in self.varintro: |
| 552 | + return self.varintro.get("ptrto", "no") == "yes" |
| 553 | + return False |
| 554 | + |
| 555 | + def to_xml(self, node: ET.Element) -> None: |
| 556 | + xvintro = ET.Element("vintro") |
| 557 | + node.append(xvintro) |
| 558 | + xvintro.set("name", self.name) |
| 559 | + xvintro.set("offset", str(self.offset)) |
| 560 | + if self.typename is not None: |
| 561 | + xvintro.set("typename", self.typename) |
| 562 | + if self.arraysize is not None: |
| 563 | + xvintro.set("arraysize", str(self.arraysize)) |
| 564 | + elif self.ispointer: |
| 565 | + xvintro.set("ptrto", "yes") |
| 566 | + |
| 567 | + def __str__(self) -> str: |
| 568 | + return ( |
| 569 | + "offset: " + str(self.offset) |
| 570 | + + "; name: " + self.name |
| 571 | + + (("; typename: " + self.typename) if self.typename else "")) |
| 572 | + |
| 573 | + |
| 574 | +class FunctionAnnotation: |
| 575 | + |
| 576 | + def __init__(self, fnannotation: Dict[str, Any]) -> None: |
| 577 | + self._fnannotation = fnannotation |
| 578 | + |
| 579 | + @property |
| 580 | + def fnannotation(self) -> Dict[str, Any]: |
| 581 | + return self._fnannotation |
| 582 | + |
| 583 | + @property |
| 584 | + def faddr(self) -> str: |
| 585 | + if not "faddr" in self.fnannotation: |
| 586 | + chklogger.logger.warning( |
| 587 | + "Function annotation without faddr, returning 0x0") |
| 588 | + return self.fnannotation.get("faddr", "0x0") |
| 589 | + |
| 590 | + @property |
| 591 | + def stack_variable_introductions(self) -> Dict[int, StackVarIntro]: |
| 592 | + result: Dict[int, StackVarIntro] = {} |
| 593 | + for d in self.fnannotation.get("stack-variable-introductions", []): |
| 594 | + svi = StackVarIntro(d) |
| 595 | + result[svi.offset] = svi |
| 596 | + return result |
| 597 | + |
| 598 | + @property |
| 599 | + def register_variable_introductions(self) -> Dict[str, RegisterVarIntro]: |
| 600 | + result: Dict[str, RegisterVarIntro] = {} |
| 601 | + for d in self.fnannotation.get("register-variable-introductions", []): |
| 602 | + rvi = RegisterVarIntro(d) |
| 603 | + result[rvi.iaddr] = rvi |
| 604 | + return result |
| 605 | + |
| 606 | + def has_register_variable_introduction(self, iaddr: str) -> bool: |
| 607 | + return iaddr in self.register_variable_introductions |
| 608 | + |
| 609 | + def get_register_variable_introduction(self, iaddr: str) -> RegisterVarIntro: |
| 610 | + rvintros = self.register_variable_introductions |
| 611 | + if iaddr in rvintros: |
| 612 | + return rvintros[iaddr] |
| 613 | + raise UF.CHBError("No register variable introductions for " + iaddr) |
| 614 | + |
| 615 | + def to_xml(self, node: ET.Element) -> None: |
| 616 | + node.set("faddr", self.faddr) |
| 617 | + if len(self.stack_variable_introductions) > 0: |
| 618 | + xstackintros = ET.Element("stackvar-intros") |
| 619 | + node.append(xstackintros) |
| 620 | + for svintro in self.stack_variable_introductions.values(): |
| 621 | + svintro.to_xml(xstackintros) |
| 622 | + if len(self.register_variable_introductions) > 0: |
| 623 | + xregintros = ET.Element("regvar-intros") |
| 624 | + node.append(xregintros) |
| 625 | + for rvintro in self.register_variable_introductions.values(): |
| 626 | + rvintro.to_xml(xregintros) |
| 627 | + |
| 628 | + def __str__(self) -> str: |
| 629 | + lines: List[str] = [] |
| 630 | + lines.append("Function: " + self.faddr) |
| 631 | + if len(self.stack_variable_introductions) > 0: |
| 632 | + lines.append(" Stack variable introductions:") |
| 633 | + for svintro in self.stack_variable_introductions: |
| 634 | + lines.append(" " + str(svintro)) |
| 635 | + if len(self.register_variable_introductions) > 0: |
| 636 | + for rvintro in self.register_variable_introductions: |
| 637 | + lines.append(" " + str(rvintro)) |
| 638 | + lines.append("") |
| 639 | + return "\n".join(lines) |
| 640 | + |
| 641 | + |
| 642 | +class FunctionAnnotations(HintsEntry): |
| 643 | + """List of functions with variable introductions. |
| 644 | +
|
| 645 | + Supersedes variable-introductions and stack-variable-introductions. |
| 646 | + """ |
| 647 | + |
| 648 | + def __init__(self, fnannotations: List[Dict[str, Any]]) -> None: |
| 649 | + HintsEntry.__init__(self, "function-annotations") |
| 650 | + self._fnannotations = fnannotations |
| 651 | + |
| 652 | + @property |
| 653 | + def fnannotations(self) -> List[Dict[str, Any]]: |
| 654 | + return self._fnannotations |
| 655 | + |
| 656 | + def has_function(self, faddr: str) -> bool: |
| 657 | + for a in self.fnannotations: |
| 658 | + if faddr == a.get("faddr", "0x0"): |
| 659 | + return True |
| 660 | + else: |
| 661 | + return False |
| 662 | + |
| 663 | + def get_function(self, faddr: str) -> FunctionAnnotation: |
| 664 | + for a in self.fnannotations: |
| 665 | + if faddr == a.get("faddr", "0x0"): |
| 666 | + return FunctionAnnotation(a) |
| 667 | + raise UF.CHBError("No annotations found for function address " + faddr) |
| 668 | + |
| 669 | + def update(self, l: List[Dict[str, Any]]) -> None: |
| 670 | + for d in l: |
| 671 | + faddr = d.get("faddr", "0x0") |
| 672 | + if self.has_function(faddr): |
| 673 | + chklogger.logger.warning( |
| 674 | + "Duplicate record for function annotations: %s. " |
| 675 | + + "New entry is ignored", |
| 676 | + faddr) |
| 677 | + else: |
| 678 | + self._fnannotations.append(d) |
| 679 | + |
| 680 | + def to_xml(self, node: ET.Element) -> None: |
| 681 | + xfnannotations = ET.Element(self.name) |
| 682 | + node.append(xfnannotations) |
| 683 | + for a in self.fnannotations: |
| 684 | + xfa = ET.Element("function-annotation") |
| 685 | + FunctionAnnotation(a).to_xml(xfa) |
| 686 | + xfnannotations.append(xfa) |
| 687 | + |
| 688 | + def __str__(self) -> str: |
| 689 | + lines: List[str] = [] |
| 690 | + lines.append("Function annotations") |
| 691 | + lines.append("--------------------") |
| 692 | + for a in self.fnannotations: |
| 693 | + lines.append(str(FunctionAnnotation(a))) |
| 694 | + return "\n".join(lines) |
| 695 | + |
| 696 | + |
454 | 697 | class FunctionEntryPointsHints(HintsEntry): |
455 | 698 | """List of function entry points in hex.""" |
456 | 699 |
|
@@ -1131,6 +1374,19 @@ def variable_introductions(self) -> Dict[str, str]: |
1131 | 1374 | else: |
1132 | 1375 | return {} |
1133 | 1376 |
|
| 1377 | + def function_annotations(self) -> Optional[FunctionAnnotations]: |
| 1378 | + if "function-annotations" in self.astdata: |
| 1379 | + return cast( |
| 1380 | + FunctionAnnotations, self.astdata["function-annotations"]) |
| 1381 | + return None |
| 1382 | + |
| 1383 | + def function_annotation(self, faddr: str) -> Optional[FunctionAnnotation]: |
| 1384 | + fnannotations = self.function_annotations() |
| 1385 | + if fnannotations is not None: |
| 1386 | + if fnannotations.has_function(faddr): |
| 1387 | + return fnannotations.get_function(faddr) |
| 1388 | + return None |
| 1389 | + |
1134 | 1390 | def stack_variable_introductions(self) -> Dict[str, Dict[int, str]]: |
1135 | 1391 | """Return map from function address to stack offset to variable name. |
1136 | 1392 |
|
@@ -1242,6 +1498,20 @@ def add_hints(self, hints: Dict[str, Any]) -> None: |
1242 | 1498 | else: |
1243 | 1499 | self.userdata[tag] = FunctionEntryPointsHints(fepoints) |
1244 | 1500 |
|
| 1501 | + if "function-annotations" in hints: |
| 1502 | + tag = "function-annotations" |
| 1503 | + fnannotations: List[Dict[str, Any]] = hints[tag] |
| 1504 | + if self._toxml: |
| 1505 | + if tag in self.userdata: |
| 1506 | + self.userdata[tag].update(fnannotations) |
| 1507 | + else: |
| 1508 | + self.userdata[tag] = FunctionAnnotations(fnannotations) |
| 1509 | + else: |
| 1510 | + if tag in self.astdata: |
| 1511 | + self.astdata[tag].update(fnannotations) |
| 1512 | + else: |
| 1513 | + self.astdata[tag] = FunctionAnnotations(fnannotations) |
| 1514 | + |
1245 | 1515 | if "function-names" in hints: |
1246 | 1516 | tag = "function-names" |
1247 | 1517 | fnames: Dict[str, str] = hints[tag] |
|
0 commit comments