在vs2008下使用cygwin(2):i686-pc-cygwin/newlib/Makefile分析

 

快乐虾

http://blog.youkuaiyun.com/lights_joy/

lights@hb165.com

   

本文适用于

Cygwin-1.16

Vs2008

   

欢迎转载,但请保留作者信息

 

 

这个文件由主控Makefile调用newlib/configure脚本生成。并在主控Makefile中使用make all进行调用。

1.1    all

all目标定义为:

all: newlib.h

       $(MAKE) $(AM_MAKEFLAGS) all-recursive

它所依赖的文件newlib.h已经由configure脚本生成,因此转向执行all-recursive目标:

all-recursive: stmp-targ-include

all-recursive: all-multi

此外还有:

 

RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive /

       html-recursive info-recursive install-data-recursive /

       install-exec-recursive install-info-recursive /

       install-recursive installcheck-recursive installdirs-recursive /

       pdf-recursive ps-recursive uninstall-info-recursive /

       uninstall-recursive

 

# This directory's subdirectories are mostly independent; you can cd

# into them and run `make' without going through this Makefile.

# To change the values of `make' variables: instead of editing Makefiles,

# (1) if the variable is set in `config.status', edit `config.status'

#     (which will cause the Makefiles to be regenerated when you run `make');

# (2) otherwise, pass the desired values on the `make' command line.

$(RECURSIVE_TARGETS):

       @failcom='exit 1'; /

       for f in x $$MAKEFLAGS; do /

         case $$f in /

           *=* | --[!k]*);; /

           *k*) failcom='fail=yes';; /

         esac; /

       done; /

       dot_seen=no; /

       target=`echo $@ | sed s/-recursive//`; /

       list='$(SUBDIRS)'; for subdir in $$list; do /

         echo "Making $$target in $$subdir"; /

         if test "$$subdir" = "."; then /

           dot_seen=yes; /

           local_target="$$target-am"; /

         else /

           local_target="$$target"; /

         fi; /

         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) /

         || eval $$failcom; /

       done; /

       if test "$$dot_seen" = "no"; then /

         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; /

       fi; test -z "$$fail"

上述脚本将进入各个子目录执行make all,然后在当前目录下执行make all-am

 

1.1.1   stmp-targ-include

这条规则定义为:

# The targ-include directory just holds the includes files for the

# particular system and machine we have been configured for.  It is

# used while building.

