我有与 How can I implement a dynamic dispatch table in C 中的 Dave Durbin 相同的先决条件...除了我的目标是 AVR。这是我的限制:
通常,该表应包含以下类型的项目:
typedef struct jump_item {
uint16_t function_id;
void (*callback)(void);
} jump_item_t;
我已尝试按照答案中的建议使用自定义部分,但链接器会为未知符号
__start_myownsection
引发错误(尽管我使用的任何部分名称)。当然,因为代码针对的是 Linux/GCC。但我认为我很接近因为avr-gcc
居然可以用sections ,只是我还没有弄清楚如何在用户定义的部分中堆叠符号并实际指向表的开头,以及在运行时确定表的长度。怎么可能Art's answer适应AVR?
* 编辑 *
我可以看到至少有两种方法可以使用部分来实现我想要的效果,要么将函数“附加”到用户定义的部分,要么将结构表(如上定义)全部堆叠在用户定义的部分中。我目前的问题是:
-gc-sections
,未使用的函数在链接时被优化掉,我需要清理未使用的功能。 我更喜欢第二种选择,类似于以下内容:
模块1.c:
const jump_item_t module1_table[] __attribute__((__progmem__, section("tbl_dispatch"))) =
{
{ 0x02, func11 },
{ 0x03, func12 },
...
};
模块2.c:
const jump_item_t module2_table[] __attribute__((__progmem__, section("tbl_dispatch"))) =
{
{ 0x12, func21 },
{ 0x13, func22 },
...
};
注意:指数不被认为是相关的。
当所有模块都定义了这些变量时,它们会被优化掉,因为在任何地方都没有对这些变量的引用。他们需要在
tbl_dispatch
部分堆叠尽管。所以我的问题回到:如何告诉编译器删除它“认为”未使用但仅适用于特定 C/C++ 模块的变量?
到目前为止我使用的全局命令行如下:
avr-gcc -g -Wall -mcall-prologues -fshort-enums -Os \
-DF_CPU=8000000 -Wl,-relax -mmcu=... \
*.cpp *.c -o main
* 编辑 *
令我失望的是,程序 和自定义部分不在一起。我试图将它们组合起来,但我在程序内存中得到了分散的跳转表......当我完全包含这些时。事实上,甚至并非所有表格都出现在程序存储器中。
放弃。
欢迎任何想法。
最佳答案
如果您编写自己的自定义链接器脚本,并复制为构造函数和析构函数(ctors 和 dtors)所做的工作,那么您绝对可以创建一个模块系统。下面的链接器脚本基于 AVR GCC 的 avr5.x,但我向其中添加了调度内容。
如果您在下面的 shell session 中查看构建脚本的输出,您可以看到调度表设置正确,并且有指向它开始和结束的符号。 shell session 包括我用来编译这个示例的所有源代码和构建脚本。
$ ls
avr5-x-modules.ld build.sh kernel.c kernel.h module_foo.c
$ cat avr5-x-modules.ld
/* Default linker script, for normal executables */
/* Copyright (C) 2014 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:5)
MEMORY
{
text (rx) : ORIGIN = 0, LENGTH = 128K
data (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
fuse (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
lock (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K
user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = 1K
}
SECTIONS
{
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text :
{
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t*)
}
.rela.text :
{
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t*)
}
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata :
{
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r*)
}
.rela.rodata :
{
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r*)
}
.rel.data :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d*)
}
.rela.data :
{
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d*)
}
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss) }
.rela.bss : { *(.rela.bss) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
/* Internal text space or external memory. */
.text :
{
*(.vectors)
KEEP(*(.vectors))
/* For data that needs to reside in the lower 64k of progmem. */
*(.progmem.gcc*)
/* PR 13812: Placing the trampolines here gives a better chance
that they will be in range of the code that uses them. */
. = ALIGN(2);
__trampolines_start = . ;
/* The jump trampolines for the 16-bit limited relocs will reside here. */
*(.trampolines)
*(.trampolines*)
__trampolines_end = . ;
*(.progmem*)
. = ALIGN(2);
/* For future tablejump instruction arrays for 3 byte pc devices.
We don't relax jump/call instructions within these sections. */
*(.jumptables)
*(.jumptables*)
/* For code that needs to reside in the lower 128k progmem. */
*(.lowtext)
*(.lowtext*)
__ctors_start = . ;
*(.ctors)
__ctors_end = . ;
__dtors_start = . ;
*(.dtors)
__dtors_end = . ;
KEEP(SORT(*)(.ctors))
KEEP(SORT(*)(.dtors))
__dispatch_start = . ;
*(.dispatch)
__dispatch_end = . ;
KEEP(SORT(*)(.dispatch))
/* From this point on, we don't bother about wether the insns are
below or above the 16 bits boundary. */
*(.init0) /* Start here after reset. */
KEEP (*(.init0))
*(.init1)
KEEP (*(.init1))
*(.init2) /* Clear __zero_reg__, set up stack pointer. */
KEEP (*(.init2))
*(.init3)
KEEP (*(.init3))
*(.init4) /* Initialize data and BSS. */
KEEP (*(.init4))
*(.init5)
KEEP (*(.init5))
*(.init6) /* C++ constructors. */
KEEP (*(.init6))
*(.init7)
KEEP (*(.init7))
*(.init8)
KEEP (*(.init8))
*(.init9) /* Call main(). */
KEEP (*(.init9))
*(.text)
. = ALIGN(2);
*(.text.*)
. = ALIGN(2);
*(.fini9) /* _exit() starts here. */
KEEP (*(.fini9))
*(.fini8)
KEEP (*(.fini8))
*(.fini7)
KEEP (*(.fini7))
*(.fini6) /* C++ destructors. */
KEEP (*(.fini6))
*(.fini5)
KEEP (*(.fini5))
*(.fini4)
KEEP (*(.fini4))
*(.fini3)
KEEP (*(.fini3))
*(.fini2)
KEEP (*(.fini2))
*(.fini1)
KEEP (*(.fini1))
*(.fini0) /* Infinite loop after program termination. */
KEEP (*(.fini0))
_etext = . ;
} > text
.data :
{
PROVIDE (__data_start = .) ;
*(.data)
*(.data*)
*(.rodata) /* We need to include .rodata here if gcc is used */
*(.rodata*) /* with -fdata-sections. */
*(.gnu.linkonce.d*)
. = ALIGN(2);
_edata = . ;
PROVIDE (__data_end = .) ;
} > data AT> text
.bss ADDR(.data) + SIZEOF (.data) : AT (ADDR (.bss))
{
PROVIDE (__bss_start = .) ;
*(.bss)
*(.bss*)
*(COMMON)
PROVIDE (__bss_end = .) ;
} > data
__data_load_start = LOADADDR(.data);
__data_load_end = __data_load_start + SIZEOF(.data);
/* Global data not cleared after reset. */
.noinit ADDR(.bss) + SIZEOF (.bss) : AT (ADDR (.noinit))
{
PROVIDE (__noinit_start = .) ;
*(.noinit*)
PROVIDE (__noinit_end = .) ;
_end = . ;
PROVIDE (__heap_start = .) ;
} > data
.eeprom :
{
/* See .data above... */
KEEP(*(.eeprom*))
__eeprom_end = . ;
} > eeprom
.fuse :
{
KEEP(*(.fuse))
KEEP(*(.lfuse))
KEEP(*(.hfuse))
KEEP(*(.efuse))
} > fuse
.lock :
{
KEEP(*(.lock*))
} > lock
.signature :
{
KEEP(*(.signature*))
} > signature
.user_signatures :
{
KEEP(*(.user_signatures*))
} > user_signatures
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
}
$ cat build.sh
CFLAGS="-std=gnu11 -mmcu=atmega328p"
set -uex
avr-gcc $CFLAGS -c module_foo.c -o module_foo.o
avr-gcc $CFLAGS -c kernel.c -o kernel.o
avr-gcc -T avr5-x-modules.ld kernel.o module_foo.o \
-o program.elf -Wl,-Map=program.map
grep dispatch program.map
$ cat kernel.c
#include "kernel.h"
#include <avr/pgmspace.h>
extern dispatch_item * __dispatch_start;
extern dispatch_item * __dispatch_end;
int main()
{
while (1)
{
for (dispatch_item * item = __dispatch_start; item < __dispatch_end; item++)
{
// TODO: Insert code here for reading the contents of the
// dispatch item from program space and using it. You
// probably have to use pgm_read_word avr avr/pgmspace.h,
// but with GCC 5 you could probably use the new named
// memory space feature to just access the dispatch item
// the same way you would access any other struct:
// https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html
}
}
}
$ cat kernel.h
#pragma once
#include <stdint.h>
typedef struct dispatch_item {
uint16_t func_id;
void (*func)(void);
} dispatch_item;
#define DISPATCH_ITEM dispatch_item const __attribute__((section (".dispatch")))
$ cat module_foo.c
#include "kernel.h"
#include <avr/io.h>
// This gets called before main.
void __attribute__((constructor)) foo_init()
{
PINB = 0;
}
// There is a pointer to this in the dispatch table.
void foo()
{
PINB = 1;
}
// DISPATHCH_TABLE_ENTRY(0x12, &foo);
DISPATCH_ITEM foo_dispatch = { 0x12, &foo };
DISPATCH_ITEM foo_dispatch2 = { 0x13, &foo };
$ ./build.sh
++ avr-gcc -std=gnu11 -mmcu=atmega328p -c module_foo.c -o module_foo.o
++ avr-gcc -std=gnu11 -mmcu=atmega328p -c kernel.c -o kernel.o
++ avr-gcc -T avr5-x-modules.ld kernel.o module_foo.o -o program.elf -Wl,-Map=program.map
++ grep dispatch program.map
0x00000002 __dispatch_start = .
*(.dispatch)
.dispatch 0x00000002 0x8 module_foo.o
0x00000002 foo_dispatch
0x00000006 foo_dispatch2
0x0000000a __dispatch_end = .
SORT(*)(.dispatch)
关于compile-time - 如何为 AVR 实现编译时 [dispatch] 表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39733931/