Why does this small C program crash on ESP32? - c

I am working with Esspresif ESP32 WROOM board. I have tried to analyse the error I get with GDB Hardware Debugger, but I only receive the line where the error occurs, no error description.
Here's my small program:
typedef unsigned char u08;
void app_main() {
static char *mac_str = "00:00:00:00:00:00";
static u08 mac_addr[6] = {0x1a, 0x11, 0xaf, 0xa0, 0x47, 0x11};
net_dump_mac(mac_addr);
}
void net_dump_mac(const u08 *in) {
int pos = 0;
for (int i=0; i<6; i++) {
byte_to_hex(in[i], (u08 *)(mac_str+pos));
pos += 3;
}
uart_send(mac_str);
}
void byte_to_hex(u08 in, u08 *out) {
out[0] = nibble_to_hex(in >> 4); // <= crash occurs here !
out[1] = nibble_to_hex(in & 0xf);
}
u08 nibble_to_hex(u08 in) {
if (in < 10)
return '0' + in;
else
return 'A' + in - 10;
}
Some idea what am I doing wrong here?

char *mac_str = "00:00:00:00:00:00"; assigns a literal string to mac_str. A literal is read-only on many architectures. Trying to modify it will result in the memory manager no allowing it, often causing a segfault or other exception to occur.
Instead, do:
char mac_str[] = "00:00:00:00:00:00";
This creates an array that is initialized with the literal on the right, which gets copied to your array. The array will be the size of the literal string, including the null terminator. This array variable is modifyable.

Related

Iterating over string returns empty in c (os development)

