将函数分离到不同文件时的数据损坏
Data Corruption when Segregating Functions into Different Files
这个问题让我很苦恼。我正在用 C 语言为基于 LPC15XX 系列微控制器的定制传感器板进行一些裸机编程。该平台相当于一堆 I2C 传感器和一个连接到其中一个 UART 的蓝牙发射器。这不是 LPCxpresso(或类似)板之一;它是定制设计。
直到今天,所有测试代码都在一个整体文件中,一切都按预期工作。随着代码的增长,我决定将主要功能部分分解为单独的源文件以便于管理。不幸的是,我一这样做,程序就开始出现段错误。
正在移动的代码是原始整体文件的复制和粘贴。我已经在 SWD link 上逐步使用 GDB。看来我用来命中内部 ROM 驱动程序的静态变量之一被 NULLified。稍后取消引用会导致错误。
监视静态变量,我可以看到在 I2C 初始化例程中炸毁它的代码行。有问题的是,init 是使用 NXP 提供的 in-ROM 驱动程序例程完成的。此外,当所有代码都在同一个文件中时,同样的代码可以毫无问题地工作。
问题
什么会破坏这样的文件级静态变量?据我了解,该变量不应该在堆栈上,因此我没有看到导致此问题的缓冲区溢出。
为什么在文件之间移动代码会暴露这个?难道不应该全部编译并 linked 到一个命名空间中吗?
回复评论中的问题
uart_instance
在 usart.c
中定义了一次,并且只在调用 uart_setup
.[= 时直接(按名称)写入一次25=]
同样,i2c_instance
定义在main.c
中(我还没有移动I2C函数)。是静态的,声明与 uart_instance
相同。我更新了下面的代码以包含来自两个文件的所有静态变量。
指针实际上在对 pI2CApi->i2c_set_bitrate(hI2C, BASE_CLOCK, I2C_BAUD);
的调用中被置为 NULL,这是通过在相关内存上设置监视来确定的。
我无法直接追踪到任何 ROM 驱动程序例程。它们由 uC 提供,并通过在固定内存地址处提供的双指针进行访问。我检查了这些指针对象(hUart
和 pUartApi
),它们保留了正确的值;只有 uart_instance
被破坏了。
我为此应用程序创建并转储了映射文件;它不包含对 uart_instance
的引用(或任何静态变量)。奇怪的。我可以post完整内容(有点长)
代码吐槽
提醒一句,这段代码非常典型且未经优化。其中大部分是从数据表中无耻地复制的。
定义静态变量(usart.c)
static UARTD_API_T* pUartApi; // USART API function addr table
static UART_HANDLE_T uart_instance; // Raw storage for USART API
static UART_HANDLE_T* hUart; // Handle to USART API
定义静态变量(main.c)
static I2CD_API_T* pI2CApi; // I2C API function addr table
static I2C_HANDLE_T i2c_instance; // Raw storage for I2C API
static I2C_HANDLE_T* hI2C; // Handle to I2C API
初始化静态句柄(usart.c)
int setupUSART0(int sys_clock, int baud) {
UART_CONFIG_T config;
uint32_t frg_val = 0;
uint32_t size_in_bytes;
// Enable USART0 clock
LPC_SYSCON->SYSAHBCLKCTRL1 |= ((1UL << 17));
// Configure USART clock divider
LPC_SYSCON->UARTCLKDIV = (uint8_t)USART_PERIPH_PRESCALE;
// Configure USART0 pins
LPC_SWM->PINASSIGN0 = 0;
LPC_SWM->PINASSIGN0 |= ((uint8_t)18) << 0; // PIO0_18, tx
LPC_SWM->PINASSIGN0 |= ((uint8_t)13) << 8; // PIO0_13, rx
LPC_SWM->PINASSIGN0 |= ((uint8_t)0xFF) << 16; // Not wired, rts
LPC_SWM->PINASSIGN0 |= ((uint8_t)0xFF) << 24; // Not wired, cts
// Get handle to USART API
pUartApi = getUartDriver();
// Initialize memory for UART API
size_in_bytes = pUartApi->uart_get_mem_size();
if (10 < (size_in_bytes / 4)) return -1;
hUart = pUartApi->uart_setup(LPC_USART0_BASE, (uint8_t*)&uart_instance); // <- uart_instance initialized here
// Initialize USART API
config.sys_clk_in_hz = sys_clock / USART_PERIPH_PRESCALE;
config.baudrate_in_hz = baud;
config.config = 1; // 8N1
config.sync_mod = 0;
config.error_en = 0;
frg_val = (pUartApi->uart_init(hUart, &config) << 8) | 0xFF;
// Configure USART fractional divider
if (!frg_val) return -1;
LPC_SYSCON->FRGCTRL = frg_val;
// Enable USART0 in NVIC
NVIC->ISER0 |= ((1UL << 21));
// Enable UART0 interrupts
LPC_USART0->INTENSET |= ((1UL << 0));
return 0;
}
中断指针的 I2C 初始化代码 (main.c)
ErrorCode_t setupI2C() {
ErrorCode_t err;
// Enable I2C clock
LPC_SYSCON->SYSAHBCLKCTRL1 |= ((1UL << 13));
LPC_I2C0->DIV = 0x0078; // 120 decimal
LPC_I2C0->MSTTIME = 0x00; // SCL high / low = 2 clocks each
//DEBUG
LPC_SWM->PINENABLE1 = 0x00;
// Enable interrupts
NVIC->ISER0 |= ((1UL << 24)); // ISE_I2C0
LPC_I2C0->INTENSET |= ((1UL << 0)); // MSTPENDINGEN
LPC_I2C0->INTENSET |= ((1UL << 8)); // SLVPENDINGEN
// Get handle to I2C API
pI2CApi = getI2CDriver();
// Initialize memory for UART API
hI2C = pI2CApi->i2c_setup(LPC_I2C0_BASE, (uint32_t*)&i2c_instance);
// This NULLS uart_instance somehow
// Set bitrate
err = pI2CApi->i2c_set_bitrate(hI2C, BASE_CLOCK, I2C_BAUD);
// Set master mode
LPC_I2C0->CFG = ((1UL << 0)); // MSTEN
return err;
}
我看不出上面的代码有什么问题。
通常,将代码拆分为多个文件可能会意外引入的错误是静态变量的重复。例如,main.c 和 usart.c 中都可能有一个 static UART_HANDLE_T uart_instance;
(或者甚至奇怪地在两者都包含的某些 .h 中)。一切都会编译并且 link OK,但是来自 main.c 和 usart.c 的函数将使用不相关的变量,并且程序逻辑会随着数据损坏的可能性而改变。
现在,这是一个通用的评论,与您的代码没有直接关系。它有一定的帮助......祝你好运!
这个问题让我很苦恼。我正在用 C 语言为基于 LPC15XX 系列微控制器的定制传感器板进行一些裸机编程。该平台相当于一堆 I2C 传感器和一个连接到其中一个 UART 的蓝牙发射器。这不是 LPCxpresso(或类似)板之一;它是定制设计。
直到今天,所有测试代码都在一个整体文件中,一切都按预期工作。随着代码的增长,我决定将主要功能部分分解为单独的源文件以便于管理。不幸的是,我一这样做,程序就开始出现段错误。
正在移动的代码是原始整体文件的复制和粘贴。我已经在 SWD link 上逐步使用 GDB。看来我用来命中内部 ROM 驱动程序的静态变量之一被 NULLified。稍后取消引用会导致错误。
监视静态变量,我可以看到在 I2C 初始化例程中炸毁它的代码行。有问题的是,init 是使用 NXP 提供的 in-ROM 驱动程序例程完成的。此外,当所有代码都在同一个文件中时,同样的代码可以毫无问题地工作。
问题
什么会破坏这样的文件级静态变量?据我了解,该变量不应该在堆栈上,因此我没有看到导致此问题的缓冲区溢出。
为什么在文件之间移动代码会暴露这个?难道不应该全部编译并 linked 到一个命名空间中吗?
回复评论中的问题
uart_instance
在usart.c
中定义了一次,并且只在调用uart_setup
.[= 时直接(按名称)写入一次25=]同样,
i2c_instance
定义在main.c
中(我还没有移动I2C函数)。是静态的,声明与uart_instance
相同。我更新了下面的代码以包含来自两个文件的所有静态变量。指针实际上在对
pI2CApi->i2c_set_bitrate(hI2C, BASE_CLOCK, I2C_BAUD);
的调用中被置为 NULL,这是通过在相关内存上设置监视来确定的。我无法直接追踪到任何 ROM 驱动程序例程。它们由 uC 提供,并通过在固定内存地址处提供的双指针进行访问。我检查了这些指针对象(
hUart
和pUartApi
),它们保留了正确的值;只有uart_instance
被破坏了。我为此应用程序创建并转储了映射文件;它不包含对
uart_instance
的引用(或任何静态变量)。奇怪的。我可以post完整内容(有点长)
代码吐槽
提醒一句,这段代码非常典型且未经优化。其中大部分是从数据表中无耻地复制的。
定义静态变量(usart.c)
static UARTD_API_T* pUartApi; // USART API function addr table
static UART_HANDLE_T uart_instance; // Raw storage for USART API
static UART_HANDLE_T* hUart; // Handle to USART API
定义静态变量(main.c)
static I2CD_API_T* pI2CApi; // I2C API function addr table
static I2C_HANDLE_T i2c_instance; // Raw storage for I2C API
static I2C_HANDLE_T* hI2C; // Handle to I2C API
初始化静态句柄(usart.c)
int setupUSART0(int sys_clock, int baud) {
UART_CONFIG_T config;
uint32_t frg_val = 0;
uint32_t size_in_bytes;
// Enable USART0 clock
LPC_SYSCON->SYSAHBCLKCTRL1 |= ((1UL << 17));
// Configure USART clock divider
LPC_SYSCON->UARTCLKDIV = (uint8_t)USART_PERIPH_PRESCALE;
// Configure USART0 pins
LPC_SWM->PINASSIGN0 = 0;
LPC_SWM->PINASSIGN0 |= ((uint8_t)18) << 0; // PIO0_18, tx
LPC_SWM->PINASSIGN0 |= ((uint8_t)13) << 8; // PIO0_13, rx
LPC_SWM->PINASSIGN0 |= ((uint8_t)0xFF) << 16; // Not wired, rts
LPC_SWM->PINASSIGN0 |= ((uint8_t)0xFF) << 24; // Not wired, cts
// Get handle to USART API
pUartApi = getUartDriver();
// Initialize memory for UART API
size_in_bytes = pUartApi->uart_get_mem_size();
if (10 < (size_in_bytes / 4)) return -1;
hUart = pUartApi->uart_setup(LPC_USART0_BASE, (uint8_t*)&uart_instance); // <- uart_instance initialized here
// Initialize USART API
config.sys_clk_in_hz = sys_clock / USART_PERIPH_PRESCALE;
config.baudrate_in_hz = baud;
config.config = 1; // 8N1
config.sync_mod = 0;
config.error_en = 0;
frg_val = (pUartApi->uart_init(hUart, &config) << 8) | 0xFF;
// Configure USART fractional divider
if (!frg_val) return -1;
LPC_SYSCON->FRGCTRL = frg_val;
// Enable USART0 in NVIC
NVIC->ISER0 |= ((1UL << 21));
// Enable UART0 interrupts
LPC_USART0->INTENSET |= ((1UL << 0));
return 0;
}
中断指针的 I2C 初始化代码 (main.c)
ErrorCode_t setupI2C() {
ErrorCode_t err;
// Enable I2C clock
LPC_SYSCON->SYSAHBCLKCTRL1 |= ((1UL << 13));
LPC_I2C0->DIV = 0x0078; // 120 decimal
LPC_I2C0->MSTTIME = 0x00; // SCL high / low = 2 clocks each
//DEBUG
LPC_SWM->PINENABLE1 = 0x00;
// Enable interrupts
NVIC->ISER0 |= ((1UL << 24)); // ISE_I2C0
LPC_I2C0->INTENSET |= ((1UL << 0)); // MSTPENDINGEN
LPC_I2C0->INTENSET |= ((1UL << 8)); // SLVPENDINGEN
// Get handle to I2C API
pI2CApi = getI2CDriver();
// Initialize memory for UART API
hI2C = pI2CApi->i2c_setup(LPC_I2C0_BASE, (uint32_t*)&i2c_instance);
// This NULLS uart_instance somehow
// Set bitrate
err = pI2CApi->i2c_set_bitrate(hI2C, BASE_CLOCK, I2C_BAUD);
// Set master mode
LPC_I2C0->CFG = ((1UL << 0)); // MSTEN
return err;
}
我看不出上面的代码有什么问题。
通常,将代码拆分为多个文件可能会意外引入的错误是静态变量的重复。例如,main.c 和 usart.c 中都可能有一个 static UART_HANDLE_T uart_instance;
(或者甚至奇怪地在两者都包含的某些 .h 中)。一切都会编译并且 link OK,但是来自 main.c 和 usart.c 的函数将使用不相关的变量,并且程序逻辑会随着数据损坏的可能性而改变。
现在,这是一个通用的评论,与您的代码没有直接关系。它有一定的帮助......祝你好运!