Skip to content

Code coverage extension doesn't work with blob loader (baremetal binary emulation) #1565

@antcpl

Description

@antcpl

Describe the bug
Code coverage extension particularly drcov (and I'm quite sure ezcov as well) doesn't work with binary emulations that use the blob loader ie baremetal binary emulation.

Sample Code and context
I am working on the dev branch.
My setup is the following one :
I emulate a bare metal binary running on ARM cortex A7 processor. For the global emulation setup I followed this qiling/examples/hello_arm_uboot.py and defined a cortex_a.ql file to setup the memory as required by the blob loader, all this works perfectly.

with open("./baremetal_binary", "rb") as f:
        binary = f.read()

ql = Qiling(code=binary[0x00000000:],archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB , verbose=QL_VERBOSE.DISABLED, cputype=ARM_CPU_MODEL.ARM_CORTEX_A7, profile="cortex_a.ql")

[...]

with cov_utils.collect_coverage(ql, 'drcov_exact', 'output.cov'):
        ql.run(count=150000)

The problem happens when I try to collect coverage using drcov and drcov_exact : the generated coverage file is empty. No error happens during the execution but still the coverage file is empty.

Expected behavior
A non-empty coverage file.

Screenshots
Here is the generated coverage file :

DRCOV VERSION: 2
DRCOV FLAVOR: drcov
Module Table: version 2, count 0
Columns: id, base, end, entry, checksum, timestamp, path
BB Table: 0 bbs

Problem Identification and fix suggestions
I worked on the problem and figure out how to solve it but this require different modifications. As this is a particular qiling use case I don't know what you think about the modifications. These are just suggestions to help your work :)

  • Problem 1 : in the drcov.py file :
 @staticmethod
    def block_callback(ql, address, size, self):
        for mod_id, mod in enumerate(ql.loader.images):
            if mod.base <= address <= mod.end:
                ent = bb_entry(address - mod.base, size, mod_id)
                self.basic_blocks.append(ent)
                break

The for loop is never entered when working with the blob loader because the QlLoaderBLOB has an empty images attribute. Thus, no bb_entry will be appended to the list via the callback and this produce the empty coverage file.

  • Problem 2 : another problem has happened after the fix and is not due my fix. I work with a baremetal binary and no OS thus, addresses in the binary are physical addresses. The following necessary translation in other cases is a problem in mine : in the drcov.py file :
ent = bb_entry(address - mod.base, size, mod_id)       

The "address - mod.base" substraction required in a classical binary creates a problem because the drcov field that normally looks like (address, size, mod_id) has an address that doesn't match the baremetal memory mapping with physical addresses. Thus, when loading the coverage file in IDA for example, errors happen because the drcov parser doesn't recognise the stored address.

  • Fix suggestions : my personnal fix is a working but ugly althought I can still share my reflexions about this. Either you want to let the drcov class most unchanged as possible and this leads to modify the QlLoaderBLOB to initialize images attribute but you will still have to modify the drcov to avoid the address translations based on I don't know which condition. Or Either you modify only the drcov class to cover this case and let the QlLoaderBLOB unchanged. As this is a very particular use case I think the second would be the best.

I am free to try to make the modifications if needed :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions