Skip to content

Clang source-level coverage profiling instrumentation does not honor the attribute naked #127498

Open
@miyuki

Description

@miyuki

Clang source-level instrumentation (the -fprofile-instr-generate option) does not honor the function attribute naked and generates a prologue that might interfere with the function body. For example, the following code

extern "C" __attribute__((naked))
void semihost(unsigned op __attribute__((unused)),
              unsigned int* c __attribute__((unused))) {
  __asm(
    "PUSH {R4, LR}  \n"
    "SVC  #0xab     \n"
    "POP  {R4, PC}  \n"
  );
}

When compiled with the current trunk build of Clang, using the flags -target arm-none-eabi -march=armv8-a -O -fprofile-instr-generate produces the following LLVM IR for the function semihost:

define dso_local void @semihost(i32 noundef %0, ptr noundef %1) #0 {
  %3 = load i64, ptr @__profc_semihost, align 8
  %4 = add i64 %3, 1
  store i64 %4, ptr @__profc_semihost, align 8
  tail call void asm sideeffect "PUSH {R4, LR}  \0ASVC  #0xab     \0APOP  {R4, PC}  \0A", ""() #1
  unreachable
}

Which, in turn, gets compiled into the following assembly:

semihost:
        movw    r0, :lower16:.L__profc_semihost
        movt    r0, :upper16:.L__profc_semihost
        ldrd    r2, r3, [r0]
        adds    r2, r2, #1
        adc     r3, r3, #0
        strd    r2, r3, [r0]
        push    {r4, lr}
        svc     #171
        pop     {r4, pc}

This is incorrect because the function parameters are passed in the registers r0 and r1, and they get overwritten.

I think the correct behavior would be not to instrument naked functions, similarly to how it is done for IR-level coverage: #108552

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions