- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello all,
I have a function like this
void fn()
{
char buf1[160] = {0};
int count = 5;
while(count > 0){
char buf2[16] = {0};
sprintf(buf2, "%s", "aabbccddeeff-123"); //buf2 has always 16 characters
int len = strlen(buf1);
printf("Len: %d\n", len);
memcpy(&buf1[len], buf2, sizeof(buf2));
count--;
}
}
When debug mode is selected, the function works as expected.
Output:
Len 0
Len 16
Len 32
Len 48
Len 64
When release mode is selected, the output is
Len 0
Len 0
Len 0
Len 0
Len 0
I am not sure what's going on. If I make buf1 global variable, the function works as expected in release mode. What kind of optimization is the compiler doing? How do debug this?
Thank you
Dheeraj
- Labels:
-
PSoC 4 Architecture
- Tags:
- cyble-212019
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are copying a 16 character string to the 16-bytes length buf2 array.
sprintf(buf2, "%s", "aabbccddeeff-123"); //buf2 has always 16 characters
In this case, the length of the constant string "aabbccddeeff-123" is 16. But the required area size is 17 including the NULL character indicating the end of the string. Please refer following lines from the LST file.
132 0000 61616262 .ascii "aabbccddeeff-123\000"
132 63636464
132 65656666
132 2D313233
132 00
133 .LC2:
134 0011 4C656E3A .ascii "Len: %d\012\000"
134 2025640A
134 00
So, the sprintf() function copies 17 characters to the buf2 and the last NULL character is stored to the first byte of the buf1. So, the string length of the buf1 is always ZERO. The behavior in the Release mode is correct. Please confirm the value of buf1[0] after the sprintf() execution.
The difference between these modes is the interpretation of the sprintf() function.
54 .LVL2:
22:main.c **** sprintf(buf2, "%s", "aabbccddeeff-123"); //buf2 has always 16 characters
55 .loc 1 22 0
56 001a 0B49 ldr r1, .L5
57 001c 6846 mov r0, sp
58 001e FFF7FEFF bl strcpy
In the "Release" mode the line is converted to a simple strcpy() function. Total 17 characters are copied by this function.
58 .LVL2:
22:main.c **** sprintf(buf2, "%s", "aabbccddeeff-123"); //buf2 has always 16 characters
59 .loc 1 22 0
60 001c 0D4B ldr r3, .L4
61 001e 6A46 mov r2, sp
62 0020 23CB ldmia r3!, {r0, r1, r5}
63 0022 23C2 stmia r2!, {r0, r1, r5}
64 0024 1900 movs r1, r3
65 0026 1B68 ldr r3, [r3]
66 0028 1360 str r3, [r2]
67 002a 0B79 ldrb r3, [r1, #4]
68 002c 1371 strb r3, [r2, #4]
In the "Debug" mode the sprintf() is converted into a set of discrete op-codes. The first 32*3 bits are copied via the {r0, r1, r5} registers. The next 32-bit is copied via the r3 register and the last 17th byte is copied by r3. But the source address of the last byte is stored in r1 at the line 64 and the 13th byte (buf2[12]='-') is copied to the buf1[0]. So, it seems that the behavior is same as the "Release" mode.
I wonder why the compiler converts the sprintf() into such strange code. Does anyone have any idea?
Regards,
Noriaki
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for the reply. I had looked at LST files but I didn't know how to interpret them.
So, the sprintf() function copies 17 characters to the buf2 and the last NULL character is stored to the first byte of the buf1.
How is this possible? buf1 is declared before buf2 so it shouldn't be copied in buf1.
Can you explain how did you reach this statement?
The first 32*3 bits are copied via the {r0, r1, r5} registers. The next 32-bit is copied via the r3 register and the last 17th byte is copied by r3. But the source address of the last byte is stored in r1 at the line 64 and the 13th byte (buf2[12]='-') is copied to the buf1[0].
Regards,
Dheeraj
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I found incorrect description regarding your second question. It seems the behavior is same in "Release" and "Debug" mode. A debug session can be used why the buf1[0] is not destroyed in "Debug" mode, but I don't have any hardware right now.
The character array buf1[160] and buf2[16] are assigned to a memory area on the stack because they are declared as local variables.
The buf2[16] is assigned at (sp+0) and the buf1[160] is assigned at (sp+16) where sp indicates the Stack Pointer pointing the top of the Stack area.
The memory area for buf2[] and buf1[] are assigned consecutively. In addition the C specification does not detect the area overrun of an array. So, if you access to the buf2's 17th byte (buf2[16]) you are accessing the buf1's first byte (buf1[0]) Please confirm the value of buf1[0] in a debugging session.
Regards,
Noriaki
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you. I get it. The stack grows downward. Yes, buf1[0] is null. I understood this part.
A debug session can be used why the buf1[0] is not destroyed in "Debug" mode, but I don't have any hardware right now.
I have CYBLE-212019 module. Any tips on how to debug this?
Regards,
Dheeraj
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
For example, the C-language specification does not have a boundary check of the array index. So, it is recommended to check the index and the size if arrays explicitly with a macro CYASSERT.
CYASSERT(sizeof buf1 >= strlen(buf1) + sizeof buf2 + 1);
memcpy(&buf1[len], buf2, sizeof(buf2));
If the condition is FALSE, CYASSERT halts and debugger can recognize where the CPU stuck. In addition, CYASSERT macro is disabled in Release mode not to increase the program code size.
Regards,
Noriaki
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Regarding the difference between debug mode and release mode,
If I replace the line
sprintf(buf2, "%s", "aabbccddeeff-123"); //buf2 has always 16 characters
with memcpy(buf2, "aabbccddeeff-123", 16);
This doesn't copy the NULL character to buf1[0]. The output works as expected. From the LST output mentioned above, I think the debug mode is using the instructions for memcpy instead of strcpy for sprintf().
Regards,
Dheeraj