stmp-targ-include: config.status

       -rm -rf targ-include stmp-targ-include

       mkdir targ-include targ-include/sys targ-include/machine targ-include/bits

       cp newlib.h targ-include/newlib.h

       -for i in $(srcdir)/libc/machine/$(machine_dir)/machine/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/machine/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/machine/$(machine_dir)/sys/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/sys/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/machine/$(machine_dir)/include/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/include/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/sys/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/sys/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/bits/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/bits/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/machine/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/machine/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/machine/$(machine_dir)/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/machine/`basename $$i`; /

           else true; fi ; /

         done

       -for i in $(srcdir)/libc/sys/$(sys_dir)/machine/$(machine_dir)/include/*.h; do /

           if [ -f $$i ]; then /

             cp $$i targ-include/machine/`basename $$i`; /

           else true; fi ; /

         done

       touch $@

这段脚本将创建targ-include子目录,并在其中复制一些头文件:

驱动器 C 中的卷是 XP

 卷的序列号是 2855 -11F 6

 

 C:/cygwin/src/cygwin/winsup/src/i686-pc-cygwin/newlib/targ-include 的目录

 

2008-10-08  11:48    <DIR>          .

2008-10-08  11:48    <DIR>          ..

2008-10-08  11:48    <DIR>          sys

2008-10-08  11:48    <DIR>          machine

2008-10-08  11:48    <DIR>          bits

2008-10-08  11:48             5,744 newlib.h

               1 个文件          5,744 字节

 

 C:/cygwin/src/cygwin/winsup/src/i686-pc-cygwin/newlib/targ-include/sys 的目录

 

2008-10-08  11:48    <DIR>          .

2008-10-08  11:48    <DIR>          ..

               0 个文件              0 字节

 

 C:/cygwin/src/cygwin/winsup/src/i686-pc-cygwin/newlib/targ-include/machine 的目录

 

2008-10-08  11:48    <DIR>          .

2008-10-08  11:48    <DIR>          ..

2008-10-08  11:48             4,084 fastmath.h

               1 个文件          4,084 字节

 

 C:/cygwin/src/cygwin/winsup/src/i686-pc-cygwin/newlib/targ-include/bits 的目录

 

2008-10-08  11:48    <DIR>          .

2008-10-08  11:48    <DIR>          ..

               0 个文件              0 字节

 

     所列文件总数:

               2 个文件          9,828 字节

              11 个目录 15,119,286,272 可用字节

最后生成一个stmp-targ-include的空文件供Makefile做时间比较。

 

1.1.2   all-multi

这条规则定义为:

all-multi:

       $(MULTIDO) $(AM_MAKEFLAGS) DO=all multi-do # $(MAKE)

因为此时传递进来的目标为all-recursive,因此转向执行multi-do

multi-do:

       if [ -z "$(MULTIDIRS)" ]; then /

         true; /

       else /

         rootpre=`${PWD_COMMAND}`/; export rootpre; /

         srcrootpre=`cd $(srcdir); ${PWD_COMMAND}`/; export srcrootpre; /

         lib=`echo "$${rootpre}" | sed -e 's,^.*//([^/][^/]*/)/$$,/1,'`; /

         compiler="$(CC)"; /

         for i in `$${compiler} --print-multi-lib 2>/dev/null`; do /

           dir=`echo $$i | sed -e 's/;.*$$//'`; /

           if [ "$${dir}" = "." ]; then /

             true; /

           else /

             if [ -d ../$${dir}/$${lib} ]; then /

              flags=`echo $$i | sed -e 's/^[^;]*;//' -e 's/@/ -/g'`; /

              if (cd ../$${dir}/$${lib}; $(MAKE) $(FLAGS_TO_PASS) /

                            CFLAGS="$(CFLAGS) $${flags}" /

                            CCASFLAGS="$(CCASFLAGS) $${flags}" /

                            FCFLAGS="$(FCFLAGS) $${flags}" /

                            FFLAGS="$(FFLAGS) $${flags}" /

                            ADAFLAGS="$(ADAFLAGS) $${flags}" /

                            prefix="$(prefix)" /

                            exec_prefix="$(exec_prefix)" /

                            GCJFLAGS="$(GCJFLAGS) $${flags}" /

                            CXXFLAGS="$(CXXFLAGS) $${flags}" /

                            LIBCFLAGS="$(LIBCFLAGS) $${flags}" /

                            LIBCXXFLAGS="$(LIBCXXFLAGS) $${flags}" /

                            LDFLAGS="$(LDFLAGS) $${flags}" /

                            MULTIFLAGS="$${flags}" /

                            DESTDIR="$(DESTDIR)" /

                            INSTALL="$(INSTALL)" /

                            INSTALL_DATA="$(INSTALL_DATA)" /

                            INSTALL_PROGRAM="$(INSTALL_PROGRAM)" /

                            INSTALL_SCRIPT="$(INSTALL_SCRIPT)" /

                            $(DO)); then /

                true; /

              else /

                exit 1; /

              fi; /

             else true; /

             fi; /

           fi; /

         done; /

       fi;/

这段脚本其实啥也不做。

1.2    all-am

这个目标定义为:

all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(DATA) newlib.h

LIBRARIES = $(toollib_LIBRARIES)

toollib_LIBRARIES = libm.a libc.a

LTLIBRARIES = $(toollib_LTLIBRARIES)

toollib_LTLIBRARIES未定义

DATA = $(noinst_DATA) $(toollib_DATA)

noinst_DATA = stmp-targ-include

toollib_DATA = $(CRT0) $(CRT1)

CRT0 =

CRT1 =

 

 

参考资料

vs2008下使用cygwin1):前言( 2008-10-16 )

