[ 質問 ] make 時に duplicate symbol エラーが表示されてしまう。
61xxx opened this issue · comments
書籍の内容からやや外れる内容となりますが、何卒ご了承いただけますと幸いです。
MikanOS への microps の移植および ifconfig の実装を mikanos-net を参考にさせていただきながら行い、現在 HTTPクライアントの実装に挑戦している者です。
apps/httpcl ディレクトリ内で make を実行したところ、以下の画像のようなエラーが出力されました。
この 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 を実行して、当該エラーが出ることを確認してくださいね。何かの拍子に状況が変わってしまうこともありますので。)
ご返信ありがとうございます。
ご提案いただいたコマンドを実行した結果、以下のようになりました。
また、apps/socket.hpp を確認したところ、以下のような記載がありました。
これが競合の原因となっている可能性を考え、#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 を実行してみてください。ファイルの更新に関係なく全部ビルドされます。
ご返信ありがとうございます。
ご指摘の通り、make -B で通りました。
ご丁寧にありがとうございした。大変勉強になりました。
引き続き、実装を頑張っていきたいと思います。