Skip to content

Commit

Permalink
Merge branch 'esp8266:master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
Lan-Hekary authored Apr 14, 2024
2 parents 474dcd3 + 685f2c9 commit b64c1a1
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 80 deletions.
4 changes: 4 additions & 0 deletions cores/esp8266/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,21 +196,25 @@ class Stream: public Print {
// returns number of transferred bytes
size_t sendAvailable (Stream* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); }
size_t sendAvailable (Stream& to) { return sendAvailable(&to); }
size_t sendAvailable (Stream&& to) { return sendAvailable(&to); }

// transfers data until timeout
// returns number of transferred bytes
size_t sendAll (Stream* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); }
size_t sendAll (Stream& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }
size_t sendAll (Stream&& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); }

// transfers data until a char is encountered (the char is swallowed but not transferred) with timeout
// returns number of transferred bytes
size_t sendUntil (Stream* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); }
size_t sendUntil (Stream& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }
size_t sendUntil (Stream&& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); }

// transfers data until requested size or timeout
// returns number of transferred bytes
size_t sendSize (Stream* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); }
size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }
size_t sendSize (Stream&& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); }

String readStreamString (const ssize_t maxLen = -1 ,const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires);
String readStreamStringUntil (const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires);
Expand Down
16 changes: 8 additions & 8 deletions cores/esp8266/StreamString.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include "WString.h"

///////////////////////////////////////////////////////////////
// S2Stream points to a String and makes it a Stream
// S2Stream ("String to Stream") points to a String and makes it a Stream
// (it is also the helper for StreamString)

class S2Stream: public Stream
Expand Down Expand Up @@ -184,26 +184,26 @@ class S2Stream: public Stream
return peekPointer < 0 ? string->length() : string->length() - peekPointer;
}

// calling setConsume() will consume bytes as the stream is read
// (enabled by default)
// calling setConsume() will make the string consumed as the stream is read.
// (default behaviour)
void setConsume()
{
peekPointer = -1;
}

// Reading this stream will mark the string as read without consuming
// (not enabled by default)
// Calling resetPointer() resets the read state and allows rereading.
void resetPointer(int pointer = 0)
// Calling resetPointer() resets the read cursor and allows rereading.
// (this is the opposite of default mode set by setConsume())
void resetPointer(size_t pointer = 0)
{
peekPointer = pointer;
peekPointer = std::min(pointer, (size_t)string->length());
}

protected:
String* string;
int peekPointer; // -1:String is consumed / >=0:resettable pointer
};

///////////////////////////////////////////////////////////////
// StreamString is a S2Stream holding the String