/usr/lib/gcc/x86_64-pc-cygwin/12/../../../../x86_64-pc-cygwin/bin/ld: output_executable.exe: in function `mainCRTStartup': /usr/src/debug/cygwin-3.6.1-1/winsup/cygwin/crt0.c:16: multiple definition of `mainCRTStartup'; /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../lib/crt0.o:/usr/src/debug/cygwin-3.6.1-1/winsup/cygwin/crt0.c:16: first defined here /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../x86_64-pc-cygwin/bin/ld: output_executable.exe: in function `mainCRTStartup': /usr/src/debug/cygwin-3.6.1-1/winsup/cygwin/crt0.c:16: multiple definition of `WinMainCRTStartup'; /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../lib/crt0.o:/usr/src/debug/cygwin-3.6.1-1/winsup/cygwin/crt0.c:16: first defined here /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../x86_64-pc-cygwin/bin/ld: output_executable.exe:cygming-crtbeg:(.text+0x50): multiple definition of `__gcc_register_frame'; /usr/lib/gcc/x86_64-pc-cygwin/12/crtbegin.o:cygming-crtbeg:(.text+0x0): first defined here /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../x86_64-pc-cygwin/bin/ld: output_executable.exe:cygming-crtbeg:(.text+0x70): multiple definition of `__gcc_deregister_frame'; /usr/lib/gcc/x86_64-pc-cygwin/12/crtbegin.o:cygming-crtbeg:(.text+0x20): first defined here /usr/lib/gcc/x86_64-pc-cygwin/12/../../../../x86_64-pc-cygwin/bin/ld: output_executable.exe:cygming-crtbeg:(.data+0x0): multiple definition of `__dso_handle'; /usr/lib/gcc/x86_64-pc-cygwin/12/crtbegin.o:cygming-crtbeg:(.data+0x0): first defined here collect2: 错误:ld 返回 1
06-09
<think>我们正在解决Cygwin环境下链接器报错的问题,错误涉及多个定义(multipledefinition)和crt0.c文件冲突。具体错误信息包括`mainCRTStartup`,`WinMainCRTStartup`,`__gcc_register_frame`等符号的多重定义,以及`collect2:ldreturned1exitstatus`。根据引用[1]中的经验,当使用`gcca.o-oa.exe`时出现多重定义错误,而添加`-nostartfiles`选项可以解决。这是因为Cygwin环境下的GCC默认会链接系统的启动函数(如`mainCRTStartup`),这些启动函数已经在C运行时库(如`crt0.o`)中定义。如果我们编译的源文件(例如`a.c`)中包含了相同的启动函数定义,就会导致冲突。###🔍问题分析1.**多重定义错误来源**:Cygwin的GCC在链接时自动链接了标准启动文件(如`crt0.o`或`crt1.o`),这些文件包含程序入口点(如`mainCRTStartup`)。如果用户程序中也定义了这些符号(例如,用户试图自己实现启动函数),则会出现多重定义。2.**`-nostartfiles`的作用**:该选项告诉链接器不要使用标准系统启动文件,从而避免与用户定义的启动函数冲突。###⚙️解决方案####1.避免在用户代码中定义系统启动函数除非你有特殊需求(如编写操作系统内核或裸机程序),否则不要定义`mainCRTStartup`、`WinMainCRTStartup`等函数。标准的C程序应该从`main`函数开始。####2.使用正确的编译命令-**编译源文件直接生成可执行文件**(推荐):```bashgcca.c-oa.exe```这种方式不会出现多重定义问题,因为编译器会自动处理启动代码。-**先编译成目标文件,再链接**:如果必须分步编译,在链接步骤使用`-nostartfiles`:```bashgcc-ca.c-oa.o#编译成目标文件gcca.o-oa.exe-nostartfiles```注意:使用`-nostartfiles`意味着你需要自己提供程序入口点(例如,定义`_start`函数)。通常,标准C程序不需要这样做,除非是特殊环境。####3.检查代码中的入口点定义确保你的代码中没有定义以下函数(这些是系统启动函数):-`mainCRTStartup`-`WinMainCRTStartup`-`_start`标准C程序应该只有一个`main`函数。####4.确保没有链接多个启动文件在Cygwin环境中,检查是否错误地链接了多个启动文件(如同时链接了`crt0.o`和`crt1.o`)。这通常发生在复杂的构建脚本中。使用以下命令查看链接的库和对象文件:```bashgcc-va.c-oa.exe```输出中会显示详细的链接过程,注意是否链接了多个启动文件。###💡错误示例分析根据引用[1]中的示例:```bashgcca.oa.o-oa.exe#错误:同一个目标文件链接两次```这会引发多重定义错误,因为同一个目标文件(`a.o`)被链接了两次,导致其中定义的每个符号都被重复定义。确保每个目标文件只链接一次。###⚠️注意事项使用`-nostartfiles`可能会导致程序运行时环境初始化不完整(例如,标准输入输出未初始化)。如果你只是编写普通用户程序,避免使用此选项,而是让链接器自动处理启动文件。###🔧调试技巧1.**查看符号定义**:使用`nm`工具检查目标文件和可执行文件中的符号:```bashnma.o#查看a.o中的符号nm/usr/lib/crt0.o#查看系统启动文件中的符号```如果你在`a.o`中看到`mainCRTStartup`等符号,说明你的代码中定义了这些符号,需要移除。2.**详细链接过程**:添加`-Wl,--verbose`选项查看链接器详细输出:```bashgcca.o-oa.exe-Wl,--verbose```###示例:标准C程序结构```c#include<stdio.h>intmain(){printf("Hello,Cygwin!\n");return0;}```编译命令:```bashgcchello.c-ohello.exe```§§相关问题§§1.在Cygwin环境中如何自定义程序入口点?2.`-nostartfiles`和`-nostdlib`选项有何区别?3.如何为Cygwin编写裸机程序(不依赖标准库)?4.如何解决链接时出现的`__gcc_register_frame`多重定义错误?[^1]</think> 我将针对Cygwin环境下链接器报错问题提供结构化解决方案。此问题涉及多个符号(mainCRTStartup, WinMainCRTStartup等)的多重定义冲突,以及crt0启动文件相关错误。 ### 🔍 问题核心分析Cygwin环境下遇到的链接错误主要原因是**多重符号定义冲突**和**启动文件选择错误**: 1. **mainCRTStartup/WinMainCRTStartup冲突**: - Cygwin的GCC默认链接标准C启动文件(crt0.o或crt1.o),其中已定义程序入口点 - 用户代码中若包含同名的启动函数定义会导致多重定义错误 - 冲突函数包括:`mainCRTStartup`(控制台程序), `WinMainCRTStartup`(GUI程序)[^1] 2. **__gcc_register_frame冲突**: - 与异常处理框架相关,通常由重复链接libgcc导致 - 常见于混用不同编译器生成的目标文件时 3. **crt0.c相关错误**: - Cygwin环境使用特殊启动文件适配Windows环境 - 错误使用-nostartfiles/-nostdlib选项可能导致启动流程不完整 ### ⚙️ 分步解决方案 #### 方案1:使用标准编译流程(推荐) ```bash # 控制台应用程序(使用标准入口点) gcc main.c -o app.exe # GUI应用程序(使用WinMain入口) gcc main.c -o app.exe -mwindows ``` 此方案让链接器自动选择正确的启动文件,避免手动干预。 #### 方案2:正确使用入口点选项 当需要自定义入口点时: ```bash # 1. 首先正常编译目标文件(不加-nostartfiles) gcc -c main.c -o main.o # 2. 显式指定入口点链接(避免多重定义) gcc main.o -o app.exe -e my_entry_point -nostartfiles ``` 要点: - `-e`指定自定义入口点函数名 - `-nostartfiles`需与`-e`配合使用 - 在自定义入口点中需手动初始化CRT[^1] #### 方案3:解决__gcc_register_frame冲突 ```bash # 添加链接器选项解决异常处理冲突 gcc main.o -o app.exe -Wl,--disable-dynamicbase ``` #### 方案4:检查库链接顺序 调整库链接顺序可解决多数符号冲突: ```bash gcc main.o -o app.exe -lgcc -lcygwin -luser32 -lkernel32 ``` 遵循顺序:目标文件 → 语言支持库(libgcc) → 系统库 ### 💻 正确项目结构示例 ```makefile # Makefile示例 (Cygwin环境) TARGET = app.exe SRC = main.c $(TARGET): $(SRC) # 控制台程序使用标准编译 gcc $^ -o $@ # 若必须自定义入口点: # gcc -c $^ -o main.o # gcc main.o -o $@ -e my_entry -nostartfiles clean: rm -f *.o $(TARGET) ``` ### ⚠️ 关键注意事项 1. 避免在用户代码中定义`mainCRTStartup`等标准入口点 2. `-nostartfiles`会禁用所有标准启动代码,需手动完成: ```c // 自定义入口点示例 void _start() { // 1. 初始化堆栈 // 2. 初始化CRT extern void __main(void); __main(); // 初始化全局对象 // 3. 调用主函数 int ret = main(); // 4. 清理并退出 exit(ret); } ``` 3. Cygwin环境中`crt0.o`的特殊作用: - 处理Windows↔Unix环境转换 - 初始化cygwin运行时环境 - 设置异常处理框架 ### 🔧 高级调试技巧 1. 查看链接过程细节: ```bash gcc -v main.c -o app.exe ``` 2. 分析目标文件符号: ```bash nm main.o | grep 'mainCRTStartup\|WinMainCRTStartup' ``` 3. 检查默认链接脚本: ```bash ld --verbose | less ``` 4. 查看系统启动文件: ```bash ls /usr/lib/crt*.o ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值