I was making an os, or atleast trying to, but I stumbled upon a problem. While trying to iterate over a string to convert to char to print to screen, the returned char seemed to be empty!(I am actually new to os development); Here is the code snippet:
int offset = 0;
void clear_screen() {
unsigned char * video = 0xB8000;
for(int i = 0; i < 2000; i+=2){
video[i] = ' ';
}
}
void printc(char c) {
unsigned char * video = 0xB8000;
video[offset] = c;
video[offset+1] = 0x03;
offset += 2;
}
void print(unsigned char *string) {
char * sus = '\0';
uint32 i = 0;
printc('|');
sus[0] = 'a';
printc(sus[0]); //this prints "a" correctly
string[i] = 'c';
while (string[i] != '\0') {
printc(string[i]); //this while loop is only called once
i++; //it prints " " only once and exits
}
printc('|');
}
int bootup(void)
{
clear_screen();
// printc('h');
// printc('e');
// printc('l'); /* These work */
// printc('l');
// printc('o');
print("hello"); //this doesn't
return 1;
}
Output that it prints:
|a |
Thanks in advance!!
edit
New print function
void print(unsigned char *string) {
uint32 i = 0;
printc('|');
while (string[i] != '\0') {
printc('i'); //not printed
printc(string[i]);
i++;
}
printc('|');
}
still does not work
edit 2
updated the code as per #lundin's advice
int offset = 0;
void clear_screen() {
unsigned char * video = (unsigned char *)0xB8000;
for(int i = 0; i < 2000; i+=2){
video[i] = ' ';
}
}
void printc(char c) {
unsigned char * video = (unsigned char *)0xB8000;
video[offset] = c;
video[offset+1] = 0x03;
offset += 2;
}
void print(const char *string) {
int i = 0;
printc('|');
while (string[i] != '\0') {
printc('i');
printc(string[i]);
i++;
}
printc('|');
}
int bootup(void)
{
clear_screen();
// printc('h');
// printc('e');
// printc('l');
// printc('l');
// printc('o');
print("hello");
return 1;
}
stack:
init_lm:
mov ax, 0x10
mov fs, ax ;other segments are ignored
mov gs, ax
mov rbp, 0x90000 ;set up stack
mov rsp, rbp
;Load kernel from disk
xor ebx, ebx ;upper 2 bytes above bh in ebx is for cylinder = 0x0
mov bl, 0x2 ;read from 2nd sectors
mov bh, 0x0 ;head
mov ch, 1 ;read 1 sector
mov rdi, KERNEL_ADDRESS
call ata_chs_read
jmp KERNEL_ADDRESS
jmp $
Before proceeding I would recommend reading the OSDev wiki's page on text-based UIs.
While this may go beyond the scope of the question somewhat, I would strongly recommend that, rather than working with the character/attribute values as unsigned char manually, you might want to declare a struct type for those pairs:
struct TextCell {
volatile unsigned char ch;
volatile uint8_t attribute;
};
(You could actually be even more refined about it, by using a bitfield for the individual foreground, background, and decoration components of the attributes, but that's probably getting ahead of things.)
From there you can define the text buffer as a constant pointer:
const struct TextCell* text_buffer = (TextCell *)0xB8000;
You could further define
const uint16_t MAXH = 80, MAXV = 25;
uint16_t currv = 0, currh = 0;
struct TextCell* text_cursor = text_buffer;
void advance_cursor() {
text_cursor++;
if (currh < MAXH) {
currh++;
}
else {
currh = 0;
if (currv < MAXV) {
currv++;
}
else {
/* handle scrolling */
}
}
}
void gotoxy(uint16_t x, uint16_t y) {
uint16_t new_pos = x * y;
if (new_pos > (MAXV * MAXH)) {
text_cursor = text_buffer + (MAXV * MAXH);
currh = MAXH;
currv = MAXV;
}
else {
text_cursor += new_pos;
currh = x;
currv = y;
}
Which would lead to the following modifications of your code:
void kprintc(char c, uint8_t attrib) {
text_cursor->ch = c;
text_cursor->attribute = attrib;
advance_cursor();
}
void kprint(const char *string, uint8_t attribs) {
int i;
for (i = 0; string[i] != '\0'; i++) {
kprintc(string[i], attribs);
}
}
void clear_screen() {
for(int i = 0; i < (MAXH * MAXV); i++) {
kprintc(' ', 0);
}
}
int bootup(void) {
clear_screen();
// kprintc('h', 0x03);
// kprintc('e', 0x03);
// kprintc('l', 0x03);
// kprintc('l', 0x03);
// kprintc('o', 0x03);
kprint("hello", 0x03);
return 1;
}
So, why am I suggesting all of this extra stuff? Because it is a lot easier to debug this way, mainly - it divides the concerns up better, and structures the data (or in this case, the video text buffer) more effectively. Also, you'll eventually need to do something like this at some point in the project, so if it helps now, you might as well do it now.
If I am out of line in this, please let me know.
Your program has undefined behavior since it contains multiple lines that aren't valid C. You will have gotten compiler messages about those lines.
unsigned char * video = 0xB8000; etc is not valid C, you need an explicit cast. "Pointer from integer/integer from pointer without a cast" issues
Similarly, char * sus = '\0'; is also not valid C. You are trying to assign a pointer to a single character, which doesn't make sense. String handling beginner FAQ here: Common string handling pitfalls in C programming. It also addresses memory allocation basics.
sus[0] = 'a'; etc here you have wildly undefined behavior since sus isn't pointing at valid memory.
In case you are actually trying to access physical memory addresses, this isn't the correct way to do so. You need volatile qualified pointers. See How to access a hardware register from firmware? (In your case it probably isn't a register but everything from that link still applies - how to use hex constants etc.)
EDIT: void print(unsigned char *string) ... string[i] = 'c'; is also wrong. First of all you are passing a char* which is not necessarily compatible with unsigned char*. Then you shouldn't modify the passed string from inside a function called print, that doesn't make sense. This should have been const char* string to prevent such bugs. As it stands you are passing a string literal to this function and then try to modify it - that is undefined behavior since string literals are read-only.
Assuming gcc or clang, if you wish to block the compiler from generating an executable out of invalid C code, check out What compiler options are recommended for beginners learning C? In your case you also likely need the -ffreestanding option mentioned there.
char * sus = '\0';
Have not checked more... but this assigns a null pointer to sus, and most probably is not what you want to do.

String Allocated with malloc Enaccessible after Function Return

I have run into a strange bug and I cannot for the life of me get it figured out. I have a function that decodes a byte array into a string based on another encoding function. The function that decodes looks roughly like this:
char *decode_string( uint8_t *encoded_string, uint32_t length,
uint8_t encoding_bits ) {
char *sequence_string;
uint32_t idx = 0;
uint32_t posn_in_buffer;
uint32_t posn_in_cell;
uint32_t encoded_nucleotide;
uint32_t bit_mask;
// Useful Constants
const uint8_t CELL_SIZE = 8;
const uint8_t NUCL_PER_CELL = CELL_SIZE / encoding_bits;
sequence_string = malloc( sizeof(char) * (length + 1) );
if ( !sequence_string ) {
ERR_PRINT("could not allocate enough space to decode the string\n");
return NULL;
}
// Iterate over the buffer, converting one nucleotide at a time.
while ( idx < length ) {
posn_in_buffer = idx / NUCL_PER_CELL;
posn_in_cell = idx % NUCL_PER_CELL;
encoded_nucleotide = encoded_string[posn_in_buffer];
encoded_nucleotide >>= (CELL_SIZE - encoding_bits*(posn_in_cell+1));
bit_mask = (1 << encoding_bits) - 1;
encoded_nucleotide &= bit_mask;
sequence_string[idx] = decode_nucleotide( encoded_nucleotide );
// decode_nucleotide returns a char on integer input.
idx++;
}
sequence_string[idx] = '\0';
printf("%s", sequence_string); // prints the correct string
return sequence_string;
}
The bug is that the return pointer, if I try to print it, causes a segmentation fault. But calling printf("%s\n", sequence_string) inside of the function will print everything just fine. If I call the function like this:
const char *seq = "AA";
uint8_t *encoded_seq;
encode_string( &encoded_seq, seq, 2, 2);
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if ( decoded_seq ) {
printf("%s\n",decoded_seq); // this crashes
if ( !strcmp(decoded_seq, seq) ) {
printf("Success!");
}
then it will crash on the print.
A few notes, the other functions seem to all work, I've tested them fairly thoroughly (i.e. decode_nucleotide, encode_string). The string also prints correctly inside the function. It is only after the function returns that it stops working.
My question is, what might cause this memory to become invalid just by returning the pointer from a function? Thanks in advance!
First (and not that important, but) in the statement:
sequence_string = malloc( sizeof(char) * (length + 1) );
sizeof(char) by definition is always == 1. so the statement becomes:
sequence_string = malloc(length + 1);
In this section of your post:
char *decoded_seq = decode_string( encoded_seq, 2, 2);
...since I cannot see your implementation of decode_string, I can only make assumptions about how you are verifying its output before returning it. I do however understand that you are expecting the return value to contain values that would be legal contents for a C string. I can also assume that because you are working with coding and decoding, that the output type is likely unsigned char. If I am correct, then a legal range of characters for an output type of unsigned char is 0-255.
You are not checking the output before sending the value to the printf statement. If the value at the memory address of decoded_seq happens to be 0, (in the range of unsigned char) your program would crash. String functions do not work well with null pointers.
You should verify the return of _decode_string_ sending it to printf
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if(decoded_seq != NULL)
{
...

How to print contents of register in c

I'm trying to debug a driver that I'm writing for a UART that reads a string of chars from a serial console until the user press 'l'. The function is called 'getstring()' below.
I want to examine the contents of a status register to see which bits are set. The status register is offset by 2. I need to print it when 'getstring()' is called. I can use printf().
This is the register map for the UART.
When I call the How could I print out the contents of a register in c?
#define UART 0x00080000
void getchar(char *str) {
volatile uint32_t *uart = (volatile uint32_t*) UART;
char c = 0;
do
{
while ((uart[2] & (1<<7)) == 0);
c = uart[0];
*str++ = c;
}
while (c!='l');
}
`
To convert from binary to an ASCII string of ones and zeroes, simply do this:
uint32_t local = *uart;
for(size_t i=0; i<32; i++)
{
*str = (local & (1u << 31-i) ? '1' : '0';
str++;
}
*str = '\0';

Dereferencing an array value via pointer to the array

I'm probably missing something obvious, but my C is pretty rusty and I'm not having any luck making sense of this. I have a loop where I want to iterate over an array of uint64_t values coming from libdvdnav and then format the values and insert them into a string.
The header for libdvdnav defines the function I'm calling thusly:
uint32_t dvdnav_describe_title_chapters(dvdnav_t *self, int32_t title, uint64_t **times, uint64_t *duration);
Here's how I'm defining the variables used and executing the call (dvdnav and args[0] are defined and initialized elsewhere):
uint64_t *times;
uint64_t duration;
uint32_t times_array_len;
times_array_len = dvdnav_describe_title_chapters(dvdnav, atoi(args[0]), &times, &duration);
The code below seems to work, and compiles & runs w/o error, but of course only the first value in the array is inserted:
int i = 0;
uint64_t a_time = times[0];
while(i < times_array_len){
char time_json[100];
sprintf(time_json, "{\"chapter\":\"%d\",\"time\":\"%u\"},", i, a_time);
strcat(payload, time_json);
i++;
}
If I modify this to select each value in the array it still compiles clean, but throws a segfault at runtime:
int i = 0;
while(i < times_array_len){
char time_json[100];
sprintf(time_json, "{\"chapter\":\"%d\",\"time\":\"%u\"},", i, times[i]);
strcat(payload, time_json);
i++;
}
I thought maybe there was something in one of the array elements that was a problem (a too-large value, unexpected, NULL, etc.) but even if I replace the variable i with a known-reasonable element (say, 0) it still segfaults.
I'm sure there's countless other improvements to be made here (safer allocations, overflow protection, etc.) but the part I'm struggling to decipher is getting those values out of the array an into my formatted string.
How is payload defined? If it is too short to contain the strings then you will get a crash.
You can tackle this in several ways:
1) Since you now the number of json entries will be times_array_len you can allocate the string on heap using malloc with the size 100 * times_array_len - you will never exceed that (again, not sure if it is smart using a fixed length for the json buffer), then strcat should be safe. You can even do direct sprintf calls into the payload buffer dirrectly since you will now how far the offset is by keeping track of the return value of sprintf. Something like this:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv)
{
__int64 pTimes[] = { 1, 2, 3 ,4};
size_t nTimeCount = sizeof(pTimes) / sizeof(pTimes[0]);
size_t nPayloadOffset = 0;
char* pPayload = (char*)malloc(100 * nTimeCount);
if (pPayload)
{
for (size_t nTimeIndex = 0; nTimeIndex < nTimeCount; ++nTimeIndex)
{
nPayloadOffset += sprintf(&pPayload[nPayloadOffset], "{\"chapter\":\"%d\",\"time\":\"%u\"},", nTimeIndex, pTimes[nTimeIndex]);
}
printf("%s\n", pPayload);
free(pPayload);
}
return EXIT_SUCCESS;
}
2) To avoid running over the 100 character length on a single entry you could be wise and allocate the pPlayoad with an initial size, then calculate the size of each entry and reallocate the pPayload if it becomes too short
3) Use C++ and std::stringstream if C++ is an option:
#include <sstream>
#include <iostream>
int main(int argc, char** argv)
{
__int64 pTimes[] = { 1, 2, 3 ,4};
size_t nTimeCount = sizeof(pTimes) / sizeof(pTimes[0]);
size_t nPayloadOffset = 0;
std::stringstream JsonStream;
for (size_t nTimeIndex = 0; nTimeIndex < nTimeCount; ++nTimeIndex)
{
JsonStream << "{\"chapter\":\"" << nTimeIndex << "\",\"time\":\"" << pTimes[nTimeIndex] << "\"},";
}
std::cout << JsonStream.str() << std::endl;
return EXIT_SUCCESS;
}

Setting byte array pointer in structure in C

I have a structure that contains a pointer to a byte array.
To set the pointer I’ve tried the following two ways:
1 Use malloc then memcpy byte array data (commented out in code below).
2 Simply copy pointer.
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
typedef struct _element
{
unsigned char *pValue;
int nLength;
} Element;
Element* ElementCreate(void)
{
Element *pElement = (Element*)malloc(sizeof(*pElement));
pElement->pValue = NULL;
pElement->nLength = 0;
return pElement;
}
void ElementDestroy(Element **ppElement)
{
Element *pElement = NULL;
if (!ppElement)
return;
pElement = *ppElement;
//free(pElement->pValue);
//pElement->pValue = NULL;
free(pElement);
*ppElement = NULL;
}
void ElementSetValue(Element *pElement, unsigned char *pValue, int nLength)
{
//pElement->pValue = (unsigned char*)malloc(nLength * sizeof(*(pElement->pValue)));
//if (!(pElement->pValue))
// return;
//memcpy(pElement->pValue, pValue, nLength);
pElement->pValue = pValue;
pElement->nLength = nLength;
}
void ElementWriteValue(const Element *pElement)
{
int nIndex = 0;
for (; nIndex < pElement->nLength; nIndex++)
printf("%02X ", pElement->pValue[nIndex]);
}
int main(void)
{
//unsigned char myValue[] = { 0x01, 0x02, 0x03 };
//int nLength = sizeof(myValue) / sizeof(myValue[0]);
Element *pElement = ElementCreate();
{
unsigned char myValue[] = { 0x01, 0x02, 0x03 };
int nLength = sizeof(myValue) / sizeof(myValue[0]);
ElementSetValue(pElement, myValue, nLength);
}
// How come this writes out correct value?
ElementWriteValue(pElement);
ElementDestroy(&pElement);
return 0;
}
(Error checks are omitted for brevity)
Which way is correct?
I’d expect 2 to fail because myValue will be destroyed after the “}” so
printf("%02X ", pElement->pValue[nIndex]);
would write out rubbish data but it seems to work OK. Why?
It is undefined behaviour, of which a subset is to "work correctly".
The array myValue is out of scope at the next }. At this point the memory location in which myValue was stored is available to be resused, but it may not be leaving it unchanged and hence the code appears to work.
The correct approach is to malloc(), memcpy() and free().
when we enter into
{
unsigned char myValue[] = { 0x01, 0x02, 0x03 };
int nLength = sizeof(myValue) / sizeof(myValue[0]);
ElementSetValue(pElement, myValue, nLength);
}
that mean a memory will be reserved for myValue. and when we leave it (after }) that mean the memory related to myValue is not reserved any more and it's free but the content is not changed. and that's why you have access to memory and its content is not changed.
If your application is multithread application then there is a big risk that the data related to myValue meory changes by another thread and in this case you have always access to the same memory space but you will find that the memory content changes

Resources