class StreamString: public String, public S2Stream
Expand Down
2 changes: 1 addition & 1 deletion cores/esp8266/Updater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ bool UpdaterClass::end(bool evenIfRemaining){
_hash->begin();
for (uint32_t offset = 0; offset < binSize; offset += sizeof(buff)) {
auto len = std::min(sizeof(buff), binSize - offset);
ESP.flashRead(_startAddress + offset, reinterpret_cast<uint32_t *>(&buff[0]), len);
ESP.flashRead(_startAddress + offset, buff, len);
_hash->add(buff, len);
}
_hash->end();
Expand Down
6 changes: 3 additions & 3 deletions cores/esp8266/WString.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include <utility>
#include <type_traits>

// an abstract class used as a means to proide a unique pointer type
// an abstract class used as a means to provide a unique pointer type
// but really has no body
class __FlashStringHelper;
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
Expand Down Expand Up @@ -204,7 +204,7 @@ class String {
bool concat(double num);

// if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way)
// will be left unchanged (but this isn't signaled in any way)
template <typename T>
String &operator +=(const T &rhs) {
concat(rhs);
Expand Down Expand Up @@ -343,7 +343,7 @@ class String {
char *wbuffer() { return const_cast<char *>(buffer()); } // Writable version of buffer

// concatenation is done via non-member functions
// make sure we still have access to internal methods, since we optimize based on capacity of both sides and want to manipulate internal buffers directly
// make sure we still have access to internal methods, since we optimize based on the capacity of both sides and want to manipulate internal buffers directly
friend String operator +(const String &lhs, String &&rhs);
friend String operator +(String &&lhs, String &&rhs);
friend String operator +(char lhs, String &&rhs);
Expand Down
20 changes: 17 additions & 3 deletions cores/esp8266/cont.S
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@
cont_suspend:
/* a1: sp */
/* a2: void* cont_ctx */
/* adjust stack and save registers */
/* adjust stack */
addi a1, a1, -24

/* make sure that a1 points after cont_ctx.stack[] */
addi a4, a2, 32
bltu a1, a4, cont_overflow

/* save registers */
s32i a12, a1, 0
s32i a13, a1, 4
s32i a14, a1, 8
Expand All @@ -47,6 +53,11 @@ cont_suspend:
l32i a1, a2, 4
jx a0

cont_overflow:
mov.n a3, a1
movi a4, __stack_overflow
jx a4

cont_continue:
l32i a12, a1, 0
l32i a13, a1, 4
Expand Down Expand Up @@ -113,20 +124,23 @@ cont_run:
bnez a4, cont_resume
/* else */
/* set new stack*/
l32i a1, a2, 16;
l32i a1, a2, 16
/* goto pfn */
movi a2, cont_wrapper
jx a2

cont_resume:
/* a1 <- cont_ctx.sp_suspend */
l32i a1, a2, 12
/* make sure that a1 points after cont_ctx.stack[] */
addi a5, a2, 32
bltu a1, a5, cont_overflow
/* reset yield flag, 0 -> cont_ctx.pc_suspend */
movi a3, 0
s32i a3, a2, 8
/* jump to saved cont_ctx.pc_suspend */
movi a0, cont_ret
jx a4
jx a4

cont_norm:
/* calculate pointer to cont_ctx.struct_start from sp */
Expand Down
11 changes: 9 additions & 2 deletions cores/esp8266/cont.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@
#define CONT_H_

#include <stdbool.h>
#include <stdint.h>

#ifndef CONT_STACKSIZE
#define CONT_STACKSIZE 4096
#endif

#ifndef CONT_STACKGUARD
#define CONT_STACKGUARD 0xfeefeffe
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -62,8 +67,11 @@ void cont_run(cont_t*, void (*pfn)(void));
// execution state (registers and stack)
void cont_suspend(cont_t*);

// Check that cont resume state is valid. Immediately panics on failure.
void cont_check_overflow(cont_t*);

// Check guard bytes around the stack. Immediately panics on failure.
void cont_check(cont_t*);
void cont_check_guard(cont_t*);

// Go through stack and check how many bytes are most probably still unchanged
// and thus weren't used by the user code. i.e. that stack space is free. (high water mark)
Expand All @@ -78,7 +86,6 @@ bool cont_can_suspend(cont_t* cont);
// free, running the routine, then checking the max free
void cont_repaint_stack(cont_t *cont);


#ifdef __cplusplus
}
#endif
Expand Down
27 changes: 17 additions & 10 deletions cores/esp8266/cont_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,45 @@
#include <stddef.h>
#include <string.h>

#include "cont.h"
#include "core_esp8266_features.h"
#include "debug.h"

#include "cont.h"

extern "C"
{

static constexpr unsigned int CONT_STACKGUARD { 0xfeefeffe };
static constexpr uint32_t CONT_STACKSIZE_U32 { sizeof(cont_t::stack) / sizeof(*cont_t::stack) };

void cont_init(cont_t* cont) {
memset(cont, 0, sizeof(cont_t));

cont->stack_guard1 = CONT_STACKGUARD;
cont->stack_guard2 = CONT_STACKGUARD;
cont->stack_end = cont->stack + (sizeof(cont->stack) / 4);
cont->stack_end = &cont->stack[0] + CONT_STACKSIZE_U32;
cont->struct_start = (unsigned*) cont;

// fill stack with magic values to check high water mark
for(int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++)
for(int pos = 0; pos < (int)(CONT_STACKSIZE_U32); pos++)
{
cont->stack[pos] = CONT_STACKGUARD;
}
}

void IRAM_ATTR cont_check(cont_t* cont) {
if ((cont->stack_guard1 == CONT_STACKGUARD)
&& (cont->stack_guard2 == CONT_STACKGUARD))
void IRAM_ATTR cont_check_guard(cont_t* cont) {
if ((cont->stack_guard1 != CONT_STACKGUARD)
|| (cont->stack_guard2 != CONT_STACKGUARD))
{
return;
__stack_chk_fail();
__builtin_unreachable();
}
}

__stack_chk_fail();
__builtin_unreachable();
void IRAM_ATTR cont_check_overflow(cont_t* cont) {
if (cont->sp_suspend && (cont->sp_suspend < &cont->stack[0])) {
__stack_overflow(cont, cont->sp_suspend);
__builtin_unreachable();
}
}

// No need for this to be in IRAM, not expected to be IRQ called
Expand Down
2 changes: 1 addition & 1 deletion cores/esp8266/core_esp8266_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ static void loop_wrapper() {
}
loop();
loop_end();
cont_check(g_pcont);
cont_check_guard(g_pcont);
if (serialEventRun) {
serialEventRun();
}
Expand Down
64 changes: 49 additions & 15 deletions cores/esp8266/core_esp8266_postmortem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static const char* s_unhandled_exception = NULL;

// Common way to notify about where the stack smashing happened
// (but, **only** if caller uses our handler function)
static uint32_t s_stacksmash_addr = 0;
static uint32_t s_stack_chk_addr = 0;

void abort() __attribute__((noreturn));
static void uart_write_char_d(char c);
Expand All @@ -59,6 +59,7 @@ static void print_stack(uint32_t start, uint32_t end);
// using numbers different from "REASON_" in user_interface.h (=0..6)
enum rst_reason_sw
{
REASON_USER_STACK_OVERFLOW = 252,
REASON_USER_STACK_SMASH = 253,
REASON_USER_SWEXCEPTION_RST = 254
};
Expand Down Expand Up @@ -188,7 +189,7 @@ static void postmortem_report(uint32_t sp_dump) {
}
else if (rst_info.reason == REASON_SOFT_WDT_RST) {
ets_printf_P(PSTR("\nSoft WDT reset"));
const char infinite_loop[] = { 0x06, 0xff, 0xff }; // loop: j loop
const uint8_t infinite_loop[] = { 0x06, 0xff, 0xff }; // loop: j loop
if (is_pc_valid(rst_info.epc1) && 0 == memcmp_P(infinite_loop, (PGM_VOID_P)rst_info.epc1, 3u)) {
// The SDK is riddled with these. They are usually preceded by an ets_printf.
ets_printf_P(PSTR(" - deliberate infinite loop detected"));
Expand All @@ -198,17 +199,23 @@ static void postmortem_report(uint32_t sp_dump) {
rst_info.exccause, /* Address executing at time of Soft WDT level-1 interrupt */ rst_info.epc1, 0, 0, 0, 0);
}
else if (rst_info.reason == REASON_USER_STACK_SMASH) {
ets_printf_P(PSTR("\nStack smashing detected.\n"));
ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"),
5 /* Alloca exception, closest thing to stack fault*/, s_stacksmash_addr, 0, 0, 0, 0);
}
ets_printf_P(PSTR("\nStack smashing detected at 0x%08x\n"), s_stack_chk_addr);
}
else if (rst_info.reason == REASON_USER_STACK_OVERFLOW) {
ets_printf_P(PSTR("\nStack overflow detected\n"));
}
else {
ets_printf_P(PSTR("\nGeneric Reset\n"));
}

uint32_t cont_stack_start = (uint32_t) &(g_pcont->stack);
uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end;
uint32_t stack_end;
uint32_t cont_stack_start;
if (rst_info.reason == REASON_USER_STACK_SMASH) {
cont_stack_start = s_stack_chk_addr;
} else {
cont_stack_start = (uint32_t) (&g_pcont->stack[0]);
}

uint32_t cont_stack_end = cont_stack_start + CONT_STACKSIZE;

// amount of stack taken by interrupt or exception handler
// and everything up to __wrap_system_restart_local
Expand Down Expand Up @@ -249,15 +256,21 @@ static void postmortem_report(uint32_t sp_dump) {
sp_dump = stack_thunk_get_cont_sp();
}

if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) {
uint32_t stack_end;

// above and inside of cont, dump from the sp to the bottom of the stack
if ((rst_info.reason == REASON_USER_STACK_OVERFLOW)
|| ((sp_dump > cont_stack_start) && (sp_dump < cont_stack_end)))
{
ets_printf_P(PSTR("\nctx: cont\n"));
stack_end = cont_stack_end;
}
// in system, reposition to a known address
// it's actually 0x3ffffff0, but the stuff below ets_run
// is likely not really relevant to the crash
else {
ets_printf_P(PSTR("\nctx: sys\n"));
stack_end = 0x3fffffb0;
// it's actually 0x3ffffff0, but the stuff below ets_run
// is likely not really relevant to the crash
}

ets_printf_P(PSTR("sp: %08x end: %08x offset: %04x\n"), sp_dump, stack_end, offset);
Expand Down Expand Up @@ -296,11 +309,20 @@ static void print_stack(uint32_t start, uint32_t end) {
for (uint32_t pos = start; pos < end; pos += 0x10) {
uint32_t* values = (uint32_t*)(pos);

// avoid printing irrelevant data
if ((values[0] == CONT_STACKGUARD)
&& (values[0] == values[1])
&& (values[1] == values[2])
&& (values[2] == values[3]))
{
continue;
}

// rough indicator: stack frames usually have SP saved as the second word
bool looksLikeStackFrame = (values[2] == pos + 0x10);
const bool looksLikeStackFrame = (values[2] == pos + 0x10);

ets_printf_P(PSTR("%08x: %08x %08x %08x %08x %c\n"),
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' ');
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame) ? '<' : ' ');
}
}

Expand Down Expand Up @@ -370,7 +392,7 @@ void __panic_func(const char* file, int line, const char* func) {
uintptr_t __stack_chk_guard = 0x08675309 ^ RANDOM_REG32;
void __stack_chk_fail(void) {
s_user_reset_reason = REASON_USER_STACK_SMASH;
s_stacksmash_addr = (uint32_t)__builtin_return_address(0);
s_stack_chk_addr = (uint32_t)__builtin_return_address(0);

if (gdb_present())
__asm__ __volatile__ ("syscall"); // triggers GDB when enabled
Expand All @@ -382,4 +404,16 @@ void __stack_chk_fail(void) {
__builtin_unreachable(); // never reached, needed to satisfy "noreturn" attribute
}

void __stack_overflow(cont_t* cont, uint32_t* sp) {
s_user_reset_reason = REASON_USER_STACK_OVERFLOW;
s_stack_chk_addr = (uint32_t)&cont->stack[0];

if (gdb_present())
__asm__ __volatile__ ("syscall"); // triggers GDB when enabled

postmortem_report((uint32_t)sp);

__builtin_unreachable(); // never reached, needed to satisfy "noreturn" attribute
}

} // extern "C"
Loading

0 comments on commit b64c1a1

Please sign in to comment.