编写MIPS汇编器遇链接错误,求main及assemble函数修复建议
MIPS汇编器链接错误修复方案
问题现象
编译MIPS汇编器时出现链接错误:
/usr/bin/ld: /tmp/ccQ5mbJk.o: in function `assemble': main.c:(.text+0x2ed): undefined reference to `parseInstruction' /usr/bin/ld: main.c:(.text+0x316): undefined reference to `getRegisterNumber' /usr/bin/ld: main.c:(.text+0x338): undefined reference to `getRegisterNumber' /usr/bin/ld: main.c:(.text+0x364): undefined reference to `getRegisterNumber' /usr/bin/ld: main.c:(.text+0x463): undefined reference to `parseDirective'
错误原因
- 函数声明缺失:
assemble调用的parseInstruction、getRegisterNumber等函数未提前声明,编译器处理assemble时无法识别这些函数,导致链接阶段找不到定义。 - 主函数逻辑缺失:
main仅初始化数据段/文本段并写入文件,完全未调用assemble执行核心汇编逻辑。 - 代码语法/逻辑错误:
getDirective中strcmp判断条件缺失==0,导致匹配逻辑完全错误。assemble内存在无效语句data_segment;。main参数判断错误,程序需要输入、输出两个文件,argc<2应改为argc<3。- 字符串转义错误,
printf中的换行应使用\n而非直接换行。 - 机器码生成逻辑错误,
funct等字段的移位位置不符合MIPS指令格式。
修复方案
1. 添加函数原型与必要定义
在代码开头补充结构体、全局变量和函数原型,确保编译器能识别所有自定义函数:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #define MAX_SIZE 1024 // 定义指令结构体 typedef struct { char* name; int opcode; int funct; } Instruction; // 定义伪指令结构体 typedef struct { char* name; int n; int size; } Directive; // 示例指令集合(需根据需求补充完整) Instruction instructions[] = { {"add", 0, 0x20}, {"addi", 0x08, 0}, {"sub", 0, 0x22}, {NULL, 0, 0} }; // 示例伪指令集合(需根据需求补充完整) Directive directives[] = { {".dbyte", 2, 1}, {".integer", 3, 4}, {NULL, 0, 0} }; // 函数原型声明 void assemble(char* filename, int* text_segment, int* data_segment); Instruction* getInstruction(char* instruction); Directive* getDirective(char* directive); int getRegisterNumber(char* regis); int parseInstruction(char* instruction, int* opcode, int* funct); int parseDirective(char* directive, int* type, int* size);
2. 修正main函数逻辑
补充assemble调用,修正参数判断与文件操作逻辑:
int main(int argc, char** argv) { // 检查命令行参数:需要输入和输出两个文件 if (argc < 3) { printf("Usage: %s <test.asm> <test.out>\n", argv[0]); return -1; } // 初始化文本段和数据段 int text_segment[MAX_SIZE]; int data_segment[MAX_SIZE]; memset(text_segment, 0, sizeof(text_segment)); memset(data_segment, 0, sizeof(data_segment)); // 执行汇编逻辑 assemble(argv[1], text_segment, data_segment); // 写入输出文件 FILE* fp = fopen(argv[2], "w"); if (!fp) { perror("Failed to open output file"); return -1; } fwrite(text_segment, sizeof(int), MAX_SIZE, fp); fwrite(data_segment, sizeof(int), MAX_SIZE, fp); fclose(fp); return 0; }
3. 修复assemble及其他函数的错误
- 删除无效语句、修正
getDirective的匹配逻辑、调整机器码生成格式:
// 汇编输入文件核心逻辑 void assemble(char* filename, int* text_segment, int* data_segment) { int line_number = 0; int text_offset = 0; int data_offset = 0; char line[256]; FILE* fp = fopen(filename, "r"); if (!fp) { perror("Failed to open input file"); return; } while (fgets(line, 256, fp)) { line_number++; // 处理空行 if (strlen(line) <= 1) { continue; } // 解析行内容:标签、指令/伪指令、操作数 char* label = NULL; char* instr_or_dir = NULL; char* operands = NULL; // 分割标签(如果存在) char* colon_pos = strchr(line, ':'); if (colon_pos) { *colon_pos = '\0'; label = line; instr_or_dir = colon_pos + 1; // 跳过标签后的空白字符 while (isspace((unsigned char)*instr_or_dir)) instr_or_dir++; } else { instr_or_dir = line; } // 分割指令/伪指令与操作数 char* space_pos = strpbrk(instr_or_dir, " \t\n"); if (space_pos) { *space_pos = '\0'; operands = space_pos + 1; // 跳过操作数前的空白字符 while (isspace((unsigned char)*operands)) operands++; // 去除操作数末尾的换行 operands[strcspn(operands, "\n")] = '\0'; } // 区分指令和伪指令 if (instr_or_dir[0] == '.') { // 处理伪指令 int type = 0; int size = 0; if (parseDirective(instr_or_dir, &type, &size)) { char* token = strtok((char*)operands, ","); while (token) { // 清理操作数中的空白字符 char* start = token; while (isspace((unsigned char)*start)) start++; char* end = start + strlen(start) - 1; while (end > start && isspace((unsigned char)*end)) end--; *(end + 1) = '\0'; int value = 0; if (sscanf(start, "%d", &value) == 1 && data_offset < MAX_SIZE) { data_segment[data_offset] = value; data_offset++; } else { fprintf(stderr, "Line %d: Invalid operand '%s'\n", line_number, token); } token = strtok(NULL, ","); } } else { fprintf(stderr, "Line %d: Unknown directive '%s'\n", line_number, instr_or_dir); } } else { // 处理指令 int opcode = 0; int funct = 0; int rs = 0, rt = 0, rd = 0, imm = 0; if (parseInstruction(instr_or_dir, &opcode, &funct)) { char* op_token = strtok((char*)operands, ","); if (op_token) { rs = getRegisterNumber(op_token); op_token = strtok(NULL, ","); if (op_token) { rt = getRegisterNumber(op_token); if (opcode == 0) { // R型指令 op_token = strtok(NULL, ","); if (op_token) rd = getRegisterNumber(op_token); } else { // I型指令 op_token = strtok(NULL, ","); if (op_token) { imm = atoi(op_token); // 符号扩展立即数 if (imm < 0) imm |= 0xFFFF0000; } } } } // 生成符合MIPS格式的机器码 int machine_code = 0; machine_code |= (opcode << 26); machine_code |= (rs << 21); machine_code |= (rt << 16); if (opcode == 0) { machine_code |= (rd << 11); machine_code |= funct; // funct占低6位 } else { machine_code |= (imm & 0xFFFF); // 立即数占低16位 } // 写入文本段 if (text_offset < MAX_SIZE) { text_segment[text_offset] = machine_code; text_offset++; } } else { fprintf(stderr, "Line %d: Unknown instruction '%s'\n", line_number, instr_or_dir); } } } fclose(fp); } // 获取指令结构体 Instruction* getInstruction(char* instruction) { int i = 0; while (instructions[i].name) { if (strcmp(instructions[i].name, instruction) == 0) { return &instructions[i]; } i++; } return NULL; } // 获取伪指令结构体(修正strcmp判断条件) Directive* getDirective(char* directive) { int i = 0; while (directives[i].name) { if (strcmp(directives[i].name, directive) == 0) { return &directives[i]; } i++; } return NULL; } // 获取寄存器编号 int getRegisterNumber(char* regis) { int reg = 0; if (sscanf(regis, "$%d", ®) != 1) { fprintf(stderr, "Warning: Invalid register format '%s'\n", regis); reg = 0; } return reg; } // 解析指令 int parseInstruction(char* instruction, int* opcode, int* funct) { for (int i = 0; instructions[i].name; i++) { if (strcmp(instruction, instructions[i].name) == 0) { *opcode = instructions[i].opcode; *funct = instructions[i].funct; return 1; } } return 0; } // 解析伪指令 int parseDirective(char* directive, int* type, int* size) { Directive* dir = getDirective(directive); if (dir) { *type = dir->n; *size = dir->size; return 1; } else { return 0; } }
4. 编译命令
单文件编译时执行:
gcc -o mips_assembler main.c
内容的提问来源于stack exchange,提问作者Andrew Schelb
相关产品推荐
相关产品推荐

