banner
yono

yono

哈喽~欢迎光临
follow
github

Why not use MicroLib and printf — Arm's Semihosting

Conclusion#

The conclusion is written at the beginning

  1. First, not using microLib is to ensure the source code behavior is controllable, and it won't be altered by the vendor's reckless changes to the lib, while also giving the source code cross-platform capability, avoiding behavioral differences due to different C standard libraries.

  2. From this, it can be inferred that if microLib is not used, the original libc will be introduced, and many interaction functions in the C source library will generate Semihosting code, causing the program to run sluggishly under debug and unable to run outside of debug.

  3. In summary, if microLib is not used, the use of C standard library functions must be very cautious to avoid system interaction functions that generate Semihosting code.

The Ancestor's Law Cannot Be Changed#

Since I first entered the industry, my boss has firmly prohibited the use of printf series functions, or simply said to prohibit all libc functions, including malloc, memset, strcpy, etc., or to only introduce stdint.h, stdlib.h, stdbool.h from the standard C library. This has led to a massive amount of custom implementations for string operations, buffer operations, trace logs, etc. If malloc can still be justified as a way to avoid stack overflow in threads, static memory can make bug scale controllable; what is the reason that other implementations cannot be used?

In summary, the ancestor's law cannot be changed, and it is still the case in current projects.

Clue#

While watching this guy's introduction to bootloader vulnerabilities (which is actually also a stack operation issue), I came across the following article. Perhaps it can answer the origin of this ancestor's law.

Is Semihosting Really the Embedded Appendix? - Tencent Cloud Developer Community - Tencent Cloud (tencent.com)

Verification#

Some of the code is as follows. clock(); is a typical code that generates system interaction. In this project, microLib has been removed.

#include <stdio.h>
#include <time.h>

int main(void)
{
    // Some initialization code omitted
    while (1)
  	{
        clock_t tTime = clock();
        HAL_GPIO_TogglePin(GPIOH, GPIO_PIN_5);
        HAL_Delay(500);
    }
}

Running under debug

First, the disassembly related to the clock() function, which jumps to address 0x0800037C to execute the clock() function.

   109: clock_t tTime = clock(); 
0x08002386 F7FDFFF9  BL.W          0x0800037C clock
0x0800238A 9001      STR           r0,[sp,#0x04]
0x0800238C F6414000  MOVW          r0,#0x1C00
0x08002390 F6C50002  MOVT          r0,#0x5802
0x08002394 2120      MOVS          r1,#0x20

The clock() function appears, and indeed a BKPT soft breakpoint occurs.

Here, BKPT is the Cortex-M Break Point (software breakpoint) instruction, and the constant 0xAB is the special code for Semihosting. If the debugging tool happens to support Semihosting, the software may not stop and run normally. But if power is lost and restarted, it becomes problematic; “Executing the BKPT instruction in non-debug mode will directly cause the Cortex-M processor to enter Hardfault.”

0x0800037C 2100      MOVS          r1,#0x00
0x0800037E 2010      MOVS          r0,#0x10
0x08000380 BEAB      BKPT          0xAB ; will stop at this line, this is the soft breakpoint of Semihosting code
0x08000382 4905      LDR           r1,[pc,#20]  ; @0x08000398
0x08000384 6809      LDR           r1,[r1,#0x00]
0x08000386 1A40      SUBS          r0,r0,r1

Can Optimization Avoid This?#

In fact, modern compilers are very intelligent, and enabling some optimization can fix many bugs that you may not have noticed.

Even when compiled with O3, the occurrence of BKPT is still unavoidable; this libc should be linked in binary form into the burned file.

Which Functions to Avoid?#

The reference article mentions the following, excerpted here.

1. Standard Input/Output (Standard I/O)#

  • printf series functions: such as printf, fprintf, sprintf, etc., used for formatted output to standard output devices (usually the host console).
  • scanf series functions: such as scanf, fscanf, sscanf, etc., used for formatted input from standard input devices (usually the host keyboard input).

2. File Operations#

  • fopen: open a file.
  • fclose: close a file.
  • fread: read data from a file.
  • fwrite: write data to a file.
  • fseek: move the file pointer to a specified position.
  • ftell: get the current position of the file pointer.
  • fflush: flush the file output buffer.

3. Time and Date#

  • time: get the current time.
  • clock: get processor time.
  • difftime: calculate the time difference between two time points.
  • strftime: format time and date as a string.

4. Error Handling#

  • perror: output error information to the standard error device.
  • strerror: return the error message string corresponding to the error code.

5. System Calls#

  • exit: terminate the program and return a status code.
  • system: execute a system command (rarely used in embedded systems, but may be useful when debugging on the host).

6. Other Auxiliary Functions#

  • getenv: get the value of an environment variable.
  • putenv: set an environment variable (rarely used).
  • remove: delete a file.
  • rename: rename a file.

The Original Article Is Very Interesting, Strongly Recommended to Watch#

Clearly, we fit the characteristics summarized in feature 4, and are even more extreme.

【The Latency and Causes of "Embedded Appendicitis"】

General MacArthur once commented: Seeing a doctor at Baidu, cancer starts. You pseudo-experts make Semihosting sound so terrifying, “even the compiler is implanted by default,” how come I’m still living well? How come I’ve never encountered it?

To be frank, you may fit the following characteristics:

  1. Most of the time you use Arm Compiler 5;
  2. Most of the time you default to using MicroLib;
  3. When not selecting MicroLib under Arm Compiler 6, you encounter the phenomenon of “everything works fine in debug mode, but the program crashes when running directly” — thus silently noting in your notebook that you can only use MicroLib;
  4. Never use libc functions other than malloc, even including printf;
  5. The program template is made by a big shot;
  6. Application development is based on example projects provided by chip manufacturers;
  7. Use software platforms like RT-Thread that provide “one-stop services.”

Don’t be fooled by the many points I listed; they actually fall into two categories:

  1. A blind cat accidentally catches a rat — good luck
  2. Someone is carrying the burden for you

This article is synchronized and updated by Mix Space to xLog. The original link is https://www.yono233.cn/posts/shoot/24_8_13_Semihosting

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.