转: 关于gcc compile..滴.. trap..

本文深入解析GCC(GNU Compiler Collection)的扩展语法,包括语句表达式、预处理器技巧、标号元素、case范围等特性,并介绍了如何利用这些扩展来优化代码。
gcc的扩展语法 ,有空的时候注意一下。

在linux 的世界, 用的都是GCC(GNU gcc) , 在看kernel代码的时候, 可能会看到一些比较怪异的写法, 这些都不是ANSI c的写法,
但是我们现在要看懂linux kernel里面的一些代码, 就必须熟悉GNU gcc的扩展语法。

其实除了gcc的扩展语法以外 , 还有标准c的写法, 不过看起来有点奇怪,
比如:
#define dbg(format, arg...)                             /
do {                                                 /
printk("%s: "format,    /
  MODULE_NAME , ## arg);               /
} while (0)
为什么要 加上 do {} while(0) ,因为这也是执行一次啊, 和不加不是一样的吗?
事实上并非如此。  如果你的宏有好几行组成, 最好加上 do{}while(0) 保护一下
否则 if(a)
  dbg(); //如果dbg()的实现是好几行, 这里就出错了 。 除非
这样 if(a)
     {
  dbg();
}  
但是如果你的dbg() 实现的时候 加上do {} while(0) ;就不用了。


-------------------------------------
另外就注意:
({    })的用法:
具体可以看看我的这个帖子:
http://infomax/bbs/viewthread.php?tid=106&extra=page%3D1
事实上 ,上面的例子:也可以写成这个样子:
#define dbg(format, arg...)                            /
({                                                 /
printk("%s: "format, MODULE_NAME , ## arg);      /        \
})
但是,这样用不太标准, 用do{}while(0)就最好。
({ }) 比较适合返回一个变量,
比如下面的例子:
转自内核 spinlock.h
#define PICK_OP2_RET(op, lock, flags)                                          /
({                                                                 /
        unsigned long __ret;                                             /
                                                                  /
        if (TYPE_EQUAL((lock), raw_spinlock_t))                                /
                __ret = __spin##op((raw_spinlock_t *)(lock), flags);                   /
        else if (TYPE_EQUAL(lock, spinlock_t))                                 /
                __ret = _spin##op((spinlock_t *)(lock), flags);                    /
        else __bad_spinlock_type();                                        /
                                                                  /
        __ret;      //千万不要加上return                                   /
})





下面供参考,



一个简单的Hello,world小程序,让你体验大部分常用的gcc的C语言扩展
http://bbs.chinaunix.net/viewthread.php?tid=953181

还有下面这个 来自 www.linuxforum.net  ,  说的比较经典, 当初大家都是看这个的。

gcc核心扩展linuxforum
===========================
Linux 内核使用的 GNU C 扩展
===========================
GNC CC 是一个功能非常强大的跨平台 C 编译器,它对 C 语言提供了很多扩展,
这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。本文把
支持 GNU 扩展的 C 语言称为 GNU C。
Linux 内核代码使用了大量的 GNU C 扩展,以至于能够编译 Linux 内核的唯一编
译器是 GNU CC,以前甚至出现过编译 Linux 内核要使用特殊的 GNU CC 版本的情
况。本文是对 Linux 内核使用的 GNU C 扩展的一个汇总,希望当你读内核源码遇
到不理解的语法和语义时,能从本文找到一个初步的解答,更详细的信息可以查看
gcc.info。文中的例子取自 Linux 2.4.18。
语句表达式
==========
GNU C 把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出
现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本
只能在复合语句中使用。例如:
++++ include/linux/kernel.h
159: #define min_t(type,x,y) /
160:         ({ type __x = (x); type __y = (y); __x window_clamp, tcp_full_space(sk));
复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。
这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为:
#define min(x,y) ((x) " "%s:%d", filename, line)
使用 ## 的原因是处理 arg 不匹配任何参数的情况,这时 arg 的值为空,GNU
C 预处理器在这种特殊情况下,丢弃 ## 之前的逗号,这样
    pr_debug("success!/n")
扩展为
    printk("" "success!/n")
注意最后没有逗号。
标号元素
========
标准 C 要求数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通
过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在
初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,
例如:
+++++ arch/i386/kernel/irq.c
1079: static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。
要指定结构元素,在元素值前写 'FIELDNAME:',例如:
++++ fs/ext2/file.c
41: struct file_operations ext2_file_operations = {
42:         llseek:         generic_file_llseek,
43:         read:           generic_file_read,
44:         write:          generic_file_write,
45:         ioctl:          ext2_ioctl,
46:         mmap:           generic_file_mmap,
47:         open:           generic_file_open,
48:         release:        ext2_release_file,
49:         fsync:          ext2_sync_file,
50 };
将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,
元素 read 初始化为 genenric_file_read,依次类推。我觉得这是 GNU C 扩展中
最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然
保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。
Case 范围
=========
GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:
++++ arch/i386/kernel/irq.c
1062:                         case '0' ... '9': c -= '0'; break;
1063:                         case 'a' ... 'f': c -= 'a'-10; break;
1064:                         case 'A' ... 'F': c -= 'A'-10; break;
    case '0' ... '9':
相当于
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
声明的特殊属性
==============
GNU C 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代
码检查。要指定一个声明的属性,在声明后写
    __attribute__ (( ATTRIBUTE ))
其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。GNU C 支持十几个属性,这
里介绍最常用的:
* noreturn
属性 noreturn 用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的
代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:
++++ include/linux/kernel.h
47: # define ATTRIB_NORET  __attribute__((noreturn)) ....
61: asmlinkage NORET_TYPE void do_exit(long error_code)
        ATTRIB_NORET;
* format (ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)
属性 format 用于函数,表示该函数使用 printf, scanf 或 strftime 风格的参
数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定 format 属性可以
让编译器根据格式串检查参数类型。例如:
++++ include/linux/kernel.h?
89: asmlinkage int printk(const char * fmt, ...)
90:         __attribute__ ((format (printf, 1, 2)));
表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
* unused
属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免
编译器产生警告信息。
* section ("section-name")
属性 section 用于函数和变量,通常编译器将函数放在 .text 节,变量放在
.data 或 .bss 节,使用 section 属性,可以让编译器将函数或变量放在指定的
节中。例如:
++++ include/linux/init.h
78: #define __init          __attribute__ ((__section__ (".text.init")))
79: #define __exit          __attribute__ ((unused, __section__(".text.exit")))
80: #define __initdata      __attribute__ ((__section__ (".data.init")))
81: #define __exitdata      __attribute__ ((unused, __section__ (".data.exit")))
82: #define __initsetup     __attribute__ ((unused,__section__ (".setup.init")))
83: #define __init_call     __attribute__ ((unused,__section__ (".initcall.init")))
84: #define __exit_call     __attribute__ ((unused,__section__ (".exitcall.exit")))
连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,
例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分
内存。
* aligned (ALIGNMENT)
属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对
齐量,以字节为单位,例如:
++++ include/asm-i386/processor.h
294: struct i387_fxsave_struct {
295:         unsigned short  cwd;
296:         unsigned short  swd;
297:         unsigned short  twd;
298:         unsigned short  fop;
299:         long    fip;
300:         long    fcs;
301:         long    foo;
......
308: } __attribute__ ((aligned (16)));
表示该结构类型的变量以 16 字节对齐。通常编译器会选择合适的对齐量,显示指
定对齐通常是由于体系限制、优化等原因。
* packed
属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用
于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
++++ include/asm-i386/desc.h
51: struct Xgt_desc_struct {
52:         unsigned short size;
53:         unsigned long address __attribute__((packed));
54: };
域 address 将紧接着 size 分配。属性 packed 的用途大多是定义硬件相关的结
构,使元素之间没有因对齐而造成的空洞。
当前函数名
==========
GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码
中的名字,__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个
名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外
信息,Linux 内核只使用了 __FUNCTION__。
++++ fs/ext2/super.c
98: void ext2_update_dynamic_rev(struct super_block *sb)
99: {
100:         struct ext2_super_block *es = EXT2_SB(sb)->s_es;
101:
102:         if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
103:                 return;
104:
105:         ext2_warning(sb, __FUNCTION__,
106:                      "updating to rev %d because of new feature flag, "
107:                      "running e2fsck is recommended",
108:                      EXT2_DYNAMIC_REV);
这里 __FUNCTION__ 将被替换为字符串 "ext2_update_dynamic_rev"。虽然
__FUNCTION__ 看起来类似于标准 C 中的 __FILE__,但实际上 __FUNCTION__
是被编译器替换的,不象 __FILE__ 被预处理器替换。
内建函数
========
GNU C 提供了大量的内建函数,其中很多是标准 C 库函数的内建版本,例如
memcpy,它们与对应的 C 库函数功能相同,本文不讨论这类函数,其他内建函数
的名字通常以 __builtin 开始。
* __builtin_return_address (LEVEL)
内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址,参数
LEVEL 指定在栈上搜索框架的个数,0 表示当前函数的返回地址,1 表示当前函数
的调用者的返回地址,依此类推。例如:
++++ kernel/sched.c
437:                 printk(KERN_ERR "schedule_timeout: wrong timeout "
438:                        "value %lx from %p/n", timeout,
439:                        __builtin_return_address(0));
* __builtin_constant_p(EXP)
内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数
EXP 的值是常数,函数返回 1,否则返回 0。例如:
++++ include/asm-i386/bitops.h
249: #define test_bit(nr,addr) /
250: (__builtin_constant_p(nr) ? /
251:  constant_test_bit((nr),(addr)) : /
252:  variable_test_bit((nr),(addr)))
很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以
根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在
参数是常数时编译出最优化的代码。
* __builtin_expect(EXP, C)
内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表
达式 EXP 的值,C 的值必须是编译时常数。例如:
++++ include/linux/compiler.h
13: #define likely(x)       __builtin_expect((x),1)
14: #define unlikely(x)     __builtin_expect((x),0)
++++ kernel/sched.c
564:         if (unlikely(in_interrupt())) {
565:                 printk("Scheduling in interrupt/n");
566:                 BUG();
567:         }
这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排
语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中
断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证
经常执行的目标码更紧凑。
 
 
-----------------------A example on __attribute__-----------

GCC - packed structures

GCC allows you to specify attributes of variables and structures using the keyword__attribute__, the syntax of which is __attribute__((attribute list)). One such attribute is __packed__ which specifies that

a variable or structure field should have the smallest possible alignment--one byte for a variable, and one bit for a field, unless you specify a larger value with the aligned attribute.
GCC Manual
which means that GCC will not add any of the zero's for padding (for memory alignement) and make variables or fields immediately next to each other. For example, here are some things I tried out -- I created a C source file - test.c
struct test_t {
  int  a;
  char b;
  int  c;
} ;

struct test_t test = { 10, 20, 30};
And compiled it with the -S option (ie to generate the assembly equivalent of the code generated).
$gcc -c test.c -S -o test.s
The file test.s -
	.file	"test.c"
.globl test
	.data
	.p2align 2
	.type	test,@object
	.size	test,12
test:
	.long	10
	.byte	20
	.zero	3
	.long	30
	.ident	"GCC: (GNU) 3.2.2 [FreeBSD] 20030205 (release)"
Notice the emphasized code. You can see that the structure "test" is being declared. First the field "a" (int) as .long 10 followed by "b" (char) as .byte 20. To keep the fields' word alignment, notice that GCC has added 3 zero bytes ( .zero 3) before field "c" (int) which is declared as .long 30. This makes the effective sizeof struct test_t as 12 instead of the expected 9. Then I tried with the __packed__ attribute -
struct test_t {
  int  a;
  char b;
  int  c;
} __attribute__((__packed__));

struct test_t test = { 10, 20, 30};
and the "-S" output I got after compiling was
	.file	"test.c"
.globl test
	.data
	.type	test,@object
	.size	test,9
test:
	.long	10
	.byte	20
	.long	30
	.ident	"GCC: (GNU) 3.2.2 [FreeBSD] 20030205 (release)"
in which the zeros are missing making the sizeof structure test_t = 9. Always remember that memory alignment is *good* even if it compromises space, so think twice before using this attribute. It is generally useful when you want to assign a structure to a block of memory and manipulate it through the fields of a structure.
# Makefile for GeekOS kernel, userspace, and tools # Copyright (c) 2004,2005 David H. Hovemeyer <daveho@cs.umd.edu> # $Revision: 1.45 $ # This is free software. You are permitted to use, # redistribute, and modify it as specified in the file "COPYING". # Required software to build GeekOS: # - GNU Make (http://www.gnu.org/software/make) # - gcc 2.95.2 generating code for target (i386/ELF) and host platforms # - nasm (http://nasm.sourceforge.net) # - Perl5, AWK (any version), egrep # # Cygwin (http://cygwin.com) may be used to build GeekOS. # Make sure that gcc, binutils, nasm, and perl are installed. # NOTES: # - This makefile has been written carefully to work correctly # with the -j (parallel make) option. I regularly use "make -j 2" # to speed the build process on 2 processor systems. PROJECT_ROOT := .. VPATH := $(PROJECT_ROOT)/src # Figure out if we're compiling with cygwin, http://cygwin.com SYSTEM_NAME := $(shell uname -s) ifeq ($(findstring CYGWIN,$(SYSTEM_NAME)),CYGWIN) SYM_PFX := _ EXTRA_C_OPTS := -DNEED_UNDERSCORE -DGNU_WIN32 EXTRA_NASM_OPTS := -DNEED_UNDERSCORE NON_ELF_SYSTEM := yes EXTRA_CC_USER_OPTS := -Dmain=geekos_main endif # ---------------------------------------------------------------------- # Configuration - # Various options specifying how GeekOS should be built, # what source files to build, which user programs to build, # etc. This is generally the only section of the makefile # that will need to be modified. # ---------------------------------------------------------------------- # List of targets to build by default. # These targets encompass everything needed to boot # and run GeekOS. ALL_TARGETS := fd.img # Kernel source files KERNEL_C_SRCS := idt.c int.c trap.c irq.c io.c \ keyboard.c screen.c timer.c \ mem.c crc32.c \ gdt.c tss.c segment.c \ bget.c malloc.c \ synch.c kthread.c \ main.c # Kernel object files built from C source files KERNEL_C_OBJS := $(KERNEL_C_SRCS:%.c=geekos/%.o) # Kernel assembly files KERNEL_ASM_SRCS := lowlevel.asm # Kernel object files build from assembler source files KERNEL_ASM_OBJS := \ $(KERNEL_ASM_SRCS:%.asm=geekos/%.o) # All kernel object files KERNEL_OBJS := $(KERNEL_C_OBJS) \ $(KERNEL_ASM_OBJS) # Common library source files. # This library is linked into both the kernel and user programs. # It provides string functions and generic printf()-style # formatted output. COMMON_C_SRCS := fmtout.c string.c memmove.c # Common library object files. COMMON_C_OBJS := $(COMMON_C_SRCS:%.c=common/%.o) # Base address of kernel KERNEL_BASE_ADDR := 0x00010000 # Kernel entry point function KERNEL_ENTRY = $(SYM_PFX)Main # ---------------------------------------------------------------------- # Tools - # This section defines programs that are used to build GeekOS. # ---------------------------------------------------------------------- # Uncomment if cross compiling #TARGET_CC_PREFIX := i386-elf- # Target C compiler. gcc 2.95.2 or later should work. TARGET_CC := $(TARGET_CC_PREFIX)gcc # Host C compiler. This is used to compile programs to execute on # the host platform, not the target (x86) platform. On x86/ELF # systems, such as Linux and FreeBSD, it can generally be the same # as the target C compiler. HOST_CC := gcc # Target linker. GNU ld is probably to only one that will work. TARGET_LD := $(TARGET_CC_PREFIX)ld # Target archiver TARGET_AR := $(TARGET_CC_PREFIX)ar # Target ranlib TARGET_RANLIB := $(TARGET_CC_PREFIX)ranlib # Target nm TARGET_NM := $(TARGET_CC_PREFIX)nm # Target objcopy TARGET_OBJCOPY := $(TARGET_CC_PREFIX)objcopy # Nasm (http://nasm.sourceforge.net) NASM := nasm # Tool to build PFAT filesystem images. BUILDFAT := tools/builtFat.exe # Perl5 or later PERL := perl # Pad a file so its size is a multiple of some unit (i.e., sector size) PAD := $(PERL) $(PROJECT_ROOT)/scripts/pad # Create a file filled with zeroes. ZEROFILE := $(PERL) $(PROJECT_ROOT)/scripts/zerofile # Calculate size of file in sectors NUMSECS := $(PERL) $(PROJECT_ROOT)/scripts/numsecs # ---------------------------------------------------------------------- # Definitions - # Options passed to the tools. # ---------------------------------------------------------------------- # Flags used for all C source files GENERAL_OPTS := -O -Wall $(EXTRA_C_OPTS) CC_GENERAL_OPTS := $(GENERAL_OPTS) -Werror # Flags used for kernel C source files CC_KERNEL_OPTS := -g -DGEEKOS -I$(PROJECT_ROOT)/include # Flags user for kernel assembly files NASM_KERNEL_OPTS := -I$(PROJECT_ROOT)/src/geekos/ -f elf $(EXTRA_NASM_OPTS) # Flags used for common library and libc source files CC_USER_OPTS := -I$(PROJECT_ROOT)/include -I$(PROJECT_ROOT)/include/libc \ $(EXTRA_CC_USER_OPTS) # Flags passed to objcopy program (strip unnecessary sections from kernel.exe) OBJCOPY_FLAGS := -R .dynamic -R .note -R .comment # ---------------------------------------------------------------------- # Rules - # Describes how to compile the source files. # ---------------------------------------------------------------------- # Compilation of kernel C source files geekos/%.o : geekos/%.c $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o # Compilation of kernel assembly source files geekos/%.o : geekos/%.asm $(NASM) $(NASM_KERNEL_OPTS) $< -o geekos/$*.o geekos/%.o : geekos/%.S $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o # Compilation of common library C source files common/%.o : common/%.c $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_USER_OPTS) $< -o common/$*.o # ---------------------------------------------------------------------- # Targets - # Specifies files to be built # ---------------------------------------------------------------------- # Default target - see definition of ALL_TARGETS in Configuration section all : $(ALL_TARGETS) # Standard floppy image - just boots the kernel fd.img : geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin cat geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin > $@ # Floppy boot sector (first stage boot loader). geekos/fd_boot.bin : geekos/setup.bin geekos/kernel.bin $(PROJECT_ROOT)/src/geekos/fd_boot.asm $(NASM) -f bin \ -I$(PROJECT_ROOT)/src/geekos/ \ -DNUM_SETUP_SECTORS=`$(NUMSECS) geekos/setup.bin` \ -DNUM_KERN_SECTORS=`$(NUMSECS) geekos/kernel.bin` \ $(PROJECT_ROOT)/src/geekos/fd_boot.asm \ -o $@ # Setup program (second stage boot loader). geekos/setup.bin : geekos/kernel.exe $(PROJECT_ROOT)/src/geekos/setup.asm $(NASM) -f bin \ -I$(PROJECT_ROOT)/src/geekos/ \ -DENTRY_POINT=0x`egrep 'Main$$' geekos/kernel.syms |awk '{print $$1}'` \ $(PROJECT_ROOT)/src/geekos/setup.asm \ -o $@ $(PAD) $@ 512 # Loadable (flat) kernel image. geekos/kernel.bin : geekos/kernel.exe $(TARGET_OBJCOPY) $(OBJCOPY_FLAGS) -S -O binary geekos/kernel.exe geekos/kernel.bin $(PAD) $@ 512 # The kernel executable and symbol map. geekos/kernel.exe : $(KERNEL_OBJS) $(COMMON_C_OBJS) $(TARGET_LD) -o geekos/kernel.exe -Ttext $(KERNEL_BASE_ADDR) -e $(KERNEL_ENTRY) \ $(KERNEL_OBJS) $(COMMON_C_OBJS) $(TARGET_NM) geekos/kernel.exe > geekos/kernel.syms # Clean build directories of generated files clean : for d in geekos common libc user tools; do \ (cd $$d && rm -f *); \ done # Build header file dependencies, so source files are recompiled when # header files they depend on are modified. depend : $(GENERATED_LIBC_SRCS) $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) \ $(KERNEL_C_SRCS:%.c=$(PROJECT_ROOT)/src/geekos/%.c) \ | $(PERL) -n -e 's,^(\S),geekos/$$1,;print' \ > depend.mak $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_USER_OPTS) \ $(COMMON_C_SRCS:%.c=$(PROJECT_ROOT)/src/common/%.c) \ | $(PERL) -n -e 's,^(\S),common/$$1,;print' \ >> depend.mak # By default, there are no header file dependencies. depend.mak : touch $@ include depend.mak 在哪里修改
05-30
.c riscv64-unknown-elf-gcc -DCONFIG_SYSCALL -nostdlib -fno-builtin -g -Wall -march=rv32g -mabi=ilp32 -c -o out/syscall.o syscall.c riscv64-unknown-elf-gcc -DCONFIG_SYSCALL -nostdlib -fno-builtin -g -Wall -march=rv32g -mabi=ilp32 -c -o out/elf.o elf.c elf.c: In function 'load_user_program': elf.c:26:9: warning: implicit declaration of function 'memcpy' [-Wimplicit-function-declaration] 26 | memcpy((void *)p_vaddr, elf_binary + p_offset, p_filesz); | ^~~~~~ elf.c:3:1: note: 'memcpy' is defined in header '<string.h>'; did you forget to '#include <string.h>'? 2 | #include "elf.h" // 自己准备 Elf32_Ehdr / Phdr 的结构体头文件 +++ |+#include <string.h> 3 | elf.c:30:13: warning: implicit declaration of function 'memset' [-Wimplicit-function-declaration] 30 | memset((void *)(p_vaddr + p_filesz), 0, p_memsz - p_filesz); | ^~~~~~ elf.c:30:13: note: 'memset' is defined in header '<string.h>'; did you forget to '#include <string.h>'? elf.c:37:5: warning: implicit declaration of function 'enter_user_mode' [-Wimplicit-function-declaration] 37 | enter_user_mode(ehdr->e_entry); // 你需要自己实现 | ^~~~~~~~~~~~~~~ riscv64-unknown-elf-gcc -DCONFIG_SYSCALL -nostdlib -fno-builtin -g -Wall -march=rv32g -mabi=ilp32 -c -o out/string.o string.c riscv64-unknown-elf-gcc -E -P -x c -DCONFIG_SYSCALL -nostdlib -fno-builtin -g -Wall -march=rv32g -mabi=ilp32 os.ld > out/os.ld.generated riscv64-unknown-elf-gcc -nostdlib -fno-builtin -g -Wall -march=rv32g -mabi=ilp32 -T out/os.ld.generated -o out/os.elf out/start.o out/mem.o out/entry.o out/usys.o out/kernel.o out/uart.o out/printf.o out/page.o out/sched.o out/user.o out/trap.o out/plic.o out/timer.o out/lock.o out/syscall.o out/elf.o out/string.o /usr/lib/riscv64-unknown-elf/bin/ld: out/user.o: in function `user_task1': /home/yky483768994/new/riscv-operating-system-mooc/code/os/os/user.c:43: undefined reference to `_binary_user_elf_start' /usr/lib/riscv64-unknown-elf/bin/ld: /home/yky483768994/new/riscv-operating-system-mooc/code/os/os/user.c:43: undefined reference to `_binary_user_elf_start' collect2: error: ld returned 1 exit status make: *** [../common.mk:56: out/os.elf] Error 1怎么解决
最新发布
06-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值