-
Notifications
You must be signed in to change notification settings - Fork 81
/
main.c
1068 lines (924 loc) · 26.9 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "rvcc.h"
// 【注意】
// 如果是交叉编译,请把这个路径改为$RISCV对应的路径
// 注意 ~ 应替换为具体的 /home/用户名 的路径
static char *RVPath = "";
// 文件类型
typedef enum {
FILE_NONE, // 空类型
FILE_C, // C语言源代码类型
FILE_ASM, // 汇编代码类型
FILE_OBJ, // 可重定位文件类型
FILE_AR, // 静态库文件类型
FILE_DSO, // 动态库文件类型
} FileType;
// 引入路径区
StringArray IncludePaths;
// common块默认生成
bool OptFCommon = true;
// 位置无关代码的标记
bool OptFPIC;
// -x选项
static FileType OptX;
// -include所引入的文件
static StringArray OptInclude;
// -E选项
static bool OptE;
// -M选项
static bool OptM;
// -MD选项
static bool OptMD;
// -MMD选项
static bool OptMMD;
// -MP选项
static bool OptMP;
// -S选项
static bool OptS;
// -c选项
static bool OptC;
// cc1选项
static bool OptCC1;
// ###选项
static bool OptHashHashHash;
//-static选项
static bool OptStatic;
// -shared选项
static bool OptShared;
// -MF选项
static char *OptMF;
// -MT选项
static char *OptMT;
// 目标文件的路径
static char *OptO;
// 链接器额外参数
static StringArray LdExtraArgs;
// 标准库所引入的路径,用于-MMD选项
static StringArray StdIncludePaths;
// 输入文件名
char *BaseFile;
// 输出文件名
static char *OutputFile;
// 输入文件区
static StringArray InputPaths;
// 临时文件区
static StringArray TmpFiles;
// 输出程序的使用说明
static void usage(int Status) {
fprintf(stderr, "rvcc [ -o <path> ] <file>\n");
exit(Status);
}
// 判断需要一个参数的选项,是否具有一个参数
static bool takeArg(char *Arg) {
char *X[] = {"-o", "-I", "-idirafter", "-include",
"-x", "-MF", "-MT", "-Xlinker"};
for (int I = 0; I < sizeof(X) / sizeof(*X); I++)
if (!strcmp(Arg, X[I]))
return true;
return false;
}
// 增加默认引入路径
static void addDefaultIncludePaths(char *Argv0) {
// rvcc特定的引入文件被安装到了argv[0]的./include位置
strArrayPush(&IncludePaths, format("%s/include", dirname(strdup(Argv0))));
// 支持标准的引入路径
strArrayPush(&IncludePaths, "/usr/local/include");
strArrayPush(&IncludePaths, "/usr/include/riscv64-linux-gnu");
strArrayPush(&IncludePaths, "/usr/include");
// 为-MMD选项,复制一份标准库引入路径
for (int I = 0; I < IncludePaths.Len; I++)
strArrayPush(&StdIncludePaths, IncludePaths.Data[I]);
}
// 定义宏
static void define(char *Str) {
char *Eq = strchr(Str, '=');
if (Eq)
// 存在赋值,使用该值
defineMacro(strndup(Str, Eq - Str), Eq + 1);
else
// 不存在赋值,则设为1
defineMacro(Str, "1");
}
// 解析-x选项
static FileType parseOptX(char *S) {
// -xc,解析为C语言源代码
if (!strcmp(S, "c"))
return FILE_C;
// -xassembler,解析为汇编源代码
if (!strcmp(S, "assembler"))
return FILE_ASM;
// -xnone,解析为空类型
if (!strcmp(S, "none"))
return FILE_NONE;
error("<command line>: unknown argument for -x: %s", S);
}
// 对Make的目标中的特殊字符进行处理
static char *quoteMakefile(char *S) {
// 新字符串,确保即使S的全部字符都处理,加上'\0'也能够存储下
char *Buf = calloc(1, strlen(S) * 2 + 1);
// 遍历字符串,对特殊字符进行处理
for (int I = 0, J = 0; S[I]; I++) {
switch (S[I]) {
case '$':
Buf[J++] = '$';
Buf[J++] = '$';
break;
case '#':
Buf[J++] = '\\';
Buf[J++] = '#';
break;
case ' ':
case '\t':
// 反向遍历反斜杠字符
for (int K = I - 1; K >= 0 && S[K] == '\\'; K--)
Buf[J++] = '\\';
Buf[J++] = '\\';
Buf[J++] = S[I];
break;
default:
Buf[J++] = S[I];
break;
}
}
// 返回新字符串
return Buf;
}
// 解析传入程序的参数
static void parseArgs(int Argc, char **Argv) {
// 确保需要一个参数的选项,存在一个参数
for (int I = 1; I < Argc; I++)
// 如果需要一个参数
if (takeArg(Argv[I]))
// 如果不存在一个参数,则打印出使用说明
if (!Argv[++I])
usage(1);
// 存储-idirafter的路径参数
StringArray Idirafter = {};
// 遍历所有传入程序的参数
for (int I = 1; I < Argc; I++) {
// 解析-###
if (!strcmp(Argv[I], "-###")) {
OptHashHashHash = true;
continue;
}
// 解析-cc1
if (!strcmp(Argv[I], "-cc1")) {
OptCC1 = true;
continue;
}
// 如果存在help,则直接显示用法说明
if (!strcmp(Argv[I], "--help"))
usage(0);
// 解析-o XXX的参数
if (!strcmp(Argv[I], "-o")) {
// 目标文件的路径
OptO = Argv[++I];
continue;
}
// 解析-oXXX的参数
if (!strncmp(Argv[I], "-o", 2)) {
// 目标文件的路径
OptO = Argv[I] + 2;
continue;
}
// 解析-S
if (!strcmp(Argv[I], "-S")) {
OptS = true;
continue;
}
// // 解析-fcommon
if (!strcmp(Argv[I], "-fcommon")) {
OptFCommon = true;
continue;
}
// 解析-fno-common
if (!strcmp(Argv[I], "-fno-common")) {
OptFCommon = false;
continue;
}
// 解析-c
if (!strcmp(Argv[I], "-c")) {
OptC = true;
continue;
}
// 解析-E
if (!strcmp(Argv[I], "-E")) {
OptE = true;
continue;
}
// 解析-I
if (!strncmp(Argv[I], "-I", 2)) {
strArrayPush(&IncludePaths, Argv[I] + 2);
continue;
}
// 解析-D
if (!strcmp(Argv[I], "-D")) {
define(Argv[++I]);
continue;
}
// 解析-D
if (!strncmp(Argv[I], "-D", 2)) {
define(Argv[I] + 2);
continue;
}
// 解析-U
if (!strcmp(Argv[I], "-U")) {
undefMacro(Argv[++I]);
continue;
}
// 解析-U
if (!strncmp(Argv[I], "-U", 2)) {
undefMacro(Argv[I] + 2);
continue;
}
// 解析-include
if (!strcmp(Argv[I], "-include")) {
strArrayPush(&OptInclude, Argv[++I]);
continue;
}
// 解析-x
if (!strcmp(Argv[I], "-x")) {
OptX = parseOptX(Argv[++I]);
continue;
}
// 解析-x
if (!strncmp(Argv[I], "-x", 2)) {
OptX = parseOptX(Argv[I] + 2);
continue;
}
// 解析-l、-Wl,
if (!strncmp(Argv[I], "-l", 2) || !strncmp(Argv[I], "-Wl,", 4)) {
strArrayPush(&InputPaths, Argv[I]);
continue;
}
// 解析-Xlinker
if (!strcmp(Argv[I], "-Xlinker")) {
strArrayPush(&LdExtraArgs, Argv[++I]);
continue;
}
// 解析-s
if (!strcmp(Argv[I], "-s")) {
strArrayPush(&LdExtraArgs, "-s");
continue;
}
// 解析-M
if (!strcmp(Argv[I], "-M")) {
OptM = true;
continue;
}
// 解析-MF
if (!strcmp(Argv[I], "-MF")) {
OptMF = Argv[++I];
continue;
}
// 解析-MP
if (!strcmp(Argv[I], "-MP")) {
OptMP = true;
continue;
}
// 解析-MT
// `-MT File`,指定File为依赖规则中的目标
if (!strcmp(Argv[I], "-MT")) {
if (OptMT == NULL)
// 无依赖规则中的目标
OptMT = Argv[++I];
else
// 合并依赖规则中的目标
OptMT = format("%s %s", OptMT, Argv[++I]);
continue;
}
// 解析-MD
if (!strcmp(Argv[I], "-MD")) {
OptMD = true;
continue;
}
// 解析-MQ
if (!strcmp(Argv[I], "-MQ")) {
if (OptMT == NULL)
// 无依赖规则中的目标
OptMT = quoteMakefile(Argv[++I]);
else
// 合并依赖规则中的目标
OptMT = format("%s %s", OptMT, quoteMakefile(Argv[++I]));
continue;
}
// 解析-MMD
if (!strcmp(Argv[I], "-MMD")) {
// 同时启用-MD选项
OptMD = OptMMD = true;
continue;
}
// 解析-fpic或-fPIC
if (!strcmp(Argv[I], "-fpic") || !strcmp(Argv[I], "-fPIC")) {
OptFPIC = true;
continue;
}
// 解析-cc1-input
if (!strcmp(Argv[I], "-cc1-input")) {
BaseFile = Argv[++I];
continue;
}
// 解析-cc1-output
if (!strcmp(Argv[I], "-cc1-output")) {
OutputFile = Argv[++I];
continue;
}
// 解析-idirafter
// 将参数存入Idirafter
if (!strcmp(Argv[I], "-idirafter")) {
strArrayPush(&Idirafter, Argv[I++]);
continue;
}
// 解析-static
if (!strcmp(Argv[I], "-static")) {
OptStatic = true;
strArrayPush(&LdExtraArgs, "-static");
continue;
}
// 解析-shared
if (!strcmp(Argv[I], "-shared")) {
OptShared = true;
strArrayPush(&LdExtraArgs, "-shared");
continue;
}
// 解析-L
if (!strcmp(Argv[I], "-L")) {
strArrayPush(&LdExtraArgs, "-L");
strArrayPush(&LdExtraArgs, Argv[++I]);
continue;
}
// 解析-L
if (!strncmp(Argv[I], "-L", 2)) {
strArrayPush(&LdExtraArgs, "-L");
strArrayPush(&LdExtraArgs, Argv[I] + 2);
continue;
}
// 哈希表测试
if (!strcmp(Argv[I], "-hashmap-test")) {
hashmapTest();
exit(0);
}
// 忽略多个选项
if (!strncmp(Argv[I], "-O", 2) || !strncmp(Argv[I], "-W", 2) ||
!strncmp(Argv[I], "-g", 2) || !strncmp(Argv[I], "-std=", 5) ||
!strcmp(Argv[I], "-ffreestanding") ||
!strcmp(Argv[I], "-fno-builtin") ||
!strcmp(Argv[I], "-fno-omit-frame-pointer") ||
!strcmp(Argv[I], "-fno-stack-protector") ||
!strcmp(Argv[I], "-fno-strict-aliasing") || !strcmp(Argv[I], "-m64") ||
!strcmp(Argv[I], "-mno-red-zone") || !strcmp(Argv[I], "-w") ||
!strcmp(Argv[I], "-march=native"))
continue;
// 解析为-的参数
if (Argv[I][0] == '-' && Argv[I][1] != '\0')
error("unknown argument: %s", Argv[I]);
// 其他情况则匹配为输入文件
strArrayPush(&InputPaths, Argv[I]);
}
// 将所用Idirafter内的路径,都存入引用路径区中
for (int I = 0; I < Idirafter.Len; I++)
strArrayPush(&IncludePaths, Idirafter.Data[I]);
// 不存在输入文件时报错
if (InputPaths.Len == 0)
error("no input files");
// -E隐式包含输入是C语言的宏
if (OptE)
OptX = FILE_C;
}
// 打开需要写入的文件
static FILE *openFile(char *Path) {
if (!Path || strcmp(Path, "-") == 0)
return stdout;
// 以写入模式打开文件
FILE *Out = fopen(Path, "w");
if (!Out)
error("cannot open output file: %s: %s", Path, strerror(errno));
return Out;
}
// 判断字符串P是否以字符串Q结尾
static bool endsWith(char *P, char *Q) {
int len1 = strlen(P);
int len2 = strlen(Q);
// P比Q长且P最后Len2长度的字符和Q相等,则为真
return (len1 >= len2) && !strcmp(P + len1 - len2, Q);
}
// 替换文件的后缀名
static char *replaceExtn(char *Tmpl, char *Extn) {
// 去除路径,返回基础文件名
char *Filename = basename(strdup(Tmpl));
// 最后一次字符出现的位置
char *Dot = strrchr(Filename, '.');
// 如果存在'.',清除此后的内容
if (Dot)
*Dot = '\0';
// 将新后缀写入文件名中
return format("%s%s", Filename, Extn);
}
// 清理临时文件区
static void cleanup(void) {
// 遍历删除临时文件
for (int I = 0; I < TmpFiles.Len; I++)
unlink(TmpFiles.Data[I]);
}
// 创建临时文件
static char *createTmpFile(void) {
// 临时文件的路径格式
char *Path = strdup("/tmp/rvcc-XXXXXX");
// 创建临时文件
int FD = mkstemp(Path);
// 临时文件创建失败
if (FD == -1)
error("mkstemp failed: %s", strerror(errno));
// 关闭文件
close(FD);
// 将文件路径存入临时文件区中
strArrayPush(&TmpFiles, Path);
return Path;
}
// 开辟子进程
static void runSubprocess(char **Argv) {
// 打印出子进程所有的命令行参数
if (OptHashHashHash) {
// 程序名
fprintf(stderr, "%s", Argv[0]);
// 程序参数
for (int I = 1; Argv[I]; I++)
fprintf(stderr, " %s", Argv[I]);
// 换行
fprintf(stderr, "\n");
}
// Fork–exec模型
// 创建当前进程的副本,这里开辟了一个子进程
// 返回-1表示错位,为0表示成功
if (fork() == 0) {
// 执行文件rvcc,没有斜杠时搜索环境变量,此时会替换子进程
execvp(Argv[0], Argv);
// 如果exec函数返回,表明没有正常执行命令
fprintf(stderr, "exec failed: %s: %s\n", Argv[0], strerror(errno));
_exit(1);
}
// 父进程, 等待子进程结束
int Status;
while (wait(&Status) > 0)
;
// 处理子进程返回值
if (Status != 0)
exit(1);
}
// 执行调用cc1程序
// 因为rvcc自身就是cc1程序
// 所以调用自身,并传入-cc1参数作为子进程
static void runCC1(int Argc, char **Argv, char *Input, char *Output) {
// 多开辟10个字符串的位置,用于传递需要新传入的参数
char **Args = calloc(Argc + 10, sizeof(char *));
// 将传入程序的参数全部写入Args
memcpy(Args, Argv, Argc * sizeof(char *));
// 在选项最后新加入"-cc1"选项
Args[Argc++] = "-cc1";
// 存入输入文件的参数
if (Input) {
Args[Argc++] = "-cc1-input";
Args[Argc++] = Input;
}
// 存入输出文件的参数
if (Output) {
Args[Argc++] = "-cc1-output";
Args[Argc++] = Output;
}
// 运行自身作为子进程,同时传入选项
runSubprocess(Args);
}
// 当指定-E选项时,打印出所有终结符
static void printTokens(Token *Tok) {
// 输出文件,默认为stdout
FILE *Out = openFile(OptO ? OptO : "-");
// 记录行数
int Line = 1;
// 遍历读取终结符
for (; Tok->Kind != TK_EOF; Tok = Tok->Next) {
// 位于行首打印出换行符
if (Line > 1 && Tok->AtBOL)
fprintf(Out, "\n");
// 打印出需要空格的位置
if (Tok->HasSpace && !Tok->AtBOL)
fprintf(Out, " ");
// 打印出终结符
fprintf(Out, "%.*s", Tok->Len, Tok->Loc);
Line++;
}
// 文件以换行符结尾
fprintf(Out, "\n");
}
// 判断是否为标准库路径
static bool inStdIncludePath(char *Path) {
for (int I = 0; I < StdIncludePaths.Len; I++) {
char *Dir = StdIncludePaths.Data[I];
int Len = strlen(Dir);
// 与库路径相同,且以斜杠结尾
if (strncmp(Dir, Path, Len) == 0 && Path[Len] == '/')
return true;
}
return false;
}
// 输出可用于Make的规则,自动化文件依赖管理
static void printDependencies(void) {
char *Path;
if (OptMF)
// 将Make的规则写入`-MF File`的File中
Path = OptMF;
else if (OptMD)
// 相当于`-M -MF File.d`,将Make的规则写入.d文件
Path = replaceExtn(OptO ? OptO : BaseFile, ".d");
else if (OptO)
Path = OptO;
else
Path = "-";
// 输出文件
FILE *Out = openFile(Path);
// 如果未指定-MT,默认需要目标名中的特殊字符处理
if (OptMT)
fprintf(Out, "%s:", OptMT);
else
// -MF指定依赖规则中的目标,否则替换后缀为.o
fprintf(Out, "%s:", quoteMakefile(replaceExtn(BaseFile, ".o")));
// 获取输入文件
File **Files = getInputFiles();
// 遍历输入文件,并将格式化的结果写入输出文件
for (int I = 0; Files[I]; I++) {
// 不输出标准库内的文件
if (OptMMD && inStdIncludePath(Files[I]->Name))
continue;
fprintf(Out, " \\\n %s", Files[I]->Name);
}
fprintf(Out, "\n\n");
// 如果指定了-MP,则为头文件生成伪目标
if (OptMP) {
for (int I = 1; Files[I]; I++) {
// 不输出标准库内的文件
if (OptMMD && inStdIncludePath(Files[I]->Name))
continue;
// 处理头文件中的特殊字符
fprintf(Out, "%s:\n\n", quoteMakefile(Files[I]->Name));
}
}
}
// 解析文件,生成终结符流
static Token *mustTokenizeFile(char *Path) {
Token *Tok = tokenizeFile(Path);
// 终结符流生成失败,对应文件报错
if (!Tok)
error("%s: %s", Path, strerror(errno));
return Tok;
}
// 拼接终结符链表
static Token *appendTokens(Token *Tok1, Token *Tok2) {
// Tok1为空时直接返回Tok2
if (!Tok1 || Tok1->Kind == TK_EOF)
return Tok2;
// 链表指针T
Token *T = Tok1;
// T指向遍历到Tok1链表中最后一个
while (T->Next->Kind != TK_EOF)
T = T->Next;
// T->Next指向Tok2
T->Next = Tok2;
// 返回拼接好的Tok1
return Tok1;
}
// 编译C文件到汇编文件
static void cc1(void) {
Token *Tok = NULL;
// 处理-include选项
for (int I = 0; I < OptInclude.Len; I++) {
// 需要引入的文件
char *Incl = OptInclude.Data[I];
char *Path;
if (fileExists(Incl)) {
// 如果文件存在,则直接使用路径
Path = Incl;
} else {
// 否则搜索引入路径区
Path = searchIncludePaths(Incl);
if (!Path)
error("-include: %s: %s", Incl, strerror(errno));
}
// 解析文件,生成终结符流
Token *Tok2 = mustTokenizeFile(Path);
Tok = appendTokens(Tok, Tok2);
}
// 解析文件,生成终结符流
Token *Tok2 = mustTokenizeFile(BaseFile);
Tok = appendTokens(Tok, Tok2);
// 预处理
Tok = preprocess(Tok);
// 如果指定了-M,打印出文件的依赖关系
if (OptM || OptMD) {
printDependencies();
if (OptM)
return;
}
// 如果指定了-E那么打印出预处理过的C代码
if (OptE) {
printTokens(Tok);
return;
}
// 解析终结符流
Obj *Prog = parse(Tok);
// 生成代码
// 防止编译器在编译途中退出,而只生成了部分的文件
// 开启临时输出缓冲区
char *Buf;
size_t BufLen;
FILE *OutputBuf = open_memstream(&Buf, &BufLen);
// 输出汇编到缓冲区中
codegen(Prog, OutputBuf);
fclose(OutputBuf);
// 从缓冲区中写入到文件中
FILE *Out = openFile(OutputFile);
fwrite(Buf, BufLen, 1, Out);
fclose(Out);
}
// 调用汇编器
static void assemble(char *Input, char *Output) {
// 选择对应环境内的汇编器
char *As = strlen(RVPath)
? format("%s/bin/riscv64-unknown-linux-gnu-as", RVPath)
: "as";
char *Cmd[] = {As, "-c", Input, "-o", Output, NULL};
runSubprocess(Cmd);
}
// 查找文件
static char *findFile(char *Pattern) {
char *Path = NULL;
// Linux文件系统中路径名称的模式匹配
glob_t Buf = {};
// 参数:用来模式匹配的路径,标记(例如是否排序结果),错误处理函数,结果存放缓冲区
glob(Pattern, 0, NULL, &Buf);
// gl_pathc匹配到的路径计数
// 复制最后的一条匹配结果到Path中
if (Buf.gl_pathc > 0)
Path = strdup(Buf.gl_pathv[Buf.gl_pathc - 1]);
// 释放内存
globfree(&Buf);
return Path;
}
// 文件存在时,为真
bool fileExists(char *Path) {
struct stat St;
return !stat(Path, &St);
}
// 查找库路径
static char *findLibPath(void) {
if (fileExists("/usr/lib/riscv64-linux-gnu/crti.o"))
return "/usr/lib/riscv64-linux-gnu";
if (fileExists("/usr/lib64/crti.o"))
return "/usr/lib64";
if (fileExists(format("%s/sysroot/usr/lib/crti.o", RVPath)))
return format("%s/sysroot/usr/lib/", RVPath);
error("library path is not found");
return NULL;
}
// 查找gcc库路径
static char *findGCCLibPath(void) {
char *paths[] = {
"/usr/lib/gcc/riscv64-linux-gnu/*/crtbegin.o",
// Gentoo
"/usr/lib/gcc/riscv64-pc-linux-gnu/*/crtbegin.o",
// Fedora
"/usr/lib/gcc/riscv64-redhat-linux/*/crtbegin.o",
// 交叉编译
format("%s/lib/gcc/riscv64-unknown-linux-gnu/*/crtbegin.o", RVPath),
};
// 遍历以查找gcc库的路径
for (int I = 0; I < sizeof(paths) / sizeof(*paths); I++) {
char *path = findFile(paths[I]);
if (path)
return dirname(path);
}
error("gcc library path is not found");
return NULL;
}
// 运行链接器ld
static void runLinker(StringArray *Inputs, char *Output) {
// 需要传递ld子进程的参数
StringArray Arr = {};
// 链接器
char *Ld = strlen(RVPath)
? format("%s/bin/riscv64-unknown-linux-gnu-ld", RVPath)
: "ld";
strArrayPush(&Arr, Ld);
// 输出文件
strArrayPush(&Arr, "-o");
strArrayPush(&Arr, Output);
strArrayPush(&Arr, "-m");
strArrayPush(&Arr, "elf64lriscv");
if (!OptStatic) {
strArrayPush(&Arr, "-dynamic-linker");
char *LP64D =
strlen(RVPath)
? format("%s/sysroot/lib/ld-linux-riscv64-lp64d.so.1", RVPath)
: "/lib/ld-linux-riscv64-lp64d.so.1";
strArrayPush(&Arr, LP64D);
}
char *LibPath = findLibPath();
char *GccLibPath = findGCCLibPath();
if (OptShared) {
strArrayPush(&Arr, format("%s/crti.o", LibPath));
strArrayPush(&Arr, format("%s/crtbeginS.o", GccLibPath));
} else {
strArrayPush(&Arr, format("%s/crt1.o", LibPath));
strArrayPush(&Arr, format("%s/crti.o", LibPath));
strArrayPush(&Arr, format("%s/crtbegin.o", GccLibPath));
}
strArrayPush(&Arr, format("-L%s", GccLibPath));
if (strlen(RVPath)) {
strArrayPush(&Arr, format("-L%s/sysroot/usr/lib64", RVPath));
strArrayPush(&Arr, format("-L%s/sysroot/lib64", RVPath));
strArrayPush(&Arr,
format("-L%s/sysroot/usr/lib/riscv64-linux-gnu", RVPath));
strArrayPush(&Arr,
format("-L%s/sysroot/usr/lib/riscv64-pc-linux-gnu", RVPath));
strArrayPush(&Arr,
format("-L%s/sysroot/usr/lib/riscv64-redhat-linux", RVPath));
strArrayPush(&Arr, format("-L%s/sysroot/usr/lib", RVPath));
strArrayPush(&Arr, format("-L%s/sysroot/lib", RVPath));
} else {
strArrayPush(&Arr, "-L/usr/lib64");
strArrayPush(&Arr, "-L/lib64");
strArrayPush(&Arr, "-L/usr/lib/riscv64-linux-gnu");
strArrayPush(&Arr, "-L/usr/lib/riscv64-pc-linux-gnu");
strArrayPush(&Arr, "-L/usr/lib/riscv64-redhat-linux");
strArrayPush(&Arr, "-L/usr/lib");
strArrayPush(&Arr, "-L/lib");
}
// 链接器额外参数存入到链接器参数中
for (int I = 0; I < LdExtraArgs.Len; I++)
strArrayPush(&Arr, LdExtraArgs.Data[I]);
// 输入文件,存入到链接器参数中
for (int I = 0; I < Inputs->Len; I++)
strArrayPush(&Arr, Inputs->Data[I]);
if (OptStatic) {
strArrayPush(&Arr, "--start-group");
strArrayPush(&Arr, "-lgcc");
strArrayPush(&Arr, "-lgcc_eh");
strArrayPush(&Arr, "-lc");
strArrayPush(&Arr, "--end-group");
} else {
strArrayPush(&Arr, "-lc");
strArrayPush(&Arr, "-lgcc");
strArrayPush(&Arr, "--as-needed");
strArrayPush(&Arr, "-lgcc_s");
strArrayPush(&Arr, "--no-as-needed");
}
if (OptShared)
strArrayPush(&Arr, format("%s/crtendS.o", GccLibPath));
else
strArrayPush(&Arr, format("%s/crtend.o", GccLibPath));
strArrayPush(&Arr, format("%s/crtn.o", LibPath));
strArrayPush(&Arr, NULL);
// 开辟的链接器子进程
runSubprocess(Arr.Data);
}
// 获取文件的类型
static FileType getFileType(char *Filename) {
// 若-x指定了不为空的类型,使用该类型
if (OptX != FILE_NONE)
return OptX;
// 以.a结尾的文件,解析为静态库文件类型
if (endsWith(Filename, ".a"))
return FILE_AR;
// 以.so结尾的文件,解析为动态库文件类型
if (endsWith(Filename, ".so"))
return FILE_DSO;
// 以.o结尾的文件,解析为空重定位文件类型
if (endsWith(Filename, ".o"))
return FILE_OBJ;
// 以.c结尾的文件,解析为C语言源代码类型
if (endsWith(Filename, ".c"))
return FILE_C;
// 以.s结尾的文件,解析为汇编类型
if (endsWith(Filename, ".s"))
return FILE_ASM;
error("<command line>: unknown file extension: %s", Filename);
}
// 编译器驱动流程
//
// 源文件
// ↓
// 预处理器预处理后的文件
// ↓
// cc1编译为汇编文件
// ↓
// as编译为可重定位文件
// ↓
// ld链接为可执行文件
// rvcc的程序入口函数
int main(int Argc, char **Argv) {
// 在程序退出时,执行cleanup函数
atexit(cleanup);
// 初始化预定义的宏
initMacros();
// 解析传入程序的参数
parseArgs(Argc, Argv);
// 如果指定了-cc1选项
// 直接编译C文件到汇编文件
if (OptCC1) {
// 增加默认引入路径
addDefaultIncludePaths(Argv[0]);
cc1();
return 0;
}
// 当前不能指定-c、-S、-E后,将多个输入文件,输出到一个文件中
if (InputPaths.Len > 1 && OptO && (OptC || OptS || OptE))
error("cannot specify '-o' with '-c', '-S' or '-E' with multiple files");
// 链接器参数
StringArray LdArgs = {};
// 遍历每个输入文件
for (int I = 0; I < InputPaths.Len; I++) {
// 读取输入文件
char *Input = InputPaths.Data[I];
// 链接时搜索指定的库文件
if (!strncmp(Input, "-l", 2)) {
strArrayPush(&LdArgs, Input);
continue;
}
// 匹配到 -Wl, 将后续参数存入链接器选项
if (!strncmp(Input, "-Wl,", 4)) {
// 以 , 分割开的参数字符串
char *S = strdup(Input + 4);
// 以 , 分割字符串,并返回一个参数
char *Arg = strtok(S, ",");
// 循环遍历剩余的参数
while (Arg) {
// 加入到链接器参数
strArrayPush(&LdArgs, Arg);
Arg = strtok(NULL, ",");
}
continue;
}
// 输出文件
char *Output;
// 如果指定了输出文件,则直接使用
if (OptO)
Output = OptO;
// 若未指定输出的汇编文件名,则输出到后缀为.s的同名文件中
else if (OptS)
Output = replaceExtn(Input, ".s");
// 若未指定输出的可重定位文件名,则输出到后缀为.o的同名文件中