You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编写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'

错误原因

  1. 函数声明缺失:assemble调用的parseInstruction、getRegisterNumber等函数未提前声明,编译器处理assemble时无法识别这些函数,导致链接阶段找不到定义。
  2. 主函数逻辑缺失:main仅初始化数据段/文本段并写入文件,完全未调用assemble执行核心汇编逻辑。
  3. 代码语法/逻辑错误:
    • 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", &reg) != 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:32:32