I noticed that compiling a file with a single simple function on a type involving nested unions somehow causes nm to list memcpy
among the symbols as undefined ("U") despite memcpy
not being used.
Why? Is there some implicit use of it due to the way my types are designed?
MRE.h
:#ifndef MRE_H
# define MRE_H
# include <stddef.h>
# define HISTORY_SIZE 1024
typedef enum e_call_ent_type {
E_MALLOC_ENT = 0,
E_FREE_ENT = 1,
E_REALLOC_ENT = 2
} t_call_ent_type;
typedef struct s_call_ent {
t_call_ent_type type;
} t_call_ent;
typedef enum e_lock_ent_type {
E_LOCKBASE_ENT = 0,
E_UNLOCKBASE_ENT = 1
} t_lock_ent_type;
typedef struct s_lock_ent {
t_lock_ent_type type;
} t_lock_ent;
typedef enum e_ent_type {
E_VOID_ENT = 0,
E_CALL_ENT = 1,
E_LOCK_ENT = 2
} t_ent_type;
typedef struct s_ent
{
t_ent_type type;
union u_ent {
t_call_ent call_ent;
t_lock_ent lock_ent;
} ent;
int errno_val;
} t_ent;
typedef struct s_history {
t_ent entries[HISTORY_SIZE];
} t_history;
t_history init_history(void);
#endif
init_history.c
:#include "MRE.h"
t_history init_history(void) {
t_history res;
res.entries[0].type = E_VOID_ENT;
return (res);
}
Compiling and running nm:
> gcc -Wall -Werror -Wextra -c -o init_history.o init_history.c
> nm init_history.o
0000000000000000 T init_history
U memcpy
U __stack_chk_fail
objdump -d init_history.o
:
init_history.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <init_history>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
f: 48 83 0c 24 00 orq $0x0,(%rsp)
14: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
1b: 48 83 0c 24 00 orq $0x0,(%rsp)
20: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
27: 48 83 0c 24 00 orq $0x0,(%rsp)
2c: 48 83 ec 20 sub $0x20,%rsp
30: 48 89 bd e8 cf ff ff mov %rdi,-0x3018(%rbp)
37: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
3e: 00 00
40: 48 89 45 f8 mov %rax,-0x8(%rbp)
44: 31 c0 xor %eax,%eax
46: c7 85 f0 cf ff ff 00 movl $0x0,-0x3010(%rbp)
4d: 00 00 00
50: 48 8b 85 e8 cf ff ff mov -0x3018(%rbp),%rax
57: 48 89 c1 mov %rax,%rcx
5a: 48 8d 85 f0 cf ff ff lea -0x3010(%rbp),%rax
61: ba 00 30 00 00 mov $0x3000,%edx
66: 48 89 c6 mov %rax,%rsi
69: 48 89 cf mov %rcx,%rdi
6c: e8 00 00 00 00 call 71 <init_history+0x71>
71: 48 8b 45 f8 mov -0x8(%rbp),%rax
75: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
7c: 00 00
7e: 74 05 je 85 <init_history+0x85>
80: e8 00 00 00 00 call 85 <init_history+0x85>
85: 48 8b 85 e8 cf ff ff mov -0x3018(%rbp),%rax
8c: c9 leave
8d: c3 ret
If you look at the assembly instructions at offsets 6c and 80, you'll see a pair of CALL instructions with opcodes e8 00 00 00 00
. Note that the destination offset i.e. the last 4 bytes are all 0. These are external functions calls that will get resolved at link time.
If you link the object file into an executable and run objdump
on the executable, you'll probably see that these instructions now point to memcpy
.
Since you're returning a struct by value, this call to memcpy
is most likely doing this copy for the return value.