云实验室案例-GCC编译
本文档涉及案例包括:
• 在云实验室开发板上直接编译包含DRM库的测试程序
为编译在开发板上运行的程序,根据编译机器的不同可简单分为两种:
1)使用ARM交叉编译工具链,在X86 PC机平台交叉编译
2)在开发板本机直接编译
其中,第一种方式常采用meson或yocto的方式进行编译,但编译相对比较复杂;但第二种方式编译快速便捷,尤其当客户的程序相对简单的时候。但当程序中涉及到了一些库的使用,比如DRM,G2D等,又往往会找不到相应的参考资料快速实现编译。
本文的目的就是带领用户,快速在云实验室的开发板上,完成含有DRM库的测试程序基于gcc的编译任务。
相关知识:
GCC基本介绍
GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器。
GCC 编译器对程序的编译下图所示,分为 4 个阶段:预处理(预编译 )、编译和优化、汇编和链接。GCC 的编译器可以将这 4 个步骤合并成一个。
预处理(-E):在这个阶段主要做了三件事: 展开头文件 、宏替换 、去掉注释行
这个阶段需要 GCC 调用预处理器来完成,最终得到的还是源文件,文本格式
编译(-S):这个阶段需要 GCC 调用编译器对文件进行编译,最终得到一个汇编文件
汇编-c:这个阶段需要 GCC 调用汇编器对文件进行汇编,最终得到一个二进制文件
链接:这个阶段需要 GCC 调用链接器对程序需要调用的库进行链接,最终得到一个可执行的二进制文件
GCC基本用法
GCC最基本的命令是∶
gcc [options] [filenames]
其中options就是编译器所需要的参数,filenames给出相关的文件名称。常见选项包括:
gcc 编译选项 [options] | 选项的意义 |
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定的源文件,但是不进行链接 |
-o [file1] [file2] / [file2] -o [file1] | 将文件 file2 编译成文件 file1 |
-I directory (大写的 i) | 指定 include 包含文件的搜索目录 |
-l | 在程序编译的时候,指定使用的库 |
-On | n 的取值范围:0~3。编译器的优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,-O3 优化级别最高 |
对于gcc生成的文件,其不同后缀所代表的意义为:
说明 | gcc参数 | |
.c | 源文件 | 无 |
.i | 预处理后的c文件 | -E |
.s | 编译后的得到的汇编语言的源文件 | -S |
.o | 汇编后得到的二进制文件 | -c |
执行步骤:
1 预定板子
2 案例执行
2.1 DRM测试程序的编译
首先用户先在测试开发板上创建一个测试程序
vi drm_test.c
并将如下显示测试代码复制到文件中并保存:
// SPDX-License-Identifier: GPL-2.0+
/* *
* Copyright 2024 NXP
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <drm/drm.h>
#include <drm/drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#define WIDTH 1280
#define HEIGHT 800
int main()
{
int ret;
int drm_fd;
drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC | O_NONBLOCK);
ret = drmSetMaster(drm_fd);
drmModeRes *res;
drmModeConnector *conn;
res = drmModeGetResources(drm_fd);
conn = drmModeGetConnector(drm_fd, res->connectors[0]);
drmModeEncoder *encoder;
encoder = drmModeGetEncoder(drm_fd, conn->encoders[0]);
int g_crtc_id;
for (int j = 0; j < res->count_crtcs; j++) {
if (encoder->possible_crtcs & (1 << j)) {
g_crtc_id = res->crtcs[j];
}
}
struct drm_mode_create_dumb creq;
struct drm_mode_map_dumb mreq;
memset(&creq, 0, sizeof(creq));
creq.width = WIDTH;
creq.height = HEIGHT;
creq.bpp = 32; //ARGB
ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
__u32 buffer_id;
ret = drmModeAddFB(drm_fd, WIDTH, HEIGHT, creq.bpp, creq.bpp,
creq.pitch, creq.handle, &buffer_id);
memset(&mreq, 0, sizeof(mreq));
mreq.handle = creq.handle;
ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
__u32 *fb_base = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED,
drm_fd, mreq.offset);
__u32 color[1] = {0xFF0000};
for (int j = 0; j < (creq.size / 4); j++)
fb_base[j] = color[0];
drmModeModeInfo mode;
memcpy(&mode, &conn->modes[0], sizeof(mode));
__u32 *conn_id = &(res->connectors[0]);
ret = drmModeSetCrtc(drm_fd, g_crtc_id, buffer_id,
0, 0, conn_id, 1, &mode);
while(1);
return 0;
}
请注意一定要申请带有DY1212-4856 LVDS显示屏的板子,因为不同的显示屏幕分辨率不同,所对应的测试程序会有所不同。
在代码中,我们使用DRM应用层的API函数,绘制了一张格式为ARGB的全红图像,分辨率为1280x720。首先尝试使用gcc命令对其直接进行编译,命令为:
gcc drm_test.c -o drm_test
格式为:
gcc [file_name] -o [output_file_name]
file_name: 要编译的c文件名
output_file_name:要输出的可执行文件名
可以看到,在编译的过程中不能够找到drm.h文件。
寻找头文件的位置,可以使用find命令,常见的find命令格式为:
find [path] -name [file_name]
如在根文件目录( / )下寻找drm.h文件,可以通过如下命令:
find / -name drm.h
能够看到如下log:
里列出了i.MX系列的DRM相关头文件被放置的路径。因此考虑在gcc的命令中加入
-I 参数,加入include路径指定,如下所示:
gcc drm_test.c -I /usr/include/drm -o drm_test
随后看到如下log:
以上log表明gcc找到了DRM的头文件,但存在大量没有定义的函数,可以猜测对应函数名可以在头文件中找到,但是函数的具体实现,也就是所谓的库并没有被链接进来,gcc无法找到对应的库文件。
gcc使用-l(小写的l)参数为编译添加库文件,小写的l后面紧跟的就是库的名称,该名称不包含前缀 lib 和后缀.so/.a。比如我们希望添加名为libg2d.so的库,就会使用参数-lg2d。
gcc在链接时按照以下顺序查询库文件:
1. -l 参数指定的目录。
2. 环境变量 LIBRARY_PATH 指定的目录。
3. /usr/lib 和 /lib(在 Unix-like 系统中)。
4. 系统默认的库目录。
如果库文件不在上述目录中,你需要使用 -L 参数指定库文件的路径。
例如,假设你有一个名为 libmylib.so 的库文件位于 /opt/mylib/lib 目录下,链接时应该这样写:
gcc -o myprogram myprogram.c -L/opt/mylib/lib -lmylib
命令中 -lmylib 告诉 GCC 链接名为 mylib 的库,它会查找名为 libmylib.so 的文件。
这里进入/usr/lib路径,搜索drm相关的库
在默认的路径下有名为libdrm.so的库,因此可以直接输入命令
gcc drm_test.c -I /usr/include/drm -ldrm -o test
即可完成编译:
如果将drm相关的库换到另一个文件夹下,如
此时就需要加入-L参数,指定drm库所在的位置也同样可以完成编译任务。
gcc drm_test.c -I /usr/include/drm -L/home/root/display_test -ldrm -o test
2.2 DRM测试程序的运行
下一步直接运行该测试程序,首先关掉weston,使其释放显示的控制权限:
可以看到weston UI界面被关掉,屏幕只显示一个console界面,右上角有一道横线:
然后直接运行该测试程序,可以发现屏幕显示为纯色的图片: