uchan-nos/os-from-zero

[ 質問 ] make 時に duplicate symbol エラーが表示されてしまう。

Closed this issue · 5 comments

61xxx commented

書籍の内容からやや外れる内容となりますが、何卒ご了承いただけますと幸いです。

MikanOS への microps の移植および ifconfig の実装を mikanos-net を参考にさせていただきながら行い、現在 HTTPクライアントの実装に挑戦している者です。

apps/httpcl ディレクトリ内で make を実行したところ、以下の画像のようなエラーが出力されました。

SnapCrab_NoName_2022-11-25_13-56-22_No-00

この make 実行以降、問題なかった apps/ifconfig ディレクトリ内での make 実行時にも同様のエラーが出るようになりました。

apps/httpcl/Makefile の内容は以下の通りです。

TARGET = httpcl
OBJS = httpcl.o
include ../Makefile.elfapp

apps/Makefile.elfappの内容は以下の通りです。

CPPFLAGS += -I. -D__SCLE \
	    -I /home/user01/osbook/devenv/x86_64-elf/include \
            -I /home/user01/osbook/devenv/x86_64-elf/include/freetype2 \
            -I /home/user01/edk2/MdePkg/Include \
            -I /home/user01/edk2/MdePkg/Include/X64 \
            -I /home/user01/osbook/devenv/x86_64-elf/include/c++/v1 \
            -nostdlibinc -D__ELF__ -D_LDBL_EQ_DBL -D_GNU_SOURCE -D_POSIX_TIMERS -DEFIAPI='__attribute__((ms_abi))' 

CFLAGS   += -O2 -Wall -g --target=x86_64-elf -ffreestanding -mcmodel=large

CXXFLAGS += -O2 -Wall -g --target=x86_64-elf -mcmodel=large \
            -fno-exceptions -fno-rtti -std=c++17

LDFLAGS += --entry main -z norelro --image-base 0xffff800000000000 --static \
           -L /home/user01/osbook/devenv/x86_64-elf/lib

OBJS += ../syscall.o ../newlib_support.o ../socket.o

.PHONY: all
all: $(TARGET)

$(TARGET): $(OBJS) Makefile
	ld.lld $(LDFLAGS) -o $@ $(OBJS) -lc -lc++ -lc++abi -lm

%.o: %.c Makefile
	clang $(CPPFLAGS) $(CFLAGS) -c $< -o $@

%.o: %.cpp Makefile
	clang++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

%.o: %.asm Makefile
	nasm -f elf64 -o $@ $<

apps/newlib_support.cの内容は以下の通りです。

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>

#include "syscall.h"

extern int close(int fd) {
  errno = EBADF;
  return -1;
}

int fstat(int fd, struct stat* buf) {
  errno = EBADF;
  return -1;
}

pid_t getpid(void) {
  return 0;
}

int isatty(int fd) {
  errno = EBADF;
  return -1;
}

int kill(pid_t pid, int sig) {
  errno = EPERM;
  return -1;
}

off_t lseek(int fd, off_t offset, int whence) {
  errno = EBADF;
  return -1;
}

int open(const char* path, int flags) {
  struct SyscallResult res = SyscallOpenFile(path, flags);
  if (res.error == 0) {
    return res.value;
  }
  errno = res.error;
  return -1;
}

int posix_memalign(void** memptr, size_t alignment, size_t size) {
  void* p = malloc(size + alignment - 1);
  if (!p) {
    return ENOMEM;
  }
  uintptr_t addr = (uintptr_t)p;
  *memptr = (void*)((addr + alignment - 1) & ~(uintptr_t)(alignment - 1));
  return 0;
}

ssize_t read(int fd, void* buf, size_t count) {
  struct SyscallResult res = SyscallReadFile(fd, buf, count);
  if (res.error == 0) {
    return res.value;
  }
  errno = res.error;
  return -1;
}

caddr_t sbrk(int incr) {
  static uint64_t dpage_end = 0;
  static uint64_t program_break = 0;

  if (dpage_end == 0 || dpage_end < program_break + incr) {
    int num_pages = (incr + 4095) / 4096;
    struct SyscallResult res = SyscallDemandPages(num_pages, 0);
    if (res.error) {
      errno = ENOMEM;
      return (caddr_t)-1;
    }
    program_break = res.value;
    dpage_end = res.value + 4096 * num_pages;
  }

  const uint64_t prev_break = program_break;
  program_break += incr;
  return (caddr_t)prev_break;
}

ssize_t write(int fd, const void* buf, size_t count) {
  struct SyscallResult res = SyscallPutString(fd, buf, count);
  if (res.error == 0) {
    return res.value;
  }
  errno = res.error;
  return -1;
}

void _exit(int status) {
  SyscallExit(status);
}

apps/socket.cppの内容は以下の通りです。

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include "syscall.h"
#include "socket.hpp"

int
ip_addr_pton (const char *p, ip_addr_t *n) {
    char *sp, *ep;
    int idx;
    long ret;

    sp = (char *)p;
    for (idx = 0; idx < 4; idx++) {
        ret = strtol(sp, &ep, 10);
        if (ret < 0 || ret > 255) {
            return -1;
        }
        if (ep == sp) {
            return -1;
        }
        if ((idx == 3 && *ep != '\0') || (idx != 3 && *ep != '.')) {
            return -1;
        }
        ((uint8_t *)n)[idx] = ret;
        sp = ep + 1;
    }
    return 0;
}

char *
ip_addr_ntop (const ip_addr_t *n, char *p, size_t size) {
    uint8_t *ptr;

    ptr = (uint8_t *)n;
    snprintf(p, size, "%d.%d.%d.%d",
        ptr[0], ptr[1], ptr[2], ptr[3]);
    return p;
}

#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN 4321
#endif
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN 1234
#endif

static int endian;

static int
byteorder(void) {
    uint32_t x = 0x00000001;

    return *(uint8_t *)&x ? __LITTLE_ENDIAN : __BIG_ENDIAN;
}

static uint16_t
byteswap16(uint16_t v)
{
    return (v & 0x00ff) << 8 | (v & 0xff00 ) >> 8;
}

static uint32_t
byteswap32(uint32_t v)
{
    return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24;
}

uint16_t
hton16(uint16_t h)
{
    if (!endian) {
        endian = byteorder();
    }
    return endian == __LITTLE_ENDIAN ? byteswap16(h) : h;
}

uint16_t
ntoh16(uint16_t n)
{
    if (!endian) {
        endian = byteorder();
    }
    return endian == __LITTLE_ENDIAN ? byteswap16(n) : n;
}

uint32_t
hton32(uint32_t h)
{
    if (!endian) {
        endian = byteorder();
    }
    return endian == __LITTLE_ENDIAN ? byteswap32(h) : h;
}

uint32_t
ntoh32(uint32_t n)
{
    if (!endian) {
        endian = byteorder();
    }
    return endian == __LITTLE_ENDIAN ? byteswap32(n) : n;
}

int
ioctl(int fd, int req, void *arg)
{
    auto [ret, err] = SyscallSocketIOCTL(fd, req, arg);

    return ret;
}

int
socket(int domain, int type, int protocol)
{
    auto [ret, err] = SyscallSocketOpen(domain, type, protocol);

    return ret;
}

int
soclose(int soc)
{
    SyscallSocketClose(soc);
    return 0;
}

int
recvfrom(int soc, char *buf, size_t size, struct sockaddr *addr, int *salen)
{
    auto [ret, err] = SyscallSocketRecvFrom(soc, buf, size, addr, salen);

    return ret;
}

int
sendto(int soc, char *buf, size_t size, struct sockaddr *addr, int salen)
{
    auto [ret, err] = SyscallSocketSendTo(soc, buf, size, addr, salen);

    return ret;
}

int
bind(int soc, struct sockaddr *addr, int salen)
{
    auto [ret, err] = SyscallSocketBind(soc, addr, salen);

    return ret;
}

int
listen(int soc, int backlog)
{
    auto [ret, err] = SyscallSocketListen(soc, backlog);
    return ret;
}

int
accept(int soc, struct sockaddr *addr, int *salen)
{
    auto [ret, err] = SyscallSocketAccept(soc, addr, salen);

    return ret;
}

int
connect(int soc, struct sockaddr *addr, int salen)
{
    auto [ret, err] = SyscallSocketConnect(soc, addr, salen);

    return ret;
}

int
recv(int soc, char *buf, size_t size)
{
    auto [ret, err] = SyscallSocketRecv(soc, buf, size);

    return ret;
}

int
send(int soc, char *buf, size_t size)
{
    auto [ret, err] = SyscallSocketSend(soc, buf, size);

    return ret;
}

apps/httpcl/httpcl.cの内容は以下の通りです。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "../syscall.h"
#include "../socket.hpp"

/* --- */

#define true 1
#define false 0

#define EXIT_FAILURE 1

#define SOCK_STREAM 1  // for TCP
#define SOCK_DGRAM 2   // for UDP

#define MALLOC_MAX_SIZE 1000000

#define SIZE_REQUEST 1000
#define SIZE_RESPONSE 2000

typedef _Bool bool;

ssize_t write(int fd, const void* buf, size_t count);

size_t strlen(const char* s);
char* strcpy(char* dest, const char* src);
char* strcat(char* dest, const char* src);
int strcmp(const char* s1, const char* s2);
void* malloc(unsigned long n);
void* memset(void* s, int c, size_t n);
int malloc_size;
uint8_t malloc_array[MALLOC_MAX_SIZE];

uint32_t inet_addr(const char* cp);

void Print(const char* s);
void Println(const char* s);
uint16_t StrToNum16(const char* s, const char** next);

void NotImplemented(const char* s) {
  Print("Not Implemented: ");
  Println(s);
  exit(1);
}

size_t strlen(const char* s) {
  size_t len = 0;
  while (*s) {
    len++;
    s++;
  }
  return len;
}

char* strcpy(char* dest, const char* src) {
  char* start = dest;
  while (*src) {
    *dest = *src;
    dest++;
    src++;
  }
  return start;
}

char* strcat(char* dest, const char* src) {
  char* ptr = dest + strlen(dest);
  while (*src != '\0')
    *ptr++ = *src++;
  *ptr = '\0';
  return dest;
}

int strcmp(const char* s1, const char* s2) {
  while (*s1) {
    if (*s1 != *s2)
      break;
    s1++;
    s2++;
  }

  return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}

void* malloc(unsigned long n) {
  if (sizeof(malloc_array) < (malloc_size + n)) {
    write(1, "fail: malloc\n", 13);
    exit(1);
  }

  void* ptr = malloc_array + malloc_size;
  malloc_size = malloc_size + n;
  memset(ptr, 0, n);
  return ptr;
}
void* memset(void* s, int c, size_t n) {
  char* dest = (char*)s;
  while (n > 0) {
    *dest = (char)c;
    dest++;
    n--;
  }
  return s;
}

uint32_t inet_addr(const char* cp) {
  int dots = 0;
  uint32_t acc = 0;
  uint32_t addrs[4];
  uint32_t addr = 0;
  int index = 0;

  while (*cp) {
    switch (*cp) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        acc = acc * 10 + (*cp - '0');
        break;
      case '.':
        if (++dots > 3) {
          return 0;
        }
        addrs[index++] = acc;
        acc = 0;
        break;
      case '\0':
        if (acc > 255) {
          return 0;
        }
        addrs[index++] = acc;
        acc = 0;
        break;
      default:
        return 0;
    }
    cp++;
  }

  addrs[index++] = acc;
  acc = 0;

  switch (dots) {
    case 3:
      addr = addrs[3] << 24 | addrs[2] << 16 | addrs[1] << 8 | addrs[0];
      break;
    case 2:
      addr = addrs[2] << 24 | addrs[1] << 8 | addrs[0];
      break;
    case 1:
      addr = addrs[1] << 24 | addrs[0];
      break;
    default:
      addr = addrs[0] << 24;
  }

  return addr;
}

void Print(const char* s) {
  write(1, s, strlen(s));
}

void Println(const char* s) {
  write(1, s, strlen(s));
  write(1, "\n", 1);
}

uint16_t StrToNum16(const char* s, const char** next) {
  uint32_t v = 0;
  while ('0' <= *s && *s <= '9') {
    v = v * 10 + *s - '0';
    s++;
  }
  if (next) {
    *next = s;
  }
  return v;
}

/* --- */


char *host;
char *path;
char *ip;
uint16_t port;
bool tcp;

void RequestLine(char *request) {
  strcpy(request, "GET ");
  strcat(request, path);
  strcat(request, " HTTP/1.1\n");
}

void Headers(char *request) {
  strcat(request, "Host: ");
  strcat(request, host);
  strcat(request, "\n");
}

void Crlf(char *request) {
  strcat(request, "\n");
}

void Body(char *request) {}

void SendRequest(char *request) {
  int socket_fd = 0;
  struct sockaddr_in address;
  int addrlen = sizeof(address);
  char response[SIZE_RESPONSE];

  if (tcp) {
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  } else {
    socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  }
  if (socket_fd < 0) {
    Println("Error: Failed to create a socket");
    exit(1);
  }

  address.sin_family = AF_INET;
  address.sin_addr.s_addr = inet_addr(ip);
  address.sin_port = hton16(port);

  if (tcp) {
    if (connect(socket_fd, (struct sockaddr *) &address, sizeof(address)) < 0) {
      Println("Error: Fail to connect a socket");
      exit(EXIT_FAILURE);
    }
  }

  if (sendto(socket_fd, request, strlen(request),
             (struct sockaddr*)&address, addrlen) < 0) {
    Println("Error: Failed to send a request.");
    exit(EXIT_FAILURE);
  }
  Println("Request sent. Waiting for a response...");

  int len = sizeof(address);
  if (recvfrom(socket_fd, response, SIZE_RESPONSE,
               (struct sockaddr*)&address, &len) < 0) {
    Println("Error: Failed to receive a response.");
    exit(EXIT_FAILURE);
  }
  Println("----- response -----");
  Println(response);

  soclose(socket_fd);
}

bool ParseArgs(int argc, char **argv) {
  ip = "192.0.2.1";
  port = 80;
  host = "";
  path = "/index.html";
  tcp = true;

  while (argc > 0) {
    if (strcmp("--ip", argv[0]) == 0 || strcmp("-i", argv[0]) == 0) {
      ip = argv[1];
      argc -= 2;
      argv += 2;
      continue;
    }

    if (strcmp("--port", argv[0]) == 0 || strcmp("-p", argv[0]) == 0) {
      port = StrToNum16(argv[1], NULL);
      argc -= 2;
      argv += 2;
      continue;
    }

    if (strcmp("--host", argv[0]) == 0 || strcmp("-h", argv[0]) == 0) {
      host = argv[1];
      argc -= 2;
      argv += 2;
      continue;
    }

    if (strcmp("--path", argv[0]) == 0 || strcmp("-P", argv[0]) == 0) {
      path = argv[1];
      argc -= 2;
      argv += 2;
      continue;
    }

    if (strcmp("--tcp", argv[0]) == 0) {
      tcp = true;
      argc -= 1;
      argv += 1;
      continue;
    }

    return false;
  }
  return true;
}

int main(int argc, char **argv) {
  if (!ParseArgs(argc - 1, argv + 1)) {
    Println("Usage: httpcl [ OPTIONS ]");
    Println("       -i, --ip      IP address. Default: 127.0.0.1");
    Println("       -p, --port    Port number. Default: 8888");
    Println("       -h, --host    Host property of the URL. Default: Ø");
    Println("       -P, --path    Path property of the URL. Default: /");
    Println("           --tcp     Flag to use TCP. Use UDP when it doesn't exist.");
    exit(EXIT_FAILURE);
    return EXIT_FAILURE;
  }

  if (tcp)
    Println("Log: Using protocol: TCP");
  else
    Println("Log: Using protocol: UDP");

  char *request = (char *) malloc(SIZE_REQUEST);

  RequestLine(request);
  Headers(request);
  Crlf(request);
  Body(request);

  Println("----- request -----");
  Println(request);

  SendRequest(request);

  exit(0);
  return 0;
}

他、必要なことがありましたら、記載いたします。

apps/httpcl/httpcl.c は hikalium 様の liumOS ( https://github.com/hikalium/liumos ) を参考にさせていただきました。

質問者はこの書籍が C言語初経験ということもあり、大変拙いコードとなっておりますが、ご教授いただけますと幸いです。

よろしくお願いいたします。

うーん、謎のエラーですね。

エラーメッセージの意味としてはapps/newlib_support.cの10行目とapps/socket.cppの125行目にsocloseという同名の関数が定義されていて、それらが競合しているということです。
が、お示しのファイルを見てもnewlib_support.cの方にはその名前の関数は無いんですよね。

試しに apps/httpcl 内で次のコマンドを実行したらどうなるでしょうか?

$ objdump -d -M intel ../newlib_support.o | grep soclose
$ objdump -d -M intel ../socket.o | grep soclose

前者では何も表示されず、後者では何行か表示されることが期待されます。

(念のため、上記のコマンド実行前後で make を実行して、当該エラーが出ることを確認してくださいね。何かの拍子に状況が変わってしまうこともありますので。)

61xxx commented

ご返信ありがとうございます。

ご提案いただいたコマンドを実行した結果、以下のようになりました。

SnapCrab_NoName_2022-11-25_17-43-3_No-00

また、apps/socket.hpp を確認したところ、以下のような記載がありました。

SnapCrab_NoName_2022-11-25_17-44-4_No-00

これが競合の原因となっている可能性を考え、#define close soclose をコメントアウトしたのですが、エラー内容に変化なしでした。

以下、apps/socket.hpp の内容となります。

// #define INADDR_ANY ((ip_addr_t)0)
#define INADDR_ANY ((unsigned long int)0x00000000)

struct ip_addr_t {
    uint32_t s_addr;
};

struct socket {
    int type;
    int desc;
};

struct sockaddr {
    unsigned short sa_family;
    char sa_data[14];
};

struct sockaddr_in {
    unsigned short sin_family;
    uint16_t sin_port;
    struct ip_addr_t sin_addr;
};

#define IFNAMSIZ 16

struct ifreq {
    char ifr_name[IFNAMSIZ]; /* Interface name */
    union {
        struct sockaddr ifr_addr;
        struct sockaddr ifr_dstaddr;
        struct sockaddr ifr_broadaddr;
        struct sockaddr ifr_netmask;
        struct sockaddr ifr_hwaddr;
        short           ifr_flags;
        int             ifr_ifindex;
        int             ifr_metric;
        int             ifr_mtu;
//      struct ifmap    ifr_map;
        char            ifr_slave[IFNAMSIZ];
        char            ifr_newname[IFNAMSIZ];
        char           *ifr_data;
    };
};

extern int
ip_addr_pton (const char *p, struct ip_addr_t *n);
extern char *
ip_addr_ntop (const struct ip_addr_t *n, char *p, size_t size);
extern uint16_t
hton16(uint16_t h);
extern uint16_t
ntoh16(uint16_t n);
extern uint32_t
hton32(uint32_t h);
extern uint32_t
ntoh32(uint32_t n);
extern int
ioctl(int fd, int req, void *arg);

extern int
socket(int domain, int type, int protocol);
extern int
soclose(int soc);
// #define close soclose
extern int
recvfrom(int soc, char *buf, size_t size, struct sockaddr *addr, int *salen);
int
sendto(int soc, char *buf, size_t size, struct sockaddr *addr, int salen);
extern int
bind(int soc, struct sockaddr *addr, int salen);
extern int
listen(int soc, int backlog);
extern int
accept(int soc, struct sockaddr *addr, int *salen);
extern int
connect(int soc, struct sockaddr *addr, int salen);
extern int
recv(int soc, char *buf, size_t size);
extern int
send(int soc, char *buf, size_t size);

#ifdef __cplusplus
}
#endif

お忙しいところと存じますが、引き続きお考えをいただければ幸いです。

おそらくそれが原因だと思います。

エラー内容に変化なしでした。

きちんとビルドされていないのではないでしょうか?
ヘッダファイルのみの変更では make にファイルが変化したことが検知されないのかもしれません(Makefile の書き方に依存)

make の代わりに make -B を実行してみてください。ファイルの更新に関係なく全部ビルドされます。

61xxx commented

ご返信ありがとうございます。

ご指摘の通り、make -B で通りました。

ご丁寧にありがとうございした。大変勉強になりました。
引き続き、実装を頑張っていきたいと思います。