我从同事那里了解到,无需编写函数即可编写和执行C程序main()
.它可以在下面完成
withoutMain.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
将其编译为:
my_main.c
运行方式为:
main()
我的问题是,何时需要做这种事情?一些现实世界的场景?
我正在尝试编写一些带有memset
-style 循环的裸机代码:
for (int i = 0; i < N; ++i) {
arr[i] = 0;
}
Run Code Online (Sandbox Code Playgroud)
它是用 GCC 编译的,GCC 足够聪明,可以将其转换为对memset()
. 不幸的是,因为它是裸机,我没有memset()
(通常在 libc 中)所以我收到链接错误。
undefined reference to `memset'
Run Code Online (Sandbox Code Playgroud)
似乎进行这种转换的优化是-ftree-loop-distribute-patterns
:
执行可以通过调用库生成代码的模式的循环分布。默认情况下,此标志在 -O2 及更高级别以及由
-fprofile-use
和启用-fauto-profile
。
所以一个人的解决方案是降低优化级别。不是很满意。
我还发现这真的有用的网页,说明这-ffreestanding
是不足以让GCC没有做到这一点,而且也根本没有选择,只能提供自己的实现memcpy
,memmove
,memset
和memcmp
。我很乐意这样做,但是怎么做?
如果我只是编写memset
编译器将检测其中的循环并将其转换为对 memset 的调用!事实上,在我使用的 CPU 供应商提供的代码中,我发现了这条评论:
/*
// This is commented out because the assembly code that the compiler generates …
Run Code Online (Sandbox Code Playgroud) 我有兴趣使用Linux头文件提供的unistd.h构建一个没有(g)libc的静态ELF程序.
我已经阅读了这些文章/问题,这些文章/问题粗略地概括了我正在尝试做什么,但并不完全:http: //www.muppetlabs.com/~breadbox/software/tiny/teensy.html
https://blogs.oracle.com/ksplice/entry/hello_from_a_libc_free
我有基本代码,它只依赖于unistd.h,其中我的理解是每个函数都是由内核提供的,而且不需要libc.这是我采取的最有希望的道路:
$ gcc -I /usr/include/asm/ -nostdlib grabbytes.c -o grabbytesstatic
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400144
/tmp/ccn1mSkn.o: In function `main':
grabbytes.c:(.text+0x38): undefined reference to `open'
grabbytes.c:(.text+0x64): undefined reference to `lseek'
grabbytes.c:(.text+0x8f): undefined reference to `lseek'
grabbytes.c:(.text+0xaa): undefined reference to `read'
grabbytes.c:(.text+0xc5): undefined reference to `write'
grabbytes.c:(.text+0xe0): undefined reference to `read'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
在此之前,我必须根据内核头文件中的值手动定义SEEK_END和SEEK_SET.否则就会错误地说那些没有定义,这是有道理的.
我想我需要链接到未经剥离的vmlinux来提供符号来使用.然而,我读完了符号,虽然有很多llseeks,但他们并不是llseek逐字逐句.
所以我的问题可以在几个方面:
如何指定ELF文件以使用符号?而且我猜测是否/如何可能,符号将不匹配.如果这是正确的,是否有一个现有的头文件将重新定义llseek和default_llseek或内核中的任何内容?
有没有更好的方法在没有libc的情况下在C语言中编写Posix代码?
我的目标是使用(可能只是)unistd.h编写或移植相当标准的C代码,并在没有libc的情况下调用它.我可能没有一些unistd函数,并且不确定哪些"纯粹"存在于内核调用或不存在.我喜欢集会,但这不是我的目标.希望保持尽可能严格的C(如果必须的话,我可以使用一些外部汇编文件),以便在某些时候允许无libc的静态系统.
谢谢你的阅读!
系统调用函数由操作系统直接提供。一些库(例如GNU C库)提供包装操作系统的系统调用的函数,但操作系统不直接提供。
在Linux 中,我是否可以在C 代码中调用系统调用函数,而无需操作系统未直接提供的库(例如GNU C 库)?操作系统是否为其系统调用提供库?
例如,如果我想read()
在我的C程序中直接调用系统调用函数,我应该包含哪个头文件?我必须使用一些图书馆吗?
我是主要使用ARM Cortex-M设备的嵌入式开发人员.最近我转向Linux并决定学习更多关于构建/汇编/链接过程,如何编写makefile等,因为我正在使用IDE(IAR,Keil,Eclipse等),其中许多东西是自动化的和用户实际上并不知道后台发生了什么.目标是更多地了解这个低级别流程,如何正确使用这些工具,而不仅仅依赖于IDE默认设置.
编写完makefile后,我能够构建我的应用程序.但是,我决定通过直接调用链接器(而不是通过编译器)手动执行链接过程,并且出现了令人惊讶的麻烦!未定义的libc引用,__ libc_init_array中的_init函数,_exit等是问题所在.经过一整天的调查,我能够手动包含所有目标文件(crt0.o,crti.o)和库(libnosys.a).显然,编译器会自动执行此操作.
在克服了这些麻烦之后,我想通了,我对这些内部结构一点都不了解.为什么我需要一些ctr0.o,crti.o等.那些来自等等.它是否链接到编译器/链接器或C运行时库或系统?
我想更多地了解这些内部结构,但是,我不确定,我实际上在寻找什么.它是一个库,系统,编译器吗?
我明白,系统(MCU)需要初始化RAM中的变量和其他东西,但是我错过了完整的图片.你能指导我读一本好书/手册,关于这些东西的读物吗?我到底在想什么?
编辑:
在与您讨论后,我很可能想出了我需要的东西,所以我会按照以下方式重新解释我的问题:
1)我收到了一个MCU(假设是STM32F4xx),我应该创建一个闪烁的LED示例.所有这些都应该从头开始,自己的启动代码,没有使用外部库等.
2)在第二步中,有人告诉我所有这些已经由其他人完成(GCC工具链/标准库,MCU供应商启动文件等).所以我只需要理解/链接我的工作与完成的工作并比较差异,为什么他们这样做等等.
所有这些都是由@mbjoe回答+我发现了一些有趣的阅读:https://www.amazon.com/Embedded-Shoestring-experience-designing-software/dp/0750676094
感谢大家的帮助,并以正确的方式指导我!
我想知道程序是如何工作的,以便尽可能地让它成为一块骨头.
我刚刚发现了如何使用wprintf函数为x86_64汇编代码(发现宽字符是32位).我所要做的只是链接到libc(-lc).
我正在尝试为32位代码组装代码做同样的事情,但我偶然发现了一点.最终我使用gcc进行链接(并将_start:更改为main :).所以我用ld自己做了连接,包括crt1.o crti.o和crtn.o. 然后我的程序工作(之前不打印任何东西)所以我的问题是,我可以在我的代码中做些什么来消除对这些其他3个目标文件的需要(当然还原为_start:而不是main :) ?
test_lib.S
.section .data
locale:
.string ""
.align 4
printformat:
.long '%','l','c',0
.section .text
.global main
main:
pushl $locale
pushl $6
call setlocale
pushl $12414
pushl $printformat
call wprintf
pushl $2
call exit
Run Code Online (Sandbox Code Playgroud)
并运行以下
as --32 test_lib.S -o test_lib.o
ld -m elf_i386 -L/lib/ -L/usr/lib/ -I/lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc /usr/lib/crtn.o test_lib.o -o test_lib
./test_lib
Run Code Online (Sandbox Code Playgroud)
哦,输出只是日本平假名(ma)ま(注意没有换行符,所以它在提示之前打印)
好吧,我已经在程序集中编写了一个引导加载程序,并尝试从中加载C内核。
这是引导程序:
bits 16
xor ax,ax
jmp 0x0000:boot
extern kernel_main
global boot
boot:
mov ah, 0x02 ; load second stage to memory
mov al, 1 ; numbers of sectors to read into memory
mov dl, 0x80 ; sector read from fixed/usb disk ;0 for floppy; 0x80 for hd
mov ch, 0 ; cylinder number
mov dh, 0 ; head number
mov cl, 2 ; sector number
mov bx, 0x8000 ; load into es:bx segment :offset of buffer
int 0x13 ; …
Run Code Online (Sandbox Code Playgroud) 如何使用没有Glibc的C中的内联汇编来获取参数值?
我需要这个代码用于Linux
archecture x86_64
和i386
.如果你知道MAC OS X
或者Windows
,也提交并请指导.
void exit(int code)
{
//This function not important!
//...
}
void _start()
{
//How Get arguments value using inline assembly
//in C without Glibc?
//argc
//argv
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
https://gist.github.com/apsun/deccca33244471c1849d29cc6bb5c78e
和
#define ReadRdi(To) asm("movq %%rdi,%0" : "=r"(To));
#define ReadRsi(To) asm("movq %%rsi,%0" : "=r"(To));
long argcL;
long argvL;
ReadRdi(argcL);
ReadRsi(argvL);
int argc = (int) argcL;
//char **argv = (char **) argvL;
exit(argc);
Run Code Online (Sandbox Code Playgroud)
但它仍然返回0.所以这段代码错了!请帮